From 60aa988f82b3dd66d7622a6559ce3749e88e789d Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sat, 9 Mar 2013 12:01:19 +0100 Subject: [PATCH 001/623] Created documentation subsection --- Makefile.am | 4 +- configure.ac | 1 + doc/Makefile.am | 13 + doc/fdl-1.3.texi | 506 +++ doc/lgpl.texi | 561 +++ doc/libhttpserver.3 | 35 + doc/texinfo.tex | 9977 +++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 11095 insertions(+), 2 deletions(-) create mode 100644 doc/Makefile.am create mode 100644 doc/fdl-1.3.texi create mode 100644 doc/lgpl.texi create mode 100644 doc/libhttpserver.3 create mode 100644 doc/texinfo.tex diff --git a/Makefile.am b/Makefile.am index 05215d4c..98e12a66 100644 --- a/Makefile.am +++ b/Makefile.am @@ -24,8 +24,8 @@ LIBTOOL_DEPS = @LIBTOOL_DEPS@ AUTOMAKE_OPTIONS = foreign 1.4 ACLOCAL_AMFLAGS = -I m4 -SUBDIRS = src test -DIST_SUBDIRS = src test +SUBDIRS = src test doc +DIST_SUBDIRS = src test doc EXTRA_DIST = libhttpserver.pc.in debian/changelog.in debian/control.in debian/copyright.in debian/rules.in debian/libhttpserver-dev.install.in debian/libhttpserver.install.in redhat/libhttpserver.SPEC.in $(DX_CONFIG) MOSTLYCLEANFILES = $(DX_CLEANFILES) redhat/SOURCES/* diff --git a/configure.ac b/configure.ac index fbf8eec2..e9508870 100644 --- a/configure.ac +++ b/configure.ac @@ -147,6 +147,7 @@ AC_SUBST(EXT_LIBS) AC_OUTPUT( libhttpserver.pc Makefile + doc/Makefile src/Makefile test/Makefile debian/changelog diff --git a/doc/Makefile.am b/doc/Makefile.am new file mode 100644 index 00000000..69a8b038 --- /dev/null +++ b/doc/Makefile.am @@ -0,0 +1,13 @@ +man_MANS = libhttpserver.3 +EXTRA_DIST = $(man_MANS) + +DISTCLEANFILES = \ + libhttpserver.cps \ + libhttpserver.dvi \ + libhttpserver-tutorial.cps \ + libhttpserver-tutorial.dvi +info_TEXINFOS = \ + libhttpserver.texi +httpserver_TEXINFOS = \ + fdl-1.3.texi \ + lgpl.texi diff --git a/doc/fdl-1.3.texi b/doc/fdl-1.3.texi new file mode 100644 index 00000000..8805f1a4 --- /dev/null +++ b/doc/fdl-1.3.texi @@ -0,0 +1,506 @@ +@c The GNU Free Documentation License. +@center Version 1.3, 3 November 2008 + +@c This file is intended to be included within another document, +@c hence no sectioning command or @node. + +@display +Copyright @copyright{} 2000, 2001, 2002, 2007, 2008 Free Software Foundation, Inc. +@uref{http://fsf.org/} + +Everyone is permitted to copy and distribute verbatim copies +of this license document, but changing it is not allowed. +@end display + +@enumerate 0 +@item +PREAMBLE + +The purpose of this License is to make a manual, textbook, or other +functional and useful document @dfn{free} in the sense of freedom: to +assure everyone the effective freedom to copy and redistribute it, +with or without modifying it, either commercially or noncommercially. +Secondarily, this License preserves for the author and publisher a way +to get credit for their work, while not being considered responsible +for modifications made by others. + +This License is a kind of ``copyleft'', which means that derivative +works of the document must themselves be free in the same sense. It +complements the GNU General Public License, which is a copyleft +license designed for free software. + +We have designed this License in order to use it for manuals for free +software, because free software needs free documentation: a free +program should come with manuals providing the same freedoms that the +software does. But this License is not limited to software manuals; +it can be used for any textual work, regardless of subject matter or +whether it is published as a printed book. We recommend this License +principally for works whose purpose is instruction or reference. + +@item +APPLICABILITY AND DEFINITIONS + +This License applies to any manual or other work, in any medium, that +contains a notice placed by the copyright holder saying it can be +distributed under the terms of this License. Such a notice grants a +world-wide, royalty-free license, unlimited in duration, to use that +work under the conditions stated herein. The ``Document'', below, +refers to any such manual or work. Any member of the public is a +licensee, and is addressed as ``you''. You accept the license if you +copy, modify or distribute the work in a way requiring permission +under copyright law. + +A ``Modified Version'' of the Document means any work containing the +Document or a portion of it, either copied verbatim, or with +modifications and/or translated into another language. + +A ``Secondary Section'' is a named appendix or a front-matter section +of the Document that deals exclusively with the relationship of the +publishers or authors of the Document to the Document's overall +subject (or to related matters) and contains nothing that could fall +directly within that overall subject. (Thus, if the Document is in +part a textbook of mathematics, a Secondary Section may not explain +any mathematics.) The relationship could be a matter of historical +connection with the subject or with related matters, or of legal, +commercial, philosophical, ethical or political position regarding +them. + +The ``Invariant Sections'' are certain Secondary Sections whose titles +are designated, as being those of Invariant Sections, in the notice +that says that the Document is released under this License. If a +section does not fit the above definition of Secondary then it is not +allowed to be designated as Invariant. The Document may contain zero +Invariant Sections. If the Document does not identify any Invariant +Sections then there are none. + +The ``Cover Texts'' are certain short passages of text that are listed, +as Front-Cover Texts or Back-Cover Texts, in the notice that says that +the Document is released under this License. A Front-Cover Text may +be at most 5 words, and a Back-Cover Text may be at most 25 words. + +A ``Transparent'' copy of the Document means a machine-readable copy, +represented in a format whose specification is available to the +general public, that is suitable for revising the document +straightforwardly with generic text editors or (for images composed of +pixels) generic paint programs or (for drawings) some widely available +drawing editor, and that is suitable for input to text formatters or +for automatic translation to a variety of formats suitable for input +to text formatters. A copy made in an otherwise Transparent file +format whose markup, or absence of markup, has been arranged to thwart +or discourage subsequent modification by readers is not Transparent. +An image format is not Transparent if used for any substantial amount +of text. A copy that is not ``Transparent'' is called ``Opaque''. + +Examples of suitable formats for Transparent copies include plain +@sc{ascii} without markup, Texinfo input format, La@TeX{} input +format, @acronym{SGML} or @acronym{XML} using a publicly available +@acronym{DTD}, and standard-conforming simple @acronym{HTML}, +PostScript or @acronym{PDF} designed for human modification. Examples +of transparent image formats include @acronym{PNG}, @acronym{XCF} and +@acronym{JPG}. Opaque formats include proprietary formats that can be +read and edited only by proprietary word processors, @acronym{SGML} or +@acronym{XML} for which the @acronym{DTD} and/or processing tools are +not generally available, and the machine-generated @acronym{HTML}, +PostScript or @acronym{PDF} produced by some word processors for +output purposes only. + +The ``Title Page'' means, for a printed book, the title page itself, +plus such following pages as are needed to hold, legibly, the material +this License requires to appear in the title page. For works in +formats which do not have any title page as such, ``Title Page'' means +the text near the most prominent appearance of the work's title, +preceding the beginning of the body of the text. + +The ``publisher'' means any person or entity that distributes copies +of the Document to the public. + +A section ``Entitled XYZ'' means a named subunit of the Document whose +title either is precisely XYZ or contains XYZ in parentheses following +text that translates XYZ in another language. (Here XYZ stands for a +specific section name mentioned below, such as ``Acknowledgements'', +``Dedications'', ``Endorsements'', or ``History''.) To ``Preserve the Title'' +of such a section when you modify the Document means that it remains a +section ``Entitled XYZ'' according to this definition. + +The Document may include Warranty Disclaimers next to the notice which +states that this License applies to the Document. These Warranty +Disclaimers are considered to be included by reference in this +License, but only as regards disclaiming warranties: any other +implication that these Warranty Disclaimers may have is void and has +no effect on the meaning of this License. + +@item +VERBATIM COPYING + +You may copy and distribute the Document in any medium, either +commercially or noncommercially, provided that this License, the +copyright notices, and the license notice saying this License applies +to the Document are reproduced in all copies, and that you add no other +conditions whatsoever to those of this License. You may not use +technical measures to obstruct or control the reading or further +copying of the copies you make or distribute. However, you may accept +compensation in exchange for copies. If you distribute a large enough +number of copies you must also follow the conditions in section 3. + +You may also lend copies, under the same conditions stated above, and +you may publicly display copies. + +@item +COPYING IN QUANTITY + +If you publish printed copies (or copies in media that commonly have +printed covers) of the Document, numbering more than 100, and the +Document's license notice requires Cover Texts, you must enclose the +copies in covers that carry, clearly and legibly, all these Cover +Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on +the back cover. Both covers must also clearly and legibly identify +you as the publisher of these copies. The front cover must present +the full title with all words of the title equally prominent and +visible. You may add other material on the covers in addition. +Copying with changes limited to the covers, as long as they preserve +the title of the Document and satisfy these conditions, can be treated +as verbatim copying in other respects. + +If the required texts for either cover are too voluminous to fit +legibly, you should put the first ones listed (as many as fit +reasonably) on the actual cover, and continue the rest onto adjacent +pages. + +If you publish or distribute Opaque copies of the Document numbering +more than 100, you must either include a machine-readable Transparent +copy along with each Opaque copy, or state in or with each Opaque copy +a computer-network location from which the general network-using +public has access to download using public-standard network protocols +a complete Transparent copy of the Document, free of added material. +If you use the latter option, you must take reasonably prudent steps, +when you begin distribution of Opaque copies in quantity, to ensure +that this Transparent copy will remain thus accessible at the stated +location until at least one year after the last time you distribute an +Opaque copy (directly or through your agents or retailers) of that +edition to the public. + +It is requested, but not required, that you contact the authors of the +Document well before redistributing any large number of copies, to give +them a chance to provide you with an updated version of the Document. + +@item +MODIFICATIONS + +You may copy and distribute a Modified Version of the Document under +the conditions of sections 2 and 3 above, provided that you release +the Modified Version under precisely this License, with the Modified +Version filling the role of the Document, thus licensing distribution +and modification of the Modified Version to whoever possesses a copy +of it. In addition, you must do these things in the Modified Version: + +@enumerate A +@item +Use in the Title Page (and on the covers, if any) a title distinct +from that of the Document, and from those of previous versions +(which should, if there were any, be listed in the History section +of the Document). You may use the same title as a previous version +if the original publisher of that version gives permission. + +@item +List on the Title Page, as authors, one or more persons or entities +responsible for authorship of the modifications in the Modified +Version, together with at least five of the principal authors of the +Document (all of its principal authors, if it has fewer than five), +unless they release you from this requirement. + +@item +State on the Title page the name of the publisher of the +Modified Version, as the publisher. + +@item +Preserve all the copyright notices of the Document. + +@item +Add an appropriate copyright notice for your modifications +adjacent to the other copyright notices. + +@item +Include, immediately after the copyright notices, a license notice +giving the public permission to use the Modified Version under the +terms of this License, in the form shown in the Addendum below. + +@item +Preserve in that license notice the full lists of Invariant Sections +and required Cover Texts given in the Document's license notice. + +@item +Include an unaltered copy of this License. + +@item +Preserve the section Entitled ``History'', Preserve its Title, and add +to it an item stating at least the title, year, new authors, and +publisher of the Modified Version as given on the Title Page. If +there is no section Entitled ``History'' in the Document, create one +stating the title, year, authors, and publisher of the Document as +given on its Title Page, then add an item describing the Modified +Version as stated in the previous sentence. + +@item +Preserve the network location, if any, given in the Document for +public access to a Transparent copy of the Document, and likewise +the network locations given in the Document for previous versions +it was based on. These may be placed in the ``History'' section. +You may omit a network location for a work that was published at +least four years before the Document itself, or if the original +publisher of the version it refers to gives permission. + +@item +For any section Entitled ``Acknowledgements'' or ``Dedications'', Preserve +the Title of the section, and preserve in the section all the +substance and tone of each of the contributor acknowledgements and/or +dedications given therein. + +@item +Preserve all the Invariant Sections of the Document, +unaltered in their text and in their titles. Section numbers +or the equivalent are not considered part of the section titles. + +@item +Delete any section Entitled ``Endorsements''. Such a section +may not be included in the Modified Version. + +@item +Do not retitle any existing section to be Entitled ``Endorsements'' or +to conflict in title with any Invariant Section. + +@item +Preserve any Warranty Disclaimers. +@end enumerate + +If the Modified Version includes new front-matter sections or +appendices that qualify as Secondary Sections and contain no material +copied from the Document, you may at your option designate some or all +of these sections as invariant. To do this, add their titles to the +list of Invariant Sections in the Modified Version's license notice. +These titles must be distinct from any other section titles. + +You may add a section Entitled ``Endorsements'', provided it contains +nothing but endorsements of your Modified Version by various +parties---for example, statements of peer review or that the text has +been approved by an organization as the authoritative definition of a +standard. + +You may add a passage of up to five words as a Front-Cover Text, and a +passage of up to 25 words as a Back-Cover Text, to the end of the list +of Cover Texts in the Modified Version. Only one passage of +Front-Cover Text and one of Back-Cover Text may be added by (or +through arrangements made by) any one entity. If the Document already +includes a cover text for the same cover, previously added by you or +by arrangement made by the same entity you are acting on behalf of, +you may not add another; but you may replace the old one, on explicit +permission from the previous publisher that added the old one. + +The author(s) and publisher(s) of the Document do not by this License +give permission to use their names for publicity for or to assert or +imply endorsement of any Modified Version. + +@item +COMBINING DOCUMENTS + +You may combine the Document with other documents released under this +License, under the terms defined in section 4 above for modified +versions, provided that you include in the combination all of the +Invariant Sections of all of the original documents, unmodified, and +list them all as Invariant Sections of your combined work in its +license notice, and that you preserve all their Warranty Disclaimers. + +The combined work need only contain one copy of this License, and +multiple identical Invariant Sections may be replaced with a single +copy. If there are multiple Invariant Sections with the same name but +different contents, make the title of each such section unique by +adding at the end of it, in parentheses, the name of the original +author or publisher of that section if known, or else a unique number. +Make the same adjustment to the section titles in the list of +Invariant Sections in the license notice of the combined work. + +In the combination, you must combine any sections Entitled ``History'' +in the various original documents, forming one section Entitled +``History''; likewise combine any sections Entitled ``Acknowledgements'', +and any sections Entitled ``Dedications''. You must delete all +sections Entitled ``Endorsements.'' + +@item +COLLECTIONS OF DOCUMENTS + +You may make a collection consisting of the Document and other documents +released under this License, and replace the individual copies of this +License in the various documents with a single copy that is included in +the collection, provided that you follow the rules of this License for +verbatim copying of each of the documents in all other respects. + +You may extract a single document from such a collection, and distribute +it individually under this License, provided you insert a copy of this +License into the extracted document, and follow this License in all +other respects regarding verbatim copying of that document. + +@item +AGGREGATION WITH INDEPENDENT WORKS + +A compilation of the Document or its derivatives with other separate +and independent documents or works, in or on a volume of a storage or +distribution medium, is called an ``aggregate'' if the copyright +resulting from the compilation is not used to limit the legal rights +of the compilation's users beyond what the individual works permit. +When the Document is included in an aggregate, this License does not +apply to the other works in the aggregate which are not themselves +derivative works of the Document. + +If the Cover Text requirement of section 3 is applicable to these +copies of the Document, then if the Document is less than one half of +the entire aggregate, the Document's Cover Texts may be placed on +covers that bracket the Document within the aggregate, or the +electronic equivalent of covers if the Document is in electronic form. +Otherwise they must appear on printed covers that bracket the whole +aggregate. + +@item +TRANSLATION + +Translation is considered a kind of modification, so you may +distribute translations of the Document under the terms of section 4. +Replacing Invariant Sections with translations requires special +permission from their copyright holders, but you may include +translations of some or all Invariant Sections in addition to the +original versions of these Invariant Sections. You may include a +translation of this License, and all the license notices in the +Document, and any Warranty Disclaimers, provided that you also include +the original English version of this License and the original versions +of those notices and disclaimers. In case of a disagreement between +the translation and the original version of this License or a notice +or disclaimer, the original version will prevail. + +If a section in the Document is Entitled ``Acknowledgements'', +``Dedications'', or ``History'', the requirement (section 4) to Preserve +its Title (section 1) will typically require changing the actual +title. + +@item +TERMINATION + +You may not copy, modify, sublicense, or distribute the Document +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense, or distribute it is void, and +will automatically terminate your rights under this License. + +However, if you cease all violation of this License, then your license +from a particular copyright holder is reinstated (a) provisionally, +unless and until the copyright holder explicitly and finally +terminates your license, and (b) permanently, if the copyright holder +fails to notify you of the violation by some reasonable means prior to +60 days after the cessation. + +Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + +Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, receipt of a copy of some or all of the same material does +not give you any rights to use it. + +@item +FUTURE REVISIONS OF THIS LICENSE + +The Free Software Foundation may publish new, revised versions +of the GNU Free Documentation 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. See +@uref{http://www.gnu.org/copyleft/}. + +Each version of the License is given a distinguishing version number. +If the Document specifies that a particular numbered version of this +License ``or any later version'' applies to it, you have the option of +following the terms and conditions either of that specified version or +of any later version that has been published (not as a draft) by the +Free Software Foundation. If the Document does not specify a version +number of this License, you may choose any version ever published (not +as a draft) by the Free Software Foundation. If the Document +specifies that a proxy can decide which future versions of this +License can be used, that proxy's public statement of acceptance of a +version permanently authorizes you to choose that version for the +Document. + +@item +RELICENSING + +``Massive Multiauthor Collaboration Site'' (or ``MMC Site'') means any +World Wide Web server that publishes copyrightable works and also +provides prominent facilities for anybody to edit those works. A +public wiki that anybody can edit is an example of such a server. A +``Massive Multiauthor Collaboration'' (or ``MMC'') contained in the +site means any set of copyrightable works thus published on the MMC +site. + +``CC-BY-SA'' means the Creative Commons Attribution-Share Alike 3.0 +license published by Creative Commons Corporation, a not-for-profit +corporation with a principal place of business in San Francisco, +California, as well as future copyleft versions of that license +published by that same organization. + +``Incorporate'' means to publish or republish a Document, in whole or +in part, as part of another Document. + +An MMC is ``eligible for relicensing'' if it is licensed under this +License, and if all works that were first published under this License +somewhere other than this MMC, and subsequently incorporated in whole +or in part into the MMC, (1) had no cover texts or invariant sections, +and (2) were thus incorporated prior to November 1, 2008. + +The operator of an MMC Site may republish an MMC contained in the site +under CC-BY-SA on the same site at any time before August 1, 2009, +provided the MMC is eligible for relicensing. + +@end enumerate + +@page +@heading ADDENDUM: How to use this License for your documents + +To use this License in a document you have written, include a copy of +the License in the document and put the following copyright and +license notices just after the title page: + +@smallexample +@group + Copyright (C) @var{year} @var{your name}. + Permission is granted to copy, distribute and/or modify this document + under the terms of the GNU Free Documentation License, Version 1.3 + or any later version published by the Free Software Foundation; + with no Invariant Sections, no Front-Cover Texts, and no Back-Cover + Texts. A copy of the license is included in the section entitled ``GNU + Free Documentation License''. +@end group +@end smallexample + +If you have Invariant Sections, Front-Cover Texts and Back-Cover Texts, +replace the ``with@dots{}Texts.'' line with this: + +@smallexample +@group + with the Invariant Sections being @var{list their titles}, with + the Front-Cover Texts being @var{list}, and with the Back-Cover Texts + being @var{list}. +@end group +@end smallexample + +If you have Invariant Sections without Cover Texts, or some other +combination of the three, merge those two alternatives to suit the +situation. + +If your document contains nontrivial examples of program code, we +recommend releasing these examples in parallel under your choice of +free software license, such as the GNU General Public License, +to permit their use in free software. + +@c Local Variables: +@c ispell-local-pdict: "ispell-dict" +@c End: + diff --git a/doc/lgpl.texi b/doc/lgpl.texi new file mode 100644 index 00000000..260c0ce0 --- /dev/null +++ b/doc/lgpl.texi @@ -0,0 +1,561 @@ +@c The GNU Lesser General Public License. +@center Version 2.1, February 1999 + +@c This file is intended to be included within another document, +@c hence no sectioning command or @node. + +@display +Copyright @copyright{} 1991, 1999 Free Software Foundation, Inc. +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +Everyone is permitted to copy and distribute verbatim copies +of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts +as the successor of the GNU Library Public License, version 2, hence the +version number 2.1.] +@end display + +@subheading Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software---to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software---typically libraries---of the Free +Software Foundation and other authors who decide to use it. You can use +it too, but we suggest you first think carefully about whether this +license or the ordinary General Public License is the better strategy to +use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +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 and use pieces of it +in new free programs; and that you are informed that you can do these +things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the @dfn{Lesser} General Public License because it +does @emph{Less} to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +``work based on the library'' and a ``work that uses the library''. The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + +@subheading TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + +@enumerate 0 +@item +This License Agreement applies to any software library or other program +which contains a notice placed by the copyright holder or other +authorized party saying it may be distributed under the terms of this +Lesser General Public License (also called ``this License''). Each +licensee is addressed as ``you''. + + A ``library'' means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The ``Library'', below, refers to any such software library or work +which has been distributed under these terms. A ``work based on the +Library'' means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term ``modification''.) + + ``Source code'' for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + +@item +You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + +@item +You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + +@enumerate a +@item +The modified work must itself be a software library. + +@item +You must cause the files modified to carry prominent notices +stating that you changed the files and the date of any change. + +@item +You must cause the whole of the work to be licensed at no +charge to all third parties under the terms of this License. + +@item +If a facility in the modified Library refers to a function or a +table of data to be supplied by an application program that uses +the facility, other than as an argument passed when the facility +is invoked, then you must make a good faith effort to ensure that, +in the event an application does not supply such function or +table, the facility still operates, and performs whatever part of +its purpose remains meaningful. + +(For example, a function in a library to compute square roots has +a purpose that is entirely well-defined independent of the +application. Therefore, Subsection 2d requires that any +application-supplied function or table used by this function must +be optional: if the application does not supply it, the square +root function must still compute square roots.) +@end enumerate + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + +@item +You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + +@item +You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + +@item +A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a ``work that uses the Library''. Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a ``work that uses the Library'' with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a ``work that uses the +library''. The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a ``work that uses the Library'' uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + +@item +As an exception to the Sections above, you may also combine or +link a ``work that uses the Library'' with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + +@enumerate a +@item +Accompany the work with the complete corresponding +machine-readable source code for the Library including whatever +changes were used in the work (which must be distributed under +Sections 1 and 2 above); and, if the work is an executable linked +with the Library, with the complete machine-readable ``work that +uses the Library'', as object code and/or source code, so that the +user can modify the Library and then relink to produce a modified +executable containing the modified Library. (It is understood +that the user who changes the contents of definitions files in the +Library will not necessarily be able to recompile the application +to use the modified definitions.) + +@item +Use a suitable shared library mechanism for linking with the Library. A +suitable mechanism is one that (1) uses at run time a copy of the +library already present on the user's computer system, rather than +copying library functions into the executable, and (2) will operate +properly with a modified version of the library, if the user installs +one, as long as the modified version is interface-compatible with the +version that the work was made with. + +@item +Accompany the work with a written offer, valid for at +least three years, to give the same user the materials +specified in Subsection 6a, above, for a charge no more +than the cost of performing this distribution. + +@item +If distribution of the work is made by offering access to copy +from a designated place, offer equivalent access to copy the above +specified materials from the same place. + +@item +Verify that the user has already received a copy of these +materials or that you have already sent this user a copy. +@end enumerate + + For an executable, the required form of the ``work that uses the +Library'' must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies the +executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + +@item +You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + +@enumerate a +@item +Accompany the combined library with a copy of the same work +based on the Library, uncombined with any other library +facilities. This must be distributed under the terms of the +Sections above. + +@item +Give prominent notice with the combined library of the fact +that part of it is a work based on the Library, and explaining +where to find the accompanying uncombined form of the same work. +@end enumerate + +@item +You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + +@item +You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + +@item +Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + +@item +If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + +@item +If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + +@item +The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +``any later version'', you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + +@item +If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + +@iftex +@heading NO WARRANTY +@end iftex +@ifinfo +@center NO WARRANTY + +@end ifinfo + +@item +BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY ``AS IS'' WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + +@item +IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. +@end enumerate + +@iftex +@heading END OF TERMS AND CONDITIONS +@end iftex +@ifinfo +@center END OF TERMS AND CONDITIONS + +@end ifinfo + +@page +@subheading How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +``copyright'' line and a pointer to where the full notice is found. + +@smallexample +@var{one line to give the library's name and an idea of what it does.} +Copyright (C) @var{year} @var{name of author} + +This library is free software; you can redistribute it and/or modify it +under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, +USA. +@end smallexample + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a ``copyright disclaimer'' for the library, if +necessary. Here is a sample; alter the names: + +@smallexample +Yoyodyne, Inc., hereby disclaims all copyright interest in the library +`Frob' (a library for tweaking knobs) written by James Random Hacker. + +@var{signature of Ty Coon}, 1 April 1990 +Ty Coon, President of Vice +@end smallexample + +That's all there is to it! diff --git a/doc/libhttpserver.3 b/doc/libhttpserver.3 new file mode 100644 index 00000000..1133cd51 --- /dev/null +++ b/doc/libhttpserver.3 @@ -0,0 +1,35 @@ +.TH LIBHTTPSERVER "3" "02 Mar 2013 "libhttpserver" +.SH "NAME" +libhttpserver \- C++ library for creating an embedded Rest HTTP server (and more) +.SH "SYNOPSIS" + + \fB#include + +.SH "DESCRIPTION" +.P +libhttpserver is an api made with the intent to allow to easily realize Rest based webservers. +.P +The details of the API are described in a detailed documentation and in doxygen generated code reference. +.P +.SH "SEE ALSO" +\fBcurl\fP(1), \fBlibcurl\fP(3), \fBlibmicrohttpd\fP(3) + +.SH "LEGAL NOTICE" +libhttpserver is released under the LGPL Version 2.1 or higher. For details on the license please read the appendix in the manual. + +.SH "FILES" +.TP +httpserver.hpp +libhttpserver include file +.TP +libhttpserver.so +libhttpserver library + +.SH "REPORTING BUGS" +Report bugs by using github issue tracker . + +.SH "AUTHORS" +GNU libhttpserver is designed and realized by Sebastiano Merlino . + +.SH "AVAILABILITY" +You can obtain the latest version from https://github.com/etr/libhttpserver . diff --git a/doc/texinfo.tex b/doc/texinfo.tex new file mode 100644 index 00000000..85b68e79 --- /dev/null +++ b/doc/texinfo.tex @@ -0,0 +1,9977 @@ +% texinfo.tex -- TeX macros to handle Texinfo files. +% +% Load plain if necessary, i.e., if running under initex. +\expandafter\ifx\csname fmtname\endcsname\relax\input plain\fi +% +\def\texinfoversion{2012-03-11.15} +% +% Copyright 1985, 1986, 1988, 1990, 1991, 1992, 1993, 1994, 1995, +% 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, +% 2007, 2008, 2009, 2010, 2011, 2012 Free Software Foundation, Inc. +% +% This texinfo.tex file 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 3 of the +% License, or (at your option) any later version. +% +% This texinfo.tex file 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, see . +% +% As a special exception, when this file is read by TeX when processing +% a Texinfo source document, you may use the result without +% restriction. (This has been our intent since Texinfo was invented.) +% +% Please try the latest version of texinfo.tex before submitting bug +% reports; you can get the latest version from: +% http://www.gnu.org/software/texinfo/ (the Texinfo home page), or +% ftp://tug.org/tex/texinfo.tex +% (and all CTAN mirrors, see http://www.ctan.org). +% The texinfo.tex in any given distribution could well be out +% of date, so if that's what you're using, please check. +% +% Send bug reports to bug-texinfo@gnu.org. Please include including a +% complete document in each bug report with which we can reproduce the +% problem. Patches are, of course, greatly appreciated. +% +% To process a Texinfo manual with TeX, it's most reliable to use the +% texi2dvi shell script that comes with the distribution. For a simple +% manual foo.texi, however, you can get away with this: +% tex foo.texi +% texindex foo.?? +% tex foo.texi +% tex foo.texi +% dvips foo.dvi -o # or whatever; this makes foo.ps. +% The extra TeX runs get the cross-reference information correct. +% Sometimes one run after texindex suffices, and sometimes you need more +% than two; texi2dvi does it as many times as necessary. +% +% It is possible to adapt texinfo.tex for other languages, to some +% extent. You can get the existing language-specific files from the +% full Texinfo distribution. +% +% The GNU Texinfo home page is http://www.gnu.org/software/texinfo. + + +\message{Loading texinfo [version \texinfoversion]:} + +% If in a .fmt file, print the version number +% and turn on active characters that we couldn't do earlier because +% they might have appeared in the input file name. +\everyjob{\message{[Texinfo version \texinfoversion]}% + \catcode`+=\active \catcode`\_=\active} + +\chardef\other=12 + +% We never want plain's \outer definition of \+ in Texinfo. +% For @tex, we can use \tabalign. +\let\+ = \relax + +% Save some plain tex macros whose names we will redefine. +\let\ptexb=\b +\let\ptexbullet=\bullet +\let\ptexc=\c +\let\ptexcomma=\, +\let\ptexdot=\. +\let\ptexdots=\dots +\let\ptexend=\end +\let\ptexequiv=\equiv +\let\ptexexclam=\! +\let\ptexfootnote=\footnote +\let\ptexgtr=> +\let\ptexhat=^ +\let\ptexi=\i +\let\ptexindent=\indent +\let\ptexinsert=\insert +\let\ptexlbrace=\{ +\let\ptexless=< +\let\ptexnewwrite\newwrite +\let\ptexnoindent=\noindent +\let\ptexplus=+ +\let\ptexraggedright=\raggedright +\let\ptexrbrace=\} +\let\ptexslash=\/ +\let\ptexstar=\* +\let\ptext=\t +\let\ptextop=\top +{\catcode`\'=\active \global\let\ptexquoteright'}% active in plain's math mode + +% If this character appears in an error message or help string, it +% starts a new line in the output. +\newlinechar = `^^J + +% Use TeX 3.0's \inputlineno to get the line number, for better error +% messages, but if we're using an old version of TeX, don't do anything. +% +\ifx\inputlineno\thisisundefined + \let\linenumber = \empty % Pre-3.0. +\else + \def\linenumber{l.\the\inputlineno:\space} +\fi + +% Set up fixed words for English if not already set. +\ifx\putwordAppendix\undefined \gdef\putwordAppendix{Appendix}\fi +\ifx\putwordChapter\undefined \gdef\putwordChapter{Chapter}\fi +\ifx\putworderror\undefined \gdef\putworderror{error}\fi +\ifx\putwordfile\undefined \gdef\putwordfile{file}\fi +\ifx\putwordin\undefined \gdef\putwordin{in}\fi +\ifx\putwordIndexIsEmpty\undefined \gdef\putwordIndexIsEmpty{(Index is empty)}\fi +\ifx\putwordIndexNonexistent\undefined \gdef\putwordIndexNonexistent{(Index is nonexistent)}\fi +\ifx\putwordInfo\undefined \gdef\putwordInfo{Info}\fi +\ifx\putwordInstanceVariableof\undefined \gdef\putwordInstanceVariableof{Instance Variable of}\fi +\ifx\putwordMethodon\undefined \gdef\putwordMethodon{Method on}\fi +\ifx\putwordNoTitle\undefined \gdef\putwordNoTitle{No Title}\fi +\ifx\putwordof\undefined \gdef\putwordof{of}\fi +\ifx\putwordon\undefined \gdef\putwordon{on}\fi +\ifx\putwordpage\undefined \gdef\putwordpage{page}\fi +\ifx\putwordsection\undefined \gdef\putwordsection{section}\fi +\ifx\putwordSection\undefined \gdef\putwordSection{Section}\fi +\ifx\putwordsee\undefined \gdef\putwordsee{see}\fi +\ifx\putwordSee\undefined \gdef\putwordSee{See}\fi +\ifx\putwordShortTOC\undefined \gdef\putwordShortTOC{Short Contents}\fi +\ifx\putwordTOC\undefined \gdef\putwordTOC{Table of Contents}\fi +% +\ifx\putwordMJan\undefined \gdef\putwordMJan{January}\fi +\ifx\putwordMFeb\undefined \gdef\putwordMFeb{February}\fi +\ifx\putwordMMar\undefined \gdef\putwordMMar{March}\fi +\ifx\putwordMApr\undefined \gdef\putwordMApr{April}\fi +\ifx\putwordMMay\undefined \gdef\putwordMMay{May}\fi +\ifx\putwordMJun\undefined \gdef\putwordMJun{June}\fi +\ifx\putwordMJul\undefined \gdef\putwordMJul{July}\fi +\ifx\putwordMAug\undefined \gdef\putwordMAug{August}\fi +\ifx\putwordMSep\undefined \gdef\putwordMSep{September}\fi +\ifx\putwordMOct\undefined \gdef\putwordMOct{October}\fi +\ifx\putwordMNov\undefined \gdef\putwordMNov{November}\fi +\ifx\putwordMDec\undefined \gdef\putwordMDec{December}\fi +% +\ifx\putwordDefmac\undefined \gdef\putwordDefmac{Macro}\fi +\ifx\putwordDefspec\undefined \gdef\putwordDefspec{Special Form}\fi +\ifx\putwordDefvar\undefined \gdef\putwordDefvar{Variable}\fi +\ifx\putwordDefopt\undefined \gdef\putwordDefopt{User Option}\fi +\ifx\putwordDeffunc\undefined \gdef\putwordDeffunc{Function}\fi + +% Since the category of space is not known, we have to be careful. +\chardef\spacecat = 10 +\def\spaceisspace{\catcode`\ =\spacecat} + +% sometimes characters are active, so we need control sequences. +\chardef\ampChar = `\& +\chardef\colonChar = `\: +\chardef\commaChar = `\, +\chardef\dashChar = `\- +\chardef\dotChar = `\. +\chardef\exclamChar= `\! +\chardef\hashChar = `\# +\chardef\lquoteChar= `\` +\chardef\questChar = `\? +\chardef\rquoteChar= `\' +\chardef\semiChar = `\; +\chardef\slashChar = `\/ +\chardef\underChar = `\_ + +% Ignore a token. +% +\def\gobble#1{} + +% The following is used inside several \edef's. +\def\makecsname#1{\expandafter\noexpand\csname#1\endcsname} + +% Hyphenation fixes. +\hyphenation{ + Flor-i-da Ghost-script Ghost-view Mac-OS Post-Script + ap-pen-dix bit-map bit-maps + data-base data-bases eshell fall-ing half-way long-est man-u-script + man-u-scripts mini-buf-fer mini-buf-fers over-view par-a-digm + par-a-digms rath-er rec-tan-gu-lar ro-bot-ics se-vere-ly set-up spa-ces + spell-ing spell-ings + stand-alone strong-est time-stamp time-stamps which-ever white-space + wide-spread wrap-around +} + +% Margin to add to right of even pages, to left of odd pages. +\newdimen\bindingoffset +\newdimen\normaloffset +\newdimen\pagewidth \newdimen\pageheight + +% For a final copy, take out the rectangles +% that mark overfull boxes (in case you have decided +% that the text looks ok even though it passes the margin). +% +\def\finalout{\overfullrule=0pt } + +% Sometimes it is convenient to have everything in the transcript file +% and nothing on the terminal. We don't just call \tracingall here, +% since that produces some useless output on the terminal. We also make +% some effort to order the tracing commands to reduce output in the log +% file; cf. trace.sty in LaTeX. +% +\def\gloggingall{\begingroup \globaldefs = 1 \loggingall \endgroup}% +\def\loggingall{% + \tracingstats2 + \tracingpages1 + \tracinglostchars2 % 2 gives us more in etex + \tracingparagraphs1 + \tracingoutput1 + \tracingmacros2 + \tracingrestores1 + \showboxbreadth\maxdimen \showboxdepth\maxdimen + \ifx\eTeXversion\thisisundefined\else % etex gives us more logging + \tracingscantokens1 + \tracingifs1 + \tracinggroups1 + \tracingnesting2 + \tracingassigns1 + \fi + \tracingcommands3 % 3 gives us more in etex + \errorcontextlines16 +}% + +% @errormsg{MSG}. Do the index-like expansions on MSG, but if things +% aren't perfect, it's not the end of the world, being an error message, +% after all. +% +\def\errormsg{\begingroup \indexnofonts \doerrormsg} +\def\doerrormsg#1{\errmessage{#1}} + +% add check for \lastpenalty to plain's definitions. If the last thing +% we did was a \nobreak, we don't want to insert more space. +% +\def\smallbreak{\ifnum\lastpenalty<10000\par\ifdim\lastskip<\smallskipamount + \removelastskip\penalty-50\smallskip\fi\fi} +\def\medbreak{\ifnum\lastpenalty<10000\par\ifdim\lastskip<\medskipamount + \removelastskip\penalty-100\medskip\fi\fi} +\def\bigbreak{\ifnum\lastpenalty<10000\par\ifdim\lastskip<\bigskipamount + \removelastskip\penalty-200\bigskip\fi\fi} + +% Do @cropmarks to get crop marks. +% +\newif\ifcropmarks +\let\cropmarks = \cropmarkstrue +% +% Dimensions to add cropmarks at corners. +% Added by P. A. MacKay, 12 Nov. 1986 +% +\newdimen\outerhsize \newdimen\outervsize % set by the paper size routines +\newdimen\cornerlong \cornerlong=1pc +\newdimen\cornerthick \cornerthick=.3pt +\newdimen\topandbottommargin \topandbottommargin=.75in + +% Output a mark which sets \thischapter, \thissection and \thiscolor. +% We dump everything together because we only have one kind of mark. +% This works because we only use \botmark / \topmark, not \firstmark. +% +% A mark contains a subexpression of the \ifcase ... \fi construct. +% \get*marks macros below extract the needed part using \ifcase. +% +% Another complication is to let the user choose whether \thischapter +% (\thissection) refers to the chapter (section) in effect at the top +% of a page, or that at the bottom of a page. The solution is +% described on page 260 of The TeXbook. It involves outputting two +% marks for the sectioning macros, one before the section break, and +% one after. I won't pretend I can describe this better than DEK... +\def\domark{% + \toks0=\expandafter{\lastchapterdefs}% + \toks2=\expandafter{\lastsectiondefs}% + \toks4=\expandafter{\prevchapterdefs}% + \toks6=\expandafter{\prevsectiondefs}% + \toks8=\expandafter{\lastcolordefs}% + \mark{% + \the\toks0 \the\toks2 + \noexpand\or \the\toks4 \the\toks6 + \noexpand\else \the\toks8 + }% +} +% \topmark doesn't work for the very first chapter (after the title +% page or the contents), so we use \firstmark there -- this gets us +% the mark with the chapter defs, unless the user sneaks in, e.g., +% @setcolor (or @url, or @link, etc.) between @contents and the very +% first @chapter. +\def\gettopheadingmarks{% + \ifcase0\topmark\fi + \ifx\thischapter\empty \ifcase0\firstmark\fi \fi +} +\def\getbottomheadingmarks{\ifcase1\botmark\fi} +\def\getcolormarks{\ifcase2\topmark\fi} + +% Avoid "undefined control sequence" errors. +\def\lastchapterdefs{} +\def\lastsectiondefs{} +\def\prevchapterdefs{} +\def\prevsectiondefs{} +\def\lastcolordefs{} + +% Main output routine. +\chardef\PAGE = 255 +\output = {\onepageout{\pagecontents\PAGE}} + +\newbox\headlinebox +\newbox\footlinebox + +% \onepageout takes a vbox as an argument. Note that \pagecontents +% does insertions, but you have to call it yourself. +\def\onepageout#1{% + \ifcropmarks \hoffset=0pt \else \hoffset=\normaloffset \fi + % + \ifodd\pageno \advance\hoffset by \bindingoffset + \else \advance\hoffset by -\bindingoffset\fi + % + % Do this outside of the \shipout so @code etc. will be expanded in + % the headline as they should be, not taken literally (outputting ''code). + \ifodd\pageno \getoddheadingmarks \else \getevenheadingmarks \fi + \setbox\headlinebox = \vbox{\let\hsize=\pagewidth \makeheadline}% + \ifodd\pageno \getoddfootingmarks \else \getevenfootingmarks \fi + \setbox\footlinebox = \vbox{\let\hsize=\pagewidth \makefootline}% + % + {% + % Have to do this stuff outside the \shipout because we want it to + % take effect in \write's, yet the group defined by the \vbox ends + % before the \shipout runs. + % + \indexdummies % don't expand commands in the output. + \normalturnoffactive % \ in index entries must not stay \, e.g., if + % the page break happens to be in the middle of an example. + % We don't want .vr (or whatever) entries like this: + % \entry{{\tt \indexbackslash }acronym}{32}{\code {\acronym}} + % "\acronym" won't work when it's read back in; + % it needs to be + % {\code {{\tt \backslashcurfont }acronym} + \shipout\vbox{% + % Do this early so pdf references go to the beginning of the page. + \ifpdfmakepagedest \pdfdest name{\the\pageno} xyz\fi + % + \ifcropmarks \vbox to \outervsize\bgroup + \hsize = \outerhsize + \vskip-\topandbottommargin + \vtop to0pt{% + \line{\ewtop\hfil\ewtop}% + \nointerlineskip + \line{% + \vbox{\moveleft\cornerthick\nstop}% + \hfill + \vbox{\moveright\cornerthick\nstop}% + }% + \vss}% + \vskip\topandbottommargin + \line\bgroup + \hfil % center the page within the outer (page) hsize. + \ifodd\pageno\hskip\bindingoffset\fi + \vbox\bgroup + \fi + % + \unvbox\headlinebox + \pagebody{#1}% + \ifdim\ht\footlinebox > 0pt + % Only leave this space if the footline is nonempty. + % (We lessened \vsize for it in \oddfootingyyy.) + % The \baselineskip=24pt in plain's \makefootline has no effect. + \vskip 24pt + \unvbox\footlinebox + \fi + % + \ifcropmarks + \egroup % end of \vbox\bgroup + \hfil\egroup % end of (centering) \line\bgroup + \vskip\topandbottommargin plus1fill minus1fill + \boxmaxdepth = \cornerthick + \vbox to0pt{\vss + \line{% + \vbox{\moveleft\cornerthick\nsbot}% + \hfill + \vbox{\moveright\cornerthick\nsbot}% + }% + \nointerlineskip + \line{\ewbot\hfil\ewbot}% + }% + \egroup % \vbox from first cropmarks clause + \fi + }% end of \shipout\vbox + }% end of group with \indexdummies + \advancepageno + \ifnum\outputpenalty>-20000 \else\dosupereject\fi +} + +\newinsert\margin \dimen\margin=\maxdimen + +\def\pagebody#1{\vbox to\pageheight{\boxmaxdepth=\maxdepth #1}} +{\catcode`\@ =11 +\gdef\pagecontents#1{\ifvoid\topins\else\unvbox\topins\fi +% marginal hacks, juha@viisa.uucp (Juha Takala) +\ifvoid\margin\else % marginal info is present + \rlap{\kern\hsize\vbox to\z@{\kern1pt\box\margin \vss}}\fi +\dimen@=\dp#1\relax \unvbox#1\relax +\ifvoid\footins\else\vskip\skip\footins\footnoterule \unvbox\footins\fi +\ifr@ggedbottom \kern-\dimen@ \vfil \fi} +} + +% Here are the rules for the cropmarks. Note that they are +% offset so that the space between them is truly \outerhsize or \outervsize +% (P. A. MacKay, 12 November, 1986) +% +\def\ewtop{\vrule height\cornerthick depth0pt width\cornerlong} +\def\nstop{\vbox + {\hrule height\cornerthick depth\cornerlong width\cornerthick}} +\def\ewbot{\vrule height0pt depth\cornerthick width\cornerlong} +\def\nsbot{\vbox + {\hrule height\cornerlong depth\cornerthick width\cornerthick}} + +% Parse an argument, then pass it to #1. The argument is the rest of +% the input line (except we remove a trailing comment). #1 should be a +% macro which expects an ordinary undelimited TeX argument. +% +\def\parsearg{\parseargusing{}} +\def\parseargusing#1#2{% + \def\argtorun{#2}% + \begingroup + \obeylines + \spaceisspace + #1% + \parseargline\empty% Insert the \empty token, see \finishparsearg below. +} + +{\obeylines % + \gdef\parseargline#1^^M{% + \endgroup % End of the group started in \parsearg. + \argremovecomment #1\comment\ArgTerm% + }% +} + +% First remove any @comment, then any @c comment. +\def\argremovecomment#1\comment#2\ArgTerm{\argremovec #1\c\ArgTerm} +\def\argremovec#1\c#2\ArgTerm{\argcheckspaces#1\^^M\ArgTerm} + +% Each occurrence of `\^^M' or `\^^M' is replaced by a single space. +% +% \argremovec might leave us with trailing space, e.g., +% @end itemize @c foo +% This space token undergoes the same procedure and is eventually removed +% by \finishparsearg. +% +\def\argcheckspaces#1\^^M{\argcheckspacesX#1\^^M \^^M} +\def\argcheckspacesX#1 \^^M{\argcheckspacesY#1\^^M} +\def\argcheckspacesY#1\^^M#2\^^M#3\ArgTerm{% + \def\temp{#3}% + \ifx\temp\empty + % Do not use \next, perhaps the caller of \parsearg uses it; reuse \temp: + \let\temp\finishparsearg + \else + \let\temp\argcheckspaces + \fi + % Put the space token in: + \temp#1 #3\ArgTerm +} + +% If a _delimited_ argument is enclosed in braces, they get stripped; so +% to get _exactly_ the rest of the line, we had to prevent such situation. +% We prepended an \empty token at the very beginning and we expand it now, +% just before passing the control to \argtorun. +% (Similarly, we have to think about #3 of \argcheckspacesY above: it is +% either the null string, or it ends with \^^M---thus there is no danger +% that a pair of braces would be stripped. +% +% But first, we have to remove the trailing space token. +% +\def\finishparsearg#1 \ArgTerm{\expandafter\argtorun\expandafter{#1}} + +% \parseargdef\foo{...} +% is roughly equivalent to +% \def\foo{\parsearg\Xfoo} +% \def\Xfoo#1{...} +% +% Actually, I use \csname\string\foo\endcsname, ie. \\foo, as it is my +% favourite TeX trick. --kasal, 16nov03 + +\def\parseargdef#1{% + \expandafter \doparseargdef \csname\string#1\endcsname #1% +} +\def\doparseargdef#1#2{% + \def#2{\parsearg#1}% + \def#1##1% +} + +% Several utility definitions with active space: +{ + \obeyspaces + \gdef\obeyedspace{ } + + % Make each space character in the input produce a normal interword + % space in the output. Don't allow a line break at this space, as this + % is used only in environments like @example, where each line of input + % should produce a line of output anyway. + % + \gdef\sepspaces{\obeyspaces\let =\tie} + + % If an index command is used in an @example environment, any spaces + % therein should become regular spaces in the raw index file, not the + % expansion of \tie (\leavevmode \penalty \@M \ ). + \gdef\unsepspaces{\let =\space} +} + + +\def\flushcr{\ifx\par\lisppar \def\next##1{}\else \let\next=\relax \fi \next} + +% Define the framework for environments in texinfo.tex. It's used like this: +% +% \envdef\foo{...} +% \def\Efoo{...} +% +% It's the responsibility of \envdef to insert \begingroup before the +% actual body; @end closes the group after calling \Efoo. \envdef also +% defines \thisenv, so the current environment is known; @end checks +% whether the environment name matches. The \checkenv macro can also be +% used to check whether the current environment is the one expected. +% +% Non-false conditionals (@iftex, @ifset) don't fit into this, so they +% are not treated as environments; they don't open a group. (The +% implementation of @end takes care not to call \endgroup in this +% special case.) + + +% At run-time, environments start with this: +\def\startenvironment#1{\begingroup\def\thisenv{#1}} +% initialize +\let\thisenv\empty + +% ... but they get defined via ``\envdef\foo{...}'': +\long\def\envdef#1#2{\def#1{\startenvironment#1#2}} +\def\envparseargdef#1#2{\parseargdef#1{\startenvironment#1#2}} + +% Check whether we're in the right environment: +\def\checkenv#1{% + \def\temp{#1}% + \ifx\thisenv\temp + \else + \badenverr + \fi +} + +% Environment mismatch, #1 expected: +\def\badenverr{% + \errhelp = \EMsimple + \errmessage{This command can appear only \inenvironment\temp, + not \inenvironment\thisenv}% +} +\def\inenvironment#1{% + \ifx#1\empty + outside of any environment% + \else + in environment \expandafter\string#1% + \fi +} + +% @end foo executes the definition of \Efoo. +% But first, it executes a specialized version of \checkenv +% +\parseargdef\end{% + \if 1\csname iscond.#1\endcsname + \else + % The general wording of \badenverr may not be ideal. + \expandafter\checkenv\csname#1\endcsname + \csname E#1\endcsname + \endgroup + \fi +} + +\newhelp\EMsimple{Press RETURN to continue.} + + +% Be sure we're in horizontal mode when doing a tie, since we make space +% equivalent to this in @example-like environments. Otherwise, a space +% at the beginning of a line will start with \penalty -- and +% since \penalty is valid in vertical mode, we'd end up putting the +% penalty on the vertical list instead of in the new paragraph. +{\catcode`@ = 11 + % Avoid using \@M directly, because that causes trouble + % if the definition is written into an index file. + \global\let\tiepenalty = \@M + \gdef\tie{\leavevmode\penalty\tiepenalty\ } +} + +% @: forces normal size whitespace following. +\def\:{\spacefactor=1000 } + +% @* forces a line break. +\def\*{\hfil\break\hbox{}\ignorespaces} + +% @/ allows a line break. +\let\/=\allowbreak + +% @. is an end-of-sentence period. +\def\.{.\spacefactor=\endofsentencespacefactor\space} + +% @! is an end-of-sentence bang. +\def\!{!\spacefactor=\endofsentencespacefactor\space} + +% @? is an end-of-sentence query. +\def\?{?\spacefactor=\endofsentencespacefactor\space} + +% @frenchspacing on|off says whether to put extra space after punctuation. +% +\def\onword{on} +\def\offword{off} +% +\parseargdef\frenchspacing{% + \def\temp{#1}% + \ifx\temp\onword \plainfrenchspacing + \else\ifx\temp\offword \plainnonfrenchspacing + \else + \errhelp = \EMsimple + \errmessage{Unknown @frenchspacing option `\temp', must be on|off}% + \fi\fi +} + +% @w prevents a word break. Without the \leavevmode, @w at the +% beginning of a paragraph, when TeX is still in vertical mode, would +% produce a whole line of output instead of starting the paragraph. +\def\w#1{\leavevmode\hbox{#1}} + +% @group ... @end group forces ... to be all on one page, by enclosing +% it in a TeX vbox. We use \vtop instead of \vbox to construct the box +% to keep its height that of a normal line. According to the rules for +% \topskip (p.114 of the TeXbook), the glue inserted is +% max (\topskip - \ht (first item), 0). If that height is large, +% therefore, no glue is inserted, and the space between the headline and +% the text is small, which looks bad. +% +% Another complication is that the group might be very large. This can +% cause the glue on the previous page to be unduly stretched, because it +% does not have much material. In this case, it's better to add an +% explicit \vfill so that the extra space is at the bottom. The +% threshold for doing this is if the group is more than \vfilllimit +% percent of a page (\vfilllimit can be changed inside of @tex). +% +\newbox\groupbox +\def\vfilllimit{0.7} +% +\envdef\group{% + \ifnum\catcode`\^^M=\active \else + \errhelp = \groupinvalidhelp + \errmessage{@group invalid in context where filling is enabled}% + \fi + \startsavinginserts + % + \setbox\groupbox = \vtop\bgroup + % Do @comment since we are called inside an environment such as + % @example, where each end-of-line in the input causes an + % end-of-line in the output. We don't want the end-of-line after + % the `@group' to put extra space in the output. Since @group + % should appear on a line by itself (according to the Texinfo + % manual), we don't worry about eating any user text. + \comment +} +% +% The \vtop produces a box with normal height and large depth; thus, TeX puts +% \baselineskip glue before it, and (when the next line of text is done) +% \lineskip glue after it. Thus, space below is not quite equal to space +% above. But it's pretty close. +\def\Egroup{% + % To get correct interline space between the last line of the group + % and the first line afterwards, we have to propagate \prevdepth. + \endgraf % Not \par, as it may have been set to \lisppar. + \global\dimen1 = \prevdepth + \egroup % End the \vtop. + % \dimen0 is the vertical size of the group's box. + \dimen0 = \ht\groupbox \advance\dimen0 by \dp\groupbox + % \dimen2 is how much space is left on the page (more or less). + \dimen2 = \pageheight \advance\dimen2 by -\pagetotal + % if the group doesn't fit on the current page, and it's a big big + % group, force a page break. + \ifdim \dimen0 > \dimen2 + \ifdim \pagetotal < \vfilllimit\pageheight + \page + \fi + \fi + \box\groupbox + \prevdepth = \dimen1 + \checkinserts +} +% +% TeX puts in an \escapechar (i.e., `@') at the beginning of the help +% message, so this ends up printing `@group can only ...'. +% +\newhelp\groupinvalidhelp{% +group can only be used in environments such as @example,^^J% +where each line of input produces a line of output.} + +% @need space-in-mils +% forces a page break if there is not space-in-mils remaining. + +\newdimen\mil \mil=0.001in + +\parseargdef\need{% + % Ensure vertical mode, so we don't make a big box in the middle of a + % paragraph. + \par + % + % If the @need value is less than one line space, it's useless. + \dimen0 = #1\mil + \dimen2 = \ht\strutbox + \advance\dimen2 by \dp\strutbox + \ifdim\dimen0 > \dimen2 + % + % Do a \strut just to make the height of this box be normal, so the + % normal leading is inserted relative to the preceding line. + % And a page break here is fine. + \vtop to #1\mil{\strut\vfil}% + % + % TeX does not even consider page breaks if a penalty added to the + % main vertical list is 10000 or more. But in order to see if the + % empty box we just added fits on the page, we must make it consider + % page breaks. On the other hand, we don't want to actually break the + % page after the empty box. So we use a penalty of 9999. + % + % There is an extremely small chance that TeX will actually break the + % page at this \penalty, if there are no other feasible breakpoints in + % sight. (If the user is using lots of big @group commands, which + % almost-but-not-quite fill up a page, TeX will have a hard time doing + % good page breaking, for example.) However, I could not construct an + % example where a page broke at this \penalty; if it happens in a real + % document, then we can reconsider our strategy. + \penalty9999 + % + % Back up by the size of the box, whether we did a page break or not. + \kern -#1\mil + % + % Do not allow a page break right after this kern. + \nobreak + \fi +} + +% @br forces paragraph break (and is undocumented). + +\let\br = \par + +% @page forces the start of a new page. +% +\def\page{\par\vfill\supereject} + +% @exdent text.... +% outputs text on separate line in roman font, starting at standard page margin + +% This records the amount of indent in the innermost environment. +% That's how much \exdent should take out. +\newskip\exdentamount + +% This defn is used inside fill environments such as @defun. +\parseargdef\exdent{\hfil\break\hbox{\kern -\exdentamount{\rm#1}}\hfil\break} + +% This defn is used inside nofill environments such as @example. +\parseargdef\nofillexdent{{\advance \leftskip by -\exdentamount + \leftline{\hskip\leftskip{\rm#1}}}} + +% @inmargin{WHICH}{TEXT} puts TEXT in the WHICH margin next to the current +% paragraph. For more general purposes, use the \margin insertion +% class. WHICH is `l' or `r'. Not documented, written for gawk manual. +% +\newskip\inmarginspacing \inmarginspacing=1cm +\def\strutdepth{\dp\strutbox} +% +\def\doinmargin#1#2{\strut\vadjust{% + \nobreak + \kern-\strutdepth + \vtop to \strutdepth{% + \baselineskip=\strutdepth + \vss + % if you have multiple lines of stuff to put here, you'll need to + % make the vbox yourself of the appropriate size. + \ifx#1l% + \llap{\ignorespaces #2\hskip\inmarginspacing}% + \else + \rlap{\hskip\hsize \hskip\inmarginspacing \ignorespaces #2}% + \fi + \null + }% +}} +\def\inleftmargin{\doinmargin l} +\def\inrightmargin{\doinmargin r} +% +% @inmargin{TEXT [, RIGHT-TEXT]} +% (if RIGHT-TEXT is given, use TEXT for left page, RIGHT-TEXT for right; +% else use TEXT for both). +% +\def\inmargin#1{\parseinmargin #1,,\finish} +\def\parseinmargin#1,#2,#3\finish{% not perfect, but better than nothing. + \setbox0 = \hbox{\ignorespaces #2}% + \ifdim\wd0 > 0pt + \def\lefttext{#1}% have both texts + \def\righttext{#2}% + \else + \def\lefttext{#1}% have only one text + \def\righttext{#1}% + \fi + % + \ifodd\pageno + \def\temp{\inrightmargin\righttext}% odd page -> outside is right margin + \else + \def\temp{\inleftmargin\lefttext}% + \fi + \temp +} + +% @| inserts a changebar to the left of the current line. It should +% surround any changed text. This approach does *not* work if the +% change spans more than two lines of output. To handle that, we would +% have adopt a much more difficult approach (putting marks into the main +% vertical list for the beginning and end of each change). This command +% is not documented, not supported, and doesn't work. +% +\def\|{% + % \vadjust can only be used in horizontal mode. + \leavevmode + % + % Append this vertical mode material after the current line in the output. + \vadjust{% + % We want to insert a rule with the height and depth of the current + % leading; that is exactly what \strutbox is supposed to record. + \vskip-\baselineskip + % + % \vadjust-items are inserted at the left edge of the type. So + % the \llap here moves out into the left-hand margin. + \llap{% + % + % For a thicker or thinner bar, change the `1pt'. + \vrule height\baselineskip width1pt + % + % This is the space between the bar and the text. + \hskip 12pt + }% + }% +} + +% @include FILE -- \input text of FILE. +% +\def\include{\parseargusing\filenamecatcodes\includezzz} +\def\includezzz#1{% + \pushthisfilestack + \def\thisfile{#1}% + {% + \makevalueexpandable % we want to expand any @value in FILE. + \turnoffactive % and allow special characters in the expansion + \indexnofonts % Allow `@@' and other weird things in file names. + \wlog{texinfo.tex: doing @include of #1^^J}% + \edef\temp{\noexpand\input #1 }% + % + % This trickery is to read FILE outside of a group, in case it makes + % definitions, etc. + \expandafter + }\temp + \popthisfilestack +} +\def\filenamecatcodes{% + \catcode`\\=\other + \catcode`~=\other + \catcode`^=\other + \catcode`_=\other + \catcode`|=\other + \catcode`<=\other + \catcode`>=\other + \catcode`+=\other + \catcode`-=\other + \catcode`\`=\other + \catcode`\'=\other +} + +\def\pushthisfilestack{% + \expandafter\pushthisfilestackX\popthisfilestack\StackTerm +} +\def\pushthisfilestackX{% + \expandafter\pushthisfilestackY\thisfile\StackTerm +} +\def\pushthisfilestackY #1\StackTerm #2\StackTerm {% + \gdef\popthisfilestack{\gdef\thisfile{#1}\gdef\popthisfilestack{#2}}% +} + +\def\popthisfilestack{\errthisfilestackempty} +\def\errthisfilestackempty{\errmessage{Internal error: + the stack of filenames is empty.}} +% +\def\thisfile{} + +% @center line +% outputs that line, centered. +% +\parseargdef\center{% + \ifhmode + \let\centersub\centerH + \else + \let\centersub\centerV + \fi + \centersub{\hfil \ignorespaces#1\unskip \hfil}% + \let\centersub\relax % don't let the definition persist, just in case +} +\def\centerH#1{{% + \hfil\break + \advance\hsize by -\leftskip + \advance\hsize by -\rightskip + \line{#1}% + \break +}} +% +\newcount\centerpenalty +\def\centerV#1{% + % The idea here is the same as in \startdefun, \cartouche, etc.: if + % @center is the first thing after a section heading, we need to wipe + % out the negative parskip inserted by \sectionheading, but still + % prevent a page break here. + \centerpenalty = \lastpenalty + \ifnum\centerpenalty>10000 \vskip\parskip \fi + \ifnum\centerpenalty>9999 \penalty\centerpenalty \fi + \line{\kern\leftskip #1\kern\rightskip}% +} + +% @sp n outputs n lines of vertical space +% +\parseargdef\sp{\vskip #1\baselineskip} + +% @comment ...line which is ignored... +% @c is the same as @comment +% @ignore ... @end ignore is another way to write a comment +% +\def\comment{\begingroup \catcode`\^^M=\other% +\catcode`\@=\other \catcode`\{=\other \catcode`\}=\other% +\commentxxx} +{\catcode`\^^M=\other \gdef\commentxxx#1^^M{\endgroup}} +% +\let\c=\comment + +% @paragraphindent NCHARS +% We'll use ems for NCHARS, close enough. +% NCHARS can also be the word `asis' or `none'. +% We cannot feasibly implement @paragraphindent asis, though. +% +\def\asisword{asis} % no translation, these are keywords +\def\noneword{none} +% +\parseargdef\paragraphindent{% + \def\temp{#1}% + \ifx\temp\asisword + \else + \ifx\temp\noneword + \defaultparindent = 0pt + \else + \defaultparindent = #1em + \fi + \fi + \parindent = \defaultparindent +} + +% @exampleindent NCHARS +% We'll use ems for NCHARS like @paragraphindent. +% It seems @exampleindent asis isn't necessary, but +% I preserve it to make it similar to @paragraphindent. +\parseargdef\exampleindent{% + \def\temp{#1}% + \ifx\temp\asisword + \else + \ifx\temp\noneword + \lispnarrowing = 0pt + \else + \lispnarrowing = #1em + \fi + \fi +} + +% @firstparagraphindent WORD +% If WORD is `none', then suppress indentation of the first paragraph +% after a section heading. If WORD is `insert', then do indent at such +% paragraphs. +% +% The paragraph indentation is suppressed or not by calling +% \suppressfirstparagraphindent, which the sectioning commands do. +% We switch the definition of this back and forth according to WORD. +% By default, we suppress indentation. +% +\def\suppressfirstparagraphindent{\dosuppressfirstparagraphindent} +\def\insertword{insert} +% +\parseargdef\firstparagraphindent{% + \def\temp{#1}% + \ifx\temp\noneword + \let\suppressfirstparagraphindent = \dosuppressfirstparagraphindent + \else\ifx\temp\insertword + \let\suppressfirstparagraphindent = \relax + \else + \errhelp = \EMsimple + \errmessage{Unknown @firstparagraphindent option `\temp'}% + \fi\fi +} + +% Here is how we actually suppress indentation. Redefine \everypar to +% \kern backwards by \parindent, and then reset itself to empty. +% +% We also make \indent itself not actually do anything until the next +% paragraph. +% +\gdef\dosuppressfirstparagraphindent{% + \gdef\indent{% + \restorefirstparagraphindent + \indent + }% + \gdef\noindent{% + \restorefirstparagraphindent + \noindent + }% + \global\everypar = {% + \kern -\parindent + \restorefirstparagraphindent + }% +} + +\gdef\restorefirstparagraphindent{% + \global \let \indent = \ptexindent + \global \let \noindent = \ptexnoindent + \global \everypar = {}% +} + + +% @refill is a no-op. +\let\refill=\relax + +% If working on a large document in chapters, it is convenient to +% be able to disable indexing, cross-referencing, and contents, for test runs. +% This is done with @novalidate (before @setfilename). +% +\newif\iflinks \linkstrue % by default we want the aux files. +\let\novalidate = \linksfalse + +% @setfilename is done at the beginning of every texinfo file. +% So open here the files we need to have open while reading the input. +% This makes it possible to make a .fmt file for texinfo. +\def\setfilename{% + \fixbackslash % Turn off hack to swallow `\input texinfo'. + \iflinks + \tryauxfile + % Open the new aux file. TeX will close it automatically at exit. + \immediate\openout\auxfile=\jobname.aux + \fi % \openindices needs to do some work in any case. + \openindices + \let\setfilename=\comment % Ignore extra @setfilename cmds. + % + % If texinfo.cnf is present on the system, read it. + % Useful for site-wide @afourpaper, etc. + \openin 1 texinfo.cnf + \ifeof 1 \else \input texinfo.cnf \fi + \closein 1 + % + \comment % Ignore the actual filename. +} + +% Called from \setfilename. +% +\def\openindices{% + \newindex{cp}% + \newcodeindex{fn}% + \newcodeindex{vr}% + \newcodeindex{tp}% + \newcodeindex{ky}% + \newcodeindex{pg}% +} + +% @bye. +\outer\def\bye{\pagealignmacro\tracingstats=1\ptexend} + + +\message{pdf,} +% adobe `portable' document format +\newcount\tempnum +\newcount\lnkcount +\newtoks\filename +\newcount\filenamelength +\newcount\pgn +\newtoks\toksA +\newtoks\toksB +\newtoks\toksC +\newtoks\toksD +\newbox\boxA +\newcount\countA +\newif\ifpdf +\newif\ifpdfmakepagedest + +% when pdftex is run in dvi mode, \pdfoutput is defined (so \pdfoutput=1 +% can be set). So we test for \relax and 0 as well as being undefined. +\ifx\pdfoutput\thisisundefined +\else + \ifx\pdfoutput\relax + \else + \ifcase\pdfoutput + \else + \pdftrue + \fi + \fi +\fi + +% PDF uses PostScript string constants for the names of xref targets, +% for display in the outlines, and in other places. Thus, we have to +% double any backslashes. Otherwise, a name like "\node" will be +% interpreted as a newline (\n), followed by o, d, e. Not good. +% +% See http://www.ntg.nl/pipermail/ntg-pdftex/2004-July/000654.html and +% related messages. The final outcome is that it is up to the TeX user +% to double the backslashes and otherwise make the string valid, so +% that's what we do. pdftex 1.30.0 (ca.2005) introduced a primitive to +% do this reliably, so we use it. + +% #1 is a control sequence in which to do the replacements, +% which we \xdef. +\def\txiescapepdf#1{% + \ifx\pdfescapestring\relax + % No primitive available; should we give a warning or log? + % Many times it won't matter. + \else + % The expandable \pdfescapestring primitive escapes parentheses, + % backslashes, and other special chars. + \xdef#1{\pdfescapestring{#1}}% + \fi +} + +\newhelp\nopdfimagehelp{Texinfo supports .png, .jpg, .jpeg, and .pdf images +with PDF output, and none of those formats could be found. (.eps cannot +be supported due to the design of the PDF format; use regular TeX (DVI +output) for that.)} + +\ifpdf + % + % Color manipulation macros based on pdfcolor.tex, + % except using rgb instead of cmyk; the latter is said to render as a + % very dark gray on-screen and a very dark halftone in print, instead + % of actual black. + \def\rgbDarkRed{0.50 0.09 0.12} + \def\rgbBlack{0 0 0} + % + % k sets the color for filling (usual text, etc.); + % K sets the color for stroking (thin rules, e.g., normal _'s). + \def\pdfsetcolor#1{\pdfliteral{#1 rg #1 RG}} + % + % Set color, and create a mark which defines \thiscolor accordingly, + % so that \makeheadline knows which color to restore. + \def\setcolor#1{% + \xdef\lastcolordefs{\gdef\noexpand\thiscolor{#1}}% + \domark + \pdfsetcolor{#1}% + } + % + \def\maincolor{\rgbBlack} + \pdfsetcolor{\maincolor} + \edef\thiscolor{\maincolor} + \def\lastcolordefs{} + % + \def\makefootline{% + \baselineskip24pt + \line{\pdfsetcolor{\maincolor}\the\footline}% + } + % + \def\makeheadline{% + \vbox to 0pt{% + \vskip-22.5pt + \line{% + \vbox to8.5pt{}% + % Extract \thiscolor definition from the marks. + \getcolormarks + % Typeset the headline with \maincolor, then restore the color. + \pdfsetcolor{\maincolor}\the\headline\pdfsetcolor{\thiscolor}% + }% + \vss + }% + \nointerlineskip + } + % + % + \pdfcatalog{/PageMode /UseOutlines} + % + % #1 is image name, #2 width (might be empty/whitespace), #3 height (ditto). + \def\dopdfimage#1#2#3{% + \def\pdfimagewidth{#2}\setbox0 = \hbox{\ignorespaces #2}% + \def\pdfimageheight{#3}\setbox2 = \hbox{\ignorespaces #3}% + % + % pdftex (and the PDF format) support .pdf, .png, .jpg (among + % others). Let's try in that order, PDF first since if + % someone has a scalable image, presumably better to use that than a + % bitmap. + \let\pdfimgext=\empty + \begingroup + \openin 1 #1.pdf \ifeof 1 + \openin 1 #1.PDF \ifeof 1 + \openin 1 #1.png \ifeof 1 + \openin 1 #1.jpg \ifeof 1 + \openin 1 #1.jpeg \ifeof 1 + \openin 1 #1.JPG \ifeof 1 + \errhelp = \nopdfimagehelp + \errmessage{Could not find image file #1 for pdf}% + \else \gdef\pdfimgext{JPG}% + \fi + \else \gdef\pdfimgext{jpeg}% + \fi + \else \gdef\pdfimgext{jpg}% + \fi + \else \gdef\pdfimgext{png}% + \fi + \else \gdef\pdfimgext{PDF}% + \fi + \else \gdef\pdfimgext{pdf}% + \fi + \closein 1 + \endgroup + % + % without \immediate, ancient pdftex seg faults when the same image is + % included twice. (Version 3.14159-pre-1.0-unofficial-20010704.) + \ifnum\pdftexversion < 14 + \immediate\pdfimage + \else + \immediate\pdfximage + \fi + \ifdim \wd0 >0pt width \pdfimagewidth \fi + \ifdim \wd2 >0pt height \pdfimageheight \fi + \ifnum\pdftexversion<13 + #1.\pdfimgext + \else + {#1.\pdfimgext}% + \fi + \ifnum\pdftexversion < 14 \else + \pdfrefximage \pdflastximage + \fi} + % + \def\pdfmkdest#1{{% + % We have to set dummies so commands such as @code, and characters + % such as \, aren't expanded when present in a section title. + \indexnofonts + \turnoffactive + \makevalueexpandable + \def\pdfdestname{#1}% + \txiescapepdf\pdfdestname + \safewhatsit{\pdfdest name{\pdfdestname} xyz}% + }} + % + % used to mark target names; must be expandable. + \def\pdfmkpgn#1{#1} + % + % by default, use a color that is dark enough to print on paper as + % nearly black, but still distinguishable for online viewing. + \def\urlcolor{\rgbDarkRed} + \def\linkcolor{\rgbDarkRed} + \def\endlink{\setcolor{\maincolor}\pdfendlink} + % + % Adding outlines to PDF; macros for calculating structure of outlines + % come from Petr Olsak + \def\expnumber#1{\expandafter\ifx\csname#1\endcsname\relax 0% + \else \csname#1\endcsname \fi} + \def\advancenumber#1{\tempnum=\expnumber{#1}\relax + \advance\tempnum by 1 + \expandafter\xdef\csname#1\endcsname{\the\tempnum}} + % + % #1 is the section text, which is what will be displayed in the + % outline by the pdf viewer. #2 is the pdf expression for the number + % of subentries (or empty, for subsubsections). #3 is the node text, + % which might be empty if this toc entry had no corresponding node. + % #4 is the page number + % + \def\dopdfoutline#1#2#3#4{% + % Generate a link to the node text if that exists; else, use the + % page number. We could generate a destination for the section + % text in the case where a section has no node, but it doesn't + % seem worth the trouble, since most documents are normally structured. + \edef\pdfoutlinedest{#3}% + \ifx\pdfoutlinedest\empty + \def\pdfoutlinedest{#4}% + \else + \txiescapepdf\pdfoutlinedest + \fi + % + % Also escape PDF chars in the display string. + \edef\pdfoutlinetext{#1}% + \txiescapepdf\pdfoutlinetext + % + \pdfoutline goto name{\pdfmkpgn{\pdfoutlinedest}}#2{\pdfoutlinetext}% + } + % + \def\pdfmakeoutlines{% + \begingroup + % Read toc silently, to get counts of subentries for \pdfoutline. + \def\partentry##1##2##3##4{}% ignore parts in the outlines + \def\numchapentry##1##2##3##4{% + \def\thischapnum{##2}% + \def\thissecnum{0}% + \def\thissubsecnum{0}% + }% + \def\numsecentry##1##2##3##4{% + \advancenumber{chap\thischapnum}% + \def\thissecnum{##2}% + \def\thissubsecnum{0}% + }% + \def\numsubsecentry##1##2##3##4{% + \advancenumber{sec\thissecnum}% + \def\thissubsecnum{##2}% + }% + \def\numsubsubsecentry##1##2##3##4{% + \advancenumber{subsec\thissubsecnum}% + }% + \def\thischapnum{0}% + \def\thissecnum{0}% + \def\thissubsecnum{0}% + % + % use \def rather than \let here because we redefine \chapentry et + % al. a second time, below. + \def\appentry{\numchapentry}% + \def\appsecentry{\numsecentry}% + \def\appsubsecentry{\numsubsecentry}% + \def\appsubsubsecentry{\numsubsubsecentry}% + \def\unnchapentry{\numchapentry}% + \def\unnsecentry{\numsecentry}% + \def\unnsubsecentry{\numsubsecentry}% + \def\unnsubsubsecentry{\numsubsubsecentry}% + \readdatafile{toc}% + % + % Read toc second time, this time actually producing the outlines. + % The `-' means take the \expnumber as the absolute number of + % subentries, which we calculated on our first read of the .toc above. + % + % We use the node names as the destinations. + \def\numchapentry##1##2##3##4{% + \dopdfoutline{##1}{count-\expnumber{chap##2}}{##3}{##4}}% + \def\numsecentry##1##2##3##4{% + \dopdfoutline{##1}{count-\expnumber{sec##2}}{##3}{##4}}% + \def\numsubsecentry##1##2##3##4{% + \dopdfoutline{##1}{count-\expnumber{subsec##2}}{##3}{##4}}% + \def\numsubsubsecentry##1##2##3##4{% count is always zero + \dopdfoutline{##1}{}{##3}{##4}}% + % + % PDF outlines are displayed using system fonts, instead of + % document fonts. Therefore we cannot use special characters, + % since the encoding is unknown. For example, the eogonek from + % Latin 2 (0xea) gets translated to a | character. Info from + % Staszek Wawrykiewicz, 19 Jan 2004 04:09:24 +0100. + % + % TODO this right, we have to translate 8-bit characters to + % their "best" equivalent, based on the @documentencoding. Too + % much work for too little return. Just use the ASCII equivalents + % we use for the index sort strings. + % + \indexnofonts + \setupdatafile + % We can have normal brace characters in the PDF outlines, unlike + % Texinfo index files. So set that up. + \def\{{\lbracecharliteral}% + \def\}{\rbracecharliteral}% + \catcode`\\=\active \otherbackslash + \input \tocreadfilename + \endgroup + } + {\catcode`[=1 \catcode`]=2 + \catcode`{=\other \catcode`}=\other + \gdef\lbracecharliteral[{]% + \gdef\rbracecharliteral[}]% + ] + % + \def\skipspaces#1{\def\PP{#1}\def\D{|}% + \ifx\PP\D\let\nextsp\relax + \else\let\nextsp\skipspaces + \ifx\p\space\else\addtokens{\filename}{\PP}% + \advance\filenamelength by 1 + \fi + \fi + \nextsp} + \def\getfilename#1{% + \filenamelength=0 + % If we don't expand the argument now, \skipspaces will get + % snagged on things like "@value{foo}". + \edef\temp{#1}% + \expandafter\skipspaces\temp|\relax + } + \ifnum\pdftexversion < 14 + \let \startlink \pdfannotlink + \else + \let \startlink \pdfstartlink + \fi + % make a live url in pdf output. + \def\pdfurl#1{% + \begingroup + % it seems we really need yet another set of dummies; have not + % tried to figure out what each command should do in the context + % of @url. for now, just make @/ a no-op, that's the only one + % people have actually reported a problem with. + % + \normalturnoffactive + \def\@{@}% + \let\/=\empty + \makevalueexpandable + % do we want to go so far as to use \indexnofonts instead of just + % special-casing \var here? + \def\var##1{##1}% + % + \leavevmode\setcolor{\urlcolor}% + \startlink attr{/Border [0 0 0]}% + user{/Subtype /Link /A << /S /URI /URI (#1) >>}% + \endgroup} + \def\pdfgettoks#1.{\setbox\boxA=\hbox{\toksA={#1.}\toksB={}\maketoks}} + \def\addtokens#1#2{\edef\addtoks{\noexpand#1={\the#1#2}}\addtoks} + \def\adn#1{\addtokens{\toksC}{#1}\global\countA=1\let\next=\maketoks} + \def\poptoks#1#2|ENDTOKS|{\let\first=#1\toksD={#1}\toksA={#2}} + \def\maketoks{% + \expandafter\poptoks\the\toksA|ENDTOKS|\relax + \ifx\first0\adn0 + \else\ifx\first1\adn1 \else\ifx\first2\adn2 \else\ifx\first3\adn3 + \else\ifx\first4\adn4 \else\ifx\first5\adn5 \else\ifx\first6\adn6 + \else\ifx\first7\adn7 \else\ifx\first8\adn8 \else\ifx\first9\adn9 + \else + \ifnum0=\countA\else\makelink\fi + \ifx\first.\let\next=\done\else + \let\next=\maketoks + \addtokens{\toksB}{\the\toksD} + \ifx\first,\addtokens{\toksB}{\space}\fi + \fi + \fi\fi\fi\fi\fi\fi\fi\fi\fi\fi + \next} + \def\makelink{\addtokens{\toksB}% + {\noexpand\pdflink{\the\toksC}}\toksC={}\global\countA=0} + \def\pdflink#1{% + \startlink attr{/Border [0 0 0]} goto name{\pdfmkpgn{#1}} + \setcolor{\linkcolor}#1\endlink} + \def\done{\edef\st{\global\noexpand\toksA={\the\toksB}}\st} +\else + % non-pdf mode + \let\pdfmkdest = \gobble + \let\pdfurl = \gobble + \let\endlink = \relax + \let\setcolor = \gobble + \let\pdfsetcolor = \gobble + \let\pdfmakeoutlines = \relax +\fi % \ifx\pdfoutput + + +\message{fonts,} + +% Change the current font style to #1, remembering it in \curfontstyle. +% For now, we do not accumulate font styles: @b{@i{foo}} prints foo in +% italics, not bold italics. +% +\def\setfontstyle#1{% + \def\curfontstyle{#1}% not as a control sequence, because we are \edef'd. + \csname ten#1\endcsname % change the current font +} + +% Select #1 fonts with the current style. +% +\def\selectfonts#1{\csname #1fonts\endcsname \csname\curfontstyle\endcsname} + +\def\rm{\fam=0 \setfontstyle{rm}} +\def\it{\fam=\itfam \setfontstyle{it}} +\def\sl{\fam=\slfam \setfontstyle{sl}} +\def\bf{\fam=\bffam \setfontstyle{bf}}\def\bfstylename{bf} +\def\tt{\fam=\ttfam \setfontstyle{tt}} + +% Unfortunately, we have to override this for titles and the like, since +% in those cases "rm" is bold. Sigh. +\def\rmisbold{\rm\def\curfontstyle{bf}} + +% Texinfo sort of supports the sans serif font style, which plain TeX does not. +% So we set up a \sf. +\newfam\sffam +\def\sf{\fam=\sffam \setfontstyle{sf}} +\let\li = \sf % Sometimes we call it \li, not \sf. + +% We don't need math for this font style. +\def\ttsl{\setfontstyle{ttsl}} + + +% Default leading. +\newdimen\textleading \textleading = 13.2pt + +% Set the baselineskip to #1, and the lineskip and strut size +% correspondingly. There is no deep meaning behind these magic numbers +% used as factors; they just match (closely enough) what Knuth defined. +% +\def\lineskipfactor{.08333} +\def\strutheightpercent{.70833} +\def\strutdepthpercent {.29167} +% +% can get a sort of poor man's double spacing by redefining this. +\def\baselinefactor{1} +% +\def\setleading#1{% + \dimen0 = #1\relax + \normalbaselineskip = \baselinefactor\dimen0 + \normallineskip = \lineskipfactor\normalbaselineskip + \normalbaselines + \setbox\strutbox =\hbox{% + \vrule width0pt height\strutheightpercent\baselineskip + depth \strutdepthpercent \baselineskip + }% +} + +% PDF CMaps. See also LaTeX's t1.cmap. +% +% do nothing with this by default. +\expandafter\let\csname cmapOT1\endcsname\gobble +\expandafter\let\csname cmapOT1IT\endcsname\gobble +\expandafter\let\csname cmapOT1TT\endcsname\gobble + +% if we are producing pdf, and we have \pdffontattr, then define cmaps. +% (\pdffontattr was introduced many years ago, but people still run +% older pdftex's; it's easy to conditionalize, so we do.) +\ifpdf \ifx\pdffontattr\thisisundefined \else + \begingroup + \catcode`\^^M=\active \def^^M{^^J}% Output line endings as the ^^J char. + \catcode`\%=12 \immediate\pdfobj stream {%!PS-Adobe-3.0 Resource-CMap +%%DocumentNeededResources: ProcSet (CIDInit) +%%IncludeResource: ProcSet (CIDInit) +%%BeginResource: CMap (TeX-OT1-0) +%%Title: (TeX-OT1-0 TeX OT1 0) +%%Version: 1.000 +%%EndComments +/CIDInit /ProcSet findresource begin +12 dict begin +begincmap +/CIDSystemInfo +<< /Registry (TeX) +/Ordering (OT1) +/Supplement 0 +>> def +/CMapName /TeX-OT1-0 def +/CMapType 2 def +1 begincodespacerange +<00> <7F> +endcodespacerange +8 beginbfrange +<00> <01> <0393> +<09> <0A> <03A8> +<23> <26> <0023> +<28> <3B> <0028> +<3F> <5B> <003F> +<5D> <5E> <005D> +<61> <7A> <0061> +<7B> <7C> <2013> +endbfrange +40 beginbfchar +<02> <0398> +<03> <039B> +<04> <039E> +<05> <03A0> +<06> <03A3> +<07> <03D2> +<08> <03A6> +<0B> <00660066> +<0C> <00660069> +<0D> <0066006C> +<0E> <006600660069> +<0F> <00660066006C> +<10> <0131> +<11> <0237> +<12> <0060> +<13> <00B4> +<14> <02C7> +<15> <02D8> +<16> <00AF> +<17> <02DA> +<18> <00B8> +<19> <00DF> +<1A> <00E6> +<1B> <0153> +<1C> <00F8> +<1D> <00C6> +<1E> <0152> +<1F> <00D8> +<21> <0021> +<22> <201D> +<27> <2019> +<3C> <00A1> +<3D> <003D> +<3E> <00BF> +<5C> <201C> +<5F> <02D9> +<60> <2018> +<7D> <02DD> +<7E> <007E> +<7F> <00A8> +endbfchar +endcmap +CMapName currentdict /CMap defineresource pop +end +end +%%EndResource +%%EOF + }\endgroup + \expandafter\edef\csname cmapOT1\endcsname#1{% + \pdffontattr#1{/ToUnicode \the\pdflastobj\space 0 R}% + }% +% +% \cmapOT1IT + \begingroup + \catcode`\^^M=\active \def^^M{^^J}% Output line endings as the ^^J char. + \catcode`\%=12 \immediate\pdfobj stream {%!PS-Adobe-3.0 Resource-CMap +%%DocumentNeededResources: ProcSet (CIDInit) +%%IncludeResource: ProcSet (CIDInit) +%%BeginResource: CMap (TeX-OT1IT-0) +%%Title: (TeX-OT1IT-0 TeX OT1IT 0) +%%Version: 1.000 +%%EndComments +/CIDInit /ProcSet findresource begin +12 dict begin +begincmap +/CIDSystemInfo +<< /Registry (TeX) +/Ordering (OT1IT) +/Supplement 0 +>> def +/CMapName /TeX-OT1IT-0 def +/CMapType 2 def +1 begincodespacerange +<00> <7F> +endcodespacerange +8 beginbfrange +<00> <01> <0393> +<09> <0A> <03A8> +<25> <26> <0025> +<28> <3B> <0028> +<3F> <5B> <003F> +<5D> <5E> <005D> +<61> <7A> <0061> +<7B> <7C> <2013> +endbfrange +42 beginbfchar +<02> <0398> +<03> <039B> +<04> <039E> +<05> <03A0> +<06> <03A3> +<07> <03D2> +<08> <03A6> +<0B> <00660066> +<0C> <00660069> +<0D> <0066006C> +<0E> <006600660069> +<0F> <00660066006C> +<10> <0131> +<11> <0237> +<12> <0060> +<13> <00B4> +<14> <02C7> +<15> <02D8> +<16> <00AF> +<17> <02DA> +<18> <00B8> +<19> <00DF> +<1A> <00E6> +<1B> <0153> +<1C> <00F8> +<1D> <00C6> +<1E> <0152> +<1F> <00D8> +<21> <0021> +<22> <201D> +<23> <0023> +<24> <00A3> +<27> <2019> +<3C> <00A1> +<3D> <003D> +<3E> <00BF> +<5C> <201C> +<5F> <02D9> +<60> <2018> +<7D> <02DD> +<7E> <007E> +<7F> <00A8> +endbfchar +endcmap +CMapName currentdict /CMap defineresource pop +end +end +%%EndResource +%%EOF + }\endgroup + \expandafter\edef\csname cmapOT1IT\endcsname#1{% + \pdffontattr#1{/ToUnicode \the\pdflastobj\space 0 R}% + }% +% +% \cmapOT1TT + \begingroup + \catcode`\^^M=\active \def^^M{^^J}% Output line endings as the ^^J char. + \catcode`\%=12 \immediate\pdfobj stream {%!PS-Adobe-3.0 Resource-CMap +%%DocumentNeededResources: ProcSet (CIDInit) +%%IncludeResource: ProcSet (CIDInit) +%%BeginResource: CMap (TeX-OT1TT-0) +%%Title: (TeX-OT1TT-0 TeX OT1TT 0) +%%Version: 1.000 +%%EndComments +/CIDInit /ProcSet findresource begin +12 dict begin +begincmap +/CIDSystemInfo +<< /Registry (TeX) +/Ordering (OT1TT) +/Supplement 0 +>> def +/CMapName /TeX-OT1TT-0 def +/CMapType 2 def +1 begincodespacerange +<00> <7F> +endcodespacerange +5 beginbfrange +<00> <01> <0393> +<09> <0A> <03A8> +<21> <26> <0021> +<28> <5F> <0028> +<61> <7E> <0061> +endbfrange +32 beginbfchar +<02> <0398> +<03> <039B> +<04> <039E> +<05> <03A0> +<06> <03A3> +<07> <03D2> +<08> <03A6> +<0B> <2191> +<0C> <2193> +<0D> <0027> +<0E> <00A1> +<0F> <00BF> +<10> <0131> +<11> <0237> +<12> <0060> +<13> <00B4> +<14> <02C7> +<15> <02D8> +<16> <00AF> +<17> <02DA> +<18> <00B8> +<19> <00DF> +<1A> <00E6> +<1B> <0153> +<1C> <00F8> +<1D> <00C6> +<1E> <0152> +<1F> <00D8> +<20> <2423> +<27> <2019> +<60> <2018> +<7F> <00A8> +endbfchar +endcmap +CMapName currentdict /CMap defineresource pop +end +end +%%EndResource +%%EOF + }\endgroup + \expandafter\edef\csname cmapOT1TT\endcsname#1{% + \pdffontattr#1{/ToUnicode \the\pdflastobj\space 0 R}% + }% +\fi\fi + + +% Set the font macro #1 to the font named #2, adding on the +% specified font prefix (normally `cm'). +% #3 is the font's design size, #4 is a scale factor, #5 is the CMap +% encoding (currently only OT1, OT1IT and OT1TT are allowed, pass +% empty to omit). +\def\setfont#1#2#3#4#5{% + \font#1=\fontprefix#2#3 scaled #4 + \csname cmap#5\endcsname#1% +} +% This is what gets called when #5 of \setfont is empty. +\let\cmap\gobble +% emacs-page end of cmaps + +% Use cm as the default font prefix. +% To specify the font prefix, you must define \fontprefix +% before you read in texinfo.tex. +\ifx\fontprefix\thisisundefined +\def\fontprefix{cm} +\fi +% Support font families that don't use the same naming scheme as CM. +\def\rmshape{r} +\def\rmbshape{bx} %where the normal face is bold +\def\bfshape{b} +\def\bxshape{bx} +\def\ttshape{tt} +\def\ttbshape{tt} +\def\ttslshape{sltt} +\def\itshape{ti} +\def\itbshape{bxti} +\def\slshape{sl} +\def\slbshape{bxsl} +\def\sfshape{ss} +\def\sfbshape{ss} +\def\scshape{csc} +\def\scbshape{csc} + +% Definitions for a main text size of 11pt. This is the default in +% Texinfo. +% +\def\definetextfontsizexi{% +% Text fonts (11.2pt, magstep1). +\def\textnominalsize{11pt} +\edef\mainmagstep{\magstephalf} +\setfont\textrm\rmshape{10}{\mainmagstep}{OT1} +\setfont\texttt\ttshape{10}{\mainmagstep}{OT1TT} +\setfont\textbf\bfshape{10}{\mainmagstep}{OT1} +\setfont\textit\itshape{10}{\mainmagstep}{OT1IT} +\setfont\textsl\slshape{10}{\mainmagstep}{OT1} +\setfont\textsf\sfshape{10}{\mainmagstep}{OT1} +\setfont\textsc\scshape{10}{\mainmagstep}{OT1} +\setfont\textttsl\ttslshape{10}{\mainmagstep}{OT1TT} +\font\texti=cmmi10 scaled \mainmagstep +\font\textsy=cmsy10 scaled \mainmagstep +\def\textecsize{1095} + +% A few fonts for @defun names and args. +\setfont\defbf\bfshape{10}{\magstep1}{OT1} +\setfont\deftt\ttshape{10}{\magstep1}{OT1TT} +\setfont\defttsl\ttslshape{10}{\magstep1}{OT1TT} +\def\df{\let\tentt=\deftt \let\tenbf = \defbf \let\tenttsl=\defttsl \bf} + +% Fonts for indices, footnotes, small examples (9pt). +\def\smallnominalsize{9pt} +\setfont\smallrm\rmshape{9}{1000}{OT1} +\setfont\smalltt\ttshape{9}{1000}{OT1TT} +\setfont\smallbf\bfshape{10}{900}{OT1} +\setfont\smallit\itshape{9}{1000}{OT1IT} +\setfont\smallsl\slshape{9}{1000}{OT1} +\setfont\smallsf\sfshape{9}{1000}{OT1} +\setfont\smallsc\scshape{10}{900}{OT1} +\setfont\smallttsl\ttslshape{10}{900}{OT1TT} +\font\smalli=cmmi9 +\font\smallsy=cmsy9 +\def\smallecsize{0900} + +% Fonts for small examples (8pt). +\def\smallernominalsize{8pt} +\setfont\smallerrm\rmshape{8}{1000}{OT1} +\setfont\smallertt\ttshape{8}{1000}{OT1TT} +\setfont\smallerbf\bfshape{10}{800}{OT1} +\setfont\smallerit\itshape{8}{1000}{OT1IT} +\setfont\smallersl\slshape{8}{1000}{OT1} +\setfont\smallersf\sfshape{8}{1000}{OT1} +\setfont\smallersc\scshape{10}{800}{OT1} +\setfont\smallerttsl\ttslshape{10}{800}{OT1TT} +\font\smalleri=cmmi8 +\font\smallersy=cmsy8 +\def\smallerecsize{0800} + +% Fonts for title page (20.4pt): +\def\titlenominalsize{20pt} +\setfont\titlerm\rmbshape{12}{\magstep3}{OT1} +\setfont\titleit\itbshape{10}{\magstep4}{OT1IT} +\setfont\titlesl\slbshape{10}{\magstep4}{OT1} +\setfont\titlett\ttbshape{12}{\magstep3}{OT1TT} +\setfont\titlettsl\ttslshape{10}{\magstep4}{OT1TT} +\setfont\titlesf\sfbshape{17}{\magstep1}{OT1} +\let\titlebf=\titlerm +\setfont\titlesc\scbshape{10}{\magstep4}{OT1} +\font\titlei=cmmi12 scaled \magstep3 +\font\titlesy=cmsy10 scaled \magstep4 +\def\titleecsize{2074} + +% Chapter (and unnumbered) fonts (17.28pt). +\def\chapnominalsize{17pt} +\setfont\chaprm\rmbshape{12}{\magstep2}{OT1} +\setfont\chapit\itbshape{10}{\magstep3}{OT1IT} +\setfont\chapsl\slbshape{10}{\magstep3}{OT1} +\setfont\chaptt\ttbshape{12}{\magstep2}{OT1TT} +\setfont\chapttsl\ttslshape{10}{\magstep3}{OT1TT} +\setfont\chapsf\sfbshape{17}{1000}{OT1} +\let\chapbf=\chaprm +\setfont\chapsc\scbshape{10}{\magstep3}{OT1} +\font\chapi=cmmi12 scaled \magstep2 +\font\chapsy=cmsy10 scaled \magstep3 +\def\chapecsize{1728} + +% Section fonts (14.4pt). +\def\secnominalsize{14pt} +\setfont\secrm\rmbshape{12}{\magstep1}{OT1} +\setfont\secit\itbshape{10}{\magstep2}{OT1IT} +\setfont\secsl\slbshape{10}{\magstep2}{OT1} +\setfont\sectt\ttbshape{12}{\magstep1}{OT1TT} +\setfont\secttsl\ttslshape{10}{\magstep2}{OT1TT} +\setfont\secsf\sfbshape{12}{\magstep1}{OT1} +\let\secbf\secrm +\setfont\secsc\scbshape{10}{\magstep2}{OT1} +\font\seci=cmmi12 scaled \magstep1 +\font\secsy=cmsy10 scaled \magstep2 +\def\sececsize{1440} + +% Subsection fonts (13.15pt). +\def\ssecnominalsize{13pt} +\setfont\ssecrm\rmbshape{12}{\magstephalf}{OT1} +\setfont\ssecit\itbshape{10}{1315}{OT1IT} +\setfont\ssecsl\slbshape{10}{1315}{OT1} +\setfont\ssectt\ttbshape{12}{\magstephalf}{OT1TT} +\setfont\ssecttsl\ttslshape{10}{1315}{OT1TT} +\setfont\ssecsf\sfbshape{12}{\magstephalf}{OT1} +\let\ssecbf\ssecrm +\setfont\ssecsc\scbshape{10}{1315}{OT1} +\font\sseci=cmmi12 scaled \magstephalf +\font\ssecsy=cmsy10 scaled 1315 +\def\ssececsize{1200} + +% Reduced fonts for @acro in text (10pt). +\def\reducednominalsize{10pt} +\setfont\reducedrm\rmshape{10}{1000}{OT1} +\setfont\reducedtt\ttshape{10}{1000}{OT1TT} +\setfont\reducedbf\bfshape{10}{1000}{OT1} +\setfont\reducedit\itshape{10}{1000}{OT1IT} +\setfont\reducedsl\slshape{10}{1000}{OT1} +\setfont\reducedsf\sfshape{10}{1000}{OT1} +\setfont\reducedsc\scshape{10}{1000}{OT1} +\setfont\reducedttsl\ttslshape{10}{1000}{OT1TT} +\font\reducedi=cmmi10 +\font\reducedsy=cmsy10 +\def\reducedecsize{1000} + +\textleading = 13.2pt % line spacing for 11pt CM +\textfonts % reset the current fonts +\rm +} % end of 11pt text font size definitions + + +% Definitions to make the main text be 10pt Computer Modern, with +% section, chapter, etc., sizes following suit. This is for the GNU +% Press printing of the Emacs 22 manual. Maybe other manuals in the +% future. Used with @smallbook, which sets the leading to 12pt. +% +\def\definetextfontsizex{% +% Text fonts (10pt). +\def\textnominalsize{10pt} +\edef\mainmagstep{1000} +\setfont\textrm\rmshape{10}{\mainmagstep}{OT1} +\setfont\texttt\ttshape{10}{\mainmagstep}{OT1TT} +\setfont\textbf\bfshape{10}{\mainmagstep}{OT1} +\setfont\textit\itshape{10}{\mainmagstep}{OT1IT} +\setfont\textsl\slshape{10}{\mainmagstep}{OT1} +\setfont\textsf\sfshape{10}{\mainmagstep}{OT1} +\setfont\textsc\scshape{10}{\mainmagstep}{OT1} +\setfont\textttsl\ttslshape{10}{\mainmagstep}{OT1TT} +\font\texti=cmmi10 scaled \mainmagstep +\font\textsy=cmsy10 scaled \mainmagstep +\def\textecsize{1000} + +% A few fonts for @defun names and args. +\setfont\defbf\bfshape{10}{\magstephalf}{OT1} +\setfont\deftt\ttshape{10}{\magstephalf}{OT1TT} +\setfont\defttsl\ttslshape{10}{\magstephalf}{OT1TT} +\def\df{\let\tentt=\deftt \let\tenbf = \defbf \let\tenttsl=\defttsl \bf} + +% Fonts for indices, footnotes, small examples (9pt). +\def\smallnominalsize{9pt} +\setfont\smallrm\rmshape{9}{1000}{OT1} +\setfont\smalltt\ttshape{9}{1000}{OT1TT} +\setfont\smallbf\bfshape{10}{900}{OT1} +\setfont\smallit\itshape{9}{1000}{OT1IT} +\setfont\smallsl\slshape{9}{1000}{OT1} +\setfont\smallsf\sfshape{9}{1000}{OT1} +\setfont\smallsc\scshape{10}{900}{OT1} +\setfont\smallttsl\ttslshape{10}{900}{OT1TT} +\font\smalli=cmmi9 +\font\smallsy=cmsy9 +\def\smallecsize{0900} + +% Fonts for small examples (8pt). +\def\smallernominalsize{8pt} +\setfont\smallerrm\rmshape{8}{1000}{OT1} +\setfont\smallertt\ttshape{8}{1000}{OT1TT} +\setfont\smallerbf\bfshape{10}{800}{OT1} +\setfont\smallerit\itshape{8}{1000}{OT1IT} +\setfont\smallersl\slshape{8}{1000}{OT1} +\setfont\smallersf\sfshape{8}{1000}{OT1} +\setfont\smallersc\scshape{10}{800}{OT1} +\setfont\smallerttsl\ttslshape{10}{800}{OT1TT} +\font\smalleri=cmmi8 +\font\smallersy=cmsy8 +\def\smallerecsize{0800} + +% Fonts for title page (20.4pt): +\def\titlenominalsize{20pt} +\setfont\titlerm\rmbshape{12}{\magstep3}{OT1} +\setfont\titleit\itbshape{10}{\magstep4}{OT1IT} +\setfont\titlesl\slbshape{10}{\magstep4}{OT1} +\setfont\titlett\ttbshape{12}{\magstep3}{OT1TT} +\setfont\titlettsl\ttslshape{10}{\magstep4}{OT1TT} +\setfont\titlesf\sfbshape{17}{\magstep1}{OT1} +\let\titlebf=\titlerm +\setfont\titlesc\scbshape{10}{\magstep4}{OT1} +\font\titlei=cmmi12 scaled \magstep3 +\font\titlesy=cmsy10 scaled \magstep4 +\def\titleecsize{2074} + +% Chapter fonts (14.4pt). +\def\chapnominalsize{14pt} +\setfont\chaprm\rmbshape{12}{\magstep1}{OT1} +\setfont\chapit\itbshape{10}{\magstep2}{OT1IT} +\setfont\chapsl\slbshape{10}{\magstep2}{OT1} +\setfont\chaptt\ttbshape{12}{\magstep1}{OT1TT} +\setfont\chapttsl\ttslshape{10}{\magstep2}{OT1TT} +\setfont\chapsf\sfbshape{12}{\magstep1}{OT1} +\let\chapbf\chaprm +\setfont\chapsc\scbshape{10}{\magstep2}{OT1} +\font\chapi=cmmi12 scaled \magstep1 +\font\chapsy=cmsy10 scaled \magstep2 +\def\chapecsize{1440} + +% Section fonts (12pt). +\def\secnominalsize{12pt} +\setfont\secrm\rmbshape{12}{1000}{OT1} +\setfont\secit\itbshape{10}{\magstep1}{OT1IT} +\setfont\secsl\slbshape{10}{\magstep1}{OT1} +\setfont\sectt\ttbshape{12}{1000}{OT1TT} +\setfont\secttsl\ttslshape{10}{\magstep1}{OT1TT} +\setfont\secsf\sfbshape{12}{1000}{OT1} +\let\secbf\secrm +\setfont\secsc\scbshape{10}{\magstep1}{OT1} +\font\seci=cmmi12 +\font\secsy=cmsy10 scaled \magstep1 +\def\sececsize{1200} + +% Subsection fonts (10pt). +\def\ssecnominalsize{10pt} +\setfont\ssecrm\rmbshape{10}{1000}{OT1} +\setfont\ssecit\itbshape{10}{1000}{OT1IT} +\setfont\ssecsl\slbshape{10}{1000}{OT1} +\setfont\ssectt\ttbshape{10}{1000}{OT1TT} +\setfont\ssecttsl\ttslshape{10}{1000}{OT1TT} +\setfont\ssecsf\sfbshape{10}{1000}{OT1} +\let\ssecbf\ssecrm +\setfont\ssecsc\scbshape{10}{1000}{OT1} +\font\sseci=cmmi10 +\font\ssecsy=cmsy10 +\def\ssececsize{1000} + +% Reduced fonts for @acro in text (9pt). +\def\reducednominalsize{9pt} +\setfont\reducedrm\rmshape{9}{1000}{OT1} +\setfont\reducedtt\ttshape{9}{1000}{OT1TT} +\setfont\reducedbf\bfshape{10}{900}{OT1} +\setfont\reducedit\itshape{9}{1000}{OT1IT} +\setfont\reducedsl\slshape{9}{1000}{OT1} +\setfont\reducedsf\sfshape{9}{1000}{OT1} +\setfont\reducedsc\scshape{10}{900}{OT1} +\setfont\reducedttsl\ttslshape{10}{900}{OT1TT} +\font\reducedi=cmmi9 +\font\reducedsy=cmsy9 +\def\reducedecsize{0900} + +\divide\parskip by 2 % reduce space between paragraphs +\textleading = 12pt % line spacing for 10pt CM +\textfonts % reset the current fonts +\rm +} % end of 10pt text font size definitions + + +% We provide the user-level command +% @fonttextsize 10 +% (or 11) to redefine the text font size. pt is assumed. +% +\def\xiword{11} +\def\xword{10} +\def\xwordpt{10pt} +% +\parseargdef\fonttextsize{% + \def\textsizearg{#1}% + %\wlog{doing @fonttextsize \textsizearg}% + % + % Set \globaldefs so that documents can use this inside @tex, since + % makeinfo 4.8 does not support it, but we need it nonetheless. + % + \begingroup \globaldefs=1 + \ifx\textsizearg\xword \definetextfontsizex + \else \ifx\textsizearg\xiword \definetextfontsizexi + \else + \errhelp=\EMsimple + \errmessage{@fonttextsize only supports `10' or `11', not `\textsizearg'} + \fi\fi + \endgroup +} + + +% In order for the font changes to affect most math symbols and letters, +% we have to define the \textfont of the standard families. Since +% texinfo doesn't allow for producing subscripts and superscripts except +% in the main text, we don't bother to reset \scriptfont and +% \scriptscriptfont (which would also require loading a lot more fonts). +% +\def\resetmathfonts{% + \textfont0=\tenrm \textfont1=\teni \textfont2=\tensy + \textfont\itfam=\tenit \textfont\slfam=\tensl \textfont\bffam=\tenbf + \textfont\ttfam=\tentt \textfont\sffam=\tensf +} + +% The font-changing commands redefine the meanings of \tenSTYLE, instead +% of just \STYLE. We do this because \STYLE needs to also set the +% current \fam for math mode. Our \STYLE (e.g., \rm) commands hardwire +% \tenSTYLE to set the current font. +% +% Each font-changing command also sets the names \lsize (one size lower) +% and \lllsize (three sizes lower). These relative commands are used in +% the LaTeX logo and acronyms. +% +% This all needs generalizing, badly. +% +\def\textfonts{% + \let\tenrm=\textrm \let\tenit=\textit \let\tensl=\textsl + \let\tenbf=\textbf \let\tentt=\texttt \let\smallcaps=\textsc + \let\tensf=\textsf \let\teni=\texti \let\tensy=\textsy + \let\tenttsl=\textttsl + \def\curfontsize{text}% + \def\lsize{reduced}\def\lllsize{smaller}% + \resetmathfonts \setleading{\textleading}} +\def\titlefonts{% + \let\tenrm=\titlerm \let\tenit=\titleit \let\tensl=\titlesl + \let\tenbf=\titlebf \let\tentt=\titlett \let\smallcaps=\titlesc + \let\tensf=\titlesf \let\teni=\titlei \let\tensy=\titlesy + \let\tenttsl=\titlettsl + \def\curfontsize{title}% + \def\lsize{chap}\def\lllsize{subsec}% + \resetmathfonts \setleading{27pt}} +\def\titlefont#1{{\titlefonts\rmisbold #1}} +\def\chapfonts{% + \let\tenrm=\chaprm \let\tenit=\chapit \let\tensl=\chapsl + \let\tenbf=\chapbf \let\tentt=\chaptt \let\smallcaps=\chapsc + \let\tensf=\chapsf \let\teni=\chapi \let\tensy=\chapsy + \let\tenttsl=\chapttsl + \def\curfontsize{chap}% + \def\lsize{sec}\def\lllsize{text}% + \resetmathfonts \setleading{19pt}} +\def\secfonts{% + \let\tenrm=\secrm \let\tenit=\secit \let\tensl=\secsl + \let\tenbf=\secbf \let\tentt=\sectt \let\smallcaps=\secsc + \let\tensf=\secsf \let\teni=\seci \let\tensy=\secsy + \let\tenttsl=\secttsl + \def\curfontsize{sec}% + \def\lsize{subsec}\def\lllsize{reduced}% + \resetmathfonts \setleading{16pt}} +\def\subsecfonts{% + \let\tenrm=\ssecrm \let\tenit=\ssecit \let\tensl=\ssecsl + \let\tenbf=\ssecbf \let\tentt=\ssectt \let\smallcaps=\ssecsc + \let\tensf=\ssecsf \let\teni=\sseci \let\tensy=\ssecsy + \let\tenttsl=\ssecttsl + \def\curfontsize{ssec}% + \def\lsize{text}\def\lllsize{small}% + \resetmathfonts \setleading{15pt}} +\let\subsubsecfonts = \subsecfonts +\def\reducedfonts{% + \let\tenrm=\reducedrm \let\tenit=\reducedit \let\tensl=\reducedsl + \let\tenbf=\reducedbf \let\tentt=\reducedtt \let\reducedcaps=\reducedsc + \let\tensf=\reducedsf \let\teni=\reducedi \let\tensy=\reducedsy + \let\tenttsl=\reducedttsl + \def\curfontsize{reduced}% + \def\lsize{small}\def\lllsize{smaller}% + \resetmathfonts \setleading{10.5pt}} +\def\smallfonts{% + \let\tenrm=\smallrm \let\tenit=\smallit \let\tensl=\smallsl + \let\tenbf=\smallbf \let\tentt=\smalltt \let\smallcaps=\smallsc + \let\tensf=\smallsf \let\teni=\smalli \let\tensy=\smallsy + \let\tenttsl=\smallttsl + \def\curfontsize{small}% + \def\lsize{smaller}\def\lllsize{smaller}% + \resetmathfonts \setleading{10.5pt}} +\def\smallerfonts{% + \let\tenrm=\smallerrm \let\tenit=\smallerit \let\tensl=\smallersl + \let\tenbf=\smallerbf \let\tentt=\smallertt \let\smallcaps=\smallersc + \let\tensf=\smallersf \let\teni=\smalleri \let\tensy=\smallersy + \let\tenttsl=\smallerttsl + \def\curfontsize{smaller}% + \def\lsize{smaller}\def\lllsize{smaller}% + \resetmathfonts \setleading{9.5pt}} + +% Fonts for short table of contents. +\setfont\shortcontrm\rmshape{12}{1000}{OT1} +\setfont\shortcontbf\bfshape{10}{\magstep1}{OT1} % no cmb12 +\setfont\shortcontsl\slshape{12}{1000}{OT1} +\setfont\shortconttt\ttshape{12}{1000}{OT1TT} + +% Define these just so they can be easily changed for other fonts. +\def\angleleft{$\langle$} +\def\angleright{$\rangle$} + +% Set the fonts to use with the @small... environments. +\let\smallexamplefonts = \smallfonts + +% About \smallexamplefonts. If we use \smallfonts (9pt), @smallexample +% can fit this many characters: +% 8.5x11=86 smallbook=72 a4=90 a5=69 +% If we use \scriptfonts (8pt), then we can fit this many characters: +% 8.5x11=90+ smallbook=80 a4=90+ a5=77 +% For me, subjectively, the few extra characters that fit aren't worth +% the additional smallness of 8pt. So I'm making the default 9pt. +% +% By the way, for comparison, here's what fits with @example (10pt): +% 8.5x11=71 smallbook=60 a4=75 a5=58 +% --karl, 24jan03. + +% Set up the default fonts, so we can use them for creating boxes. +% +\definetextfontsizexi + + +\message{markup,} + +% Check if we are currently using a typewriter font. Since all the +% Computer Modern typewriter fonts have zero interword stretch (and +% shrink), and it is reasonable to expect all typewriter fonts to have +% this property, we can check that font parameter. +% +\def\ifmonospace{\ifdim\fontdimen3\font=0pt } + +% Markup style infrastructure. \defmarkupstylesetup\INITMACRO will +% define and register \INITMACRO to be called on markup style changes. +% \INITMACRO can check \currentmarkupstyle for the innermost +% style and the set of \ifmarkupSTYLE switches for all styles +% currently in effect. +\newif\ifmarkupvar +\newif\ifmarkupsamp +\newif\ifmarkupkey +%\newif\ifmarkupfile % @file == @samp. +%\newif\ifmarkupoption % @option == @samp. +\newif\ifmarkupcode +\newif\ifmarkupkbd +%\newif\ifmarkupenv % @env == @code. +%\newif\ifmarkupcommand % @command == @code. +\newif\ifmarkuptex % @tex (and part of @math, for now). +\newif\ifmarkupexample +\newif\ifmarkupverb +\newif\ifmarkupverbatim + +\let\currentmarkupstyle\empty + +\def\setupmarkupstyle#1{% + \csname markup#1true\endcsname + \def\currentmarkupstyle{#1}% + \markupstylesetup +} + +\let\markupstylesetup\empty + +\def\defmarkupstylesetup#1{% + \expandafter\def\expandafter\markupstylesetup + \expandafter{\markupstylesetup #1}% + \def#1% +} + +% Markup style setup for left and right quotes. +\defmarkupstylesetup\markupsetuplq{% + \expandafter\let\expandafter \temp + \csname markupsetuplq\currentmarkupstyle\endcsname + \ifx\temp\relax \markupsetuplqdefault \else \temp \fi +} + +\defmarkupstylesetup\markupsetuprq{% + \expandafter\let\expandafter \temp + \csname markupsetuprq\currentmarkupstyle\endcsname + \ifx\temp\relax \markupsetuprqdefault \else \temp \fi +} + +{ +\catcode`\'=\active +\catcode`\`=\active + +\gdef\markupsetuplqdefault{\let`\lq} +\gdef\markupsetuprqdefault{\let'\rq} + +\gdef\markupsetcodequoteleft{\let`\codequoteleft} +\gdef\markupsetcodequoteright{\let'\codequoteright} + +\gdef\markupsetnoligaturesquoteleft{\let`\noligaturesquoteleft} +} + +\let\markupsetuplqcode \markupsetcodequoteleft +\let\markupsetuprqcode \markupsetcodequoteright +% +\let\markupsetuplqexample \markupsetcodequoteleft +\let\markupsetuprqexample \markupsetcodequoteright +% +\let\markupsetuplqsamp \markupsetcodequoteleft +\let\markupsetuprqsamp \markupsetcodequoteright +% +\let\markupsetuplqverb \markupsetcodequoteleft +\let\markupsetuprqverb \markupsetcodequoteright +% +\let\markupsetuplqverbatim \markupsetcodequoteleft +\let\markupsetuprqverbatim \markupsetcodequoteright + +\let\markupsetuplqkbd \markupsetnoligaturesquoteleft + +% Allow an option to not use regular directed right quote/apostrophe +% (char 0x27), but instead the undirected quote from cmtt (char 0x0d). +% The undirected quote is ugly, so don't make it the default, but it +% works for pasting with more pdf viewers (at least evince), the +% lilypond developers report. xpdf does work with the regular 0x27. +% +\def\codequoteright{% + \expandafter\ifx\csname SETtxicodequoteundirected\endcsname\relax + \expandafter\ifx\csname SETcodequoteundirected\endcsname\relax + '% + \else \char'15 \fi + \else \char'15 \fi +} +% +% and a similar option for the left quote char vs. a grave accent. +% Modern fonts display ASCII 0x60 as a grave accent, so some people like +% the code environments to do likewise. +% +\def\codequoteleft{% + \expandafter\ifx\csname SETtxicodequotebacktick\endcsname\relax + \expandafter\ifx\csname SETcodequotebacktick\endcsname\relax + % [Knuth] pp. 380,381,391 + % \relax disables Spanish ligatures ?` and !` of \tt font. + \relax`% + \else \char'22 \fi + \else \char'22 \fi +} + +% Commands to set the quote options. +% +\parseargdef\codequoteundirected{% + \def\temp{#1}% + \ifx\temp\onword + \expandafter\let\csname SETtxicodequoteundirected\endcsname + = t% + \else\ifx\temp\offword + \expandafter\let\csname SETtxicodequoteundirected\endcsname + = \relax + \else + \errhelp = \EMsimple + \errmessage{Unknown @codequoteundirected value `\temp', must be on|off}% + \fi\fi +} +% +\parseargdef\codequotebacktick{% + \def\temp{#1}% + \ifx\temp\onword + \expandafter\let\csname SETtxicodequotebacktick\endcsname + = t% + \else\ifx\temp\offword + \expandafter\let\csname SETtxicodequotebacktick\endcsname + = \relax + \else + \errhelp = \EMsimple + \errmessage{Unknown @codequotebacktick value `\temp', must be on|off}% + \fi\fi +} + +% [Knuth] pp. 380,381,391, disable Spanish ligatures ?` and !` of \tt font. +\def\noligaturesquoteleft{\relax\lq} + +% Count depth in font-changes, for error checks +\newcount\fontdepth \fontdepth=0 + +% Font commands. + +% #1 is the font command (\sl or \it), #2 is the text to slant. +% If we are in a monospaced environment, however, 1) always use \ttsl, +% and 2) do not add an italic correction. +\def\dosmartslant#1#2{% + \ifusingtt + {{\ttsl #2}\let\next=\relax}% + {\def\next{{#1#2}\futurelet\next\smartitaliccorrection}}% + \next +} +\def\smartslanted{\dosmartslant\sl} +\def\smartitalic{\dosmartslant\it} + +% Output an italic correction unless \next (presumed to be the following +% character) is such as not to need one. +\def\smartitaliccorrection{% + \ifx\next,% + \else\ifx\next-% + \else\ifx\next.% + \else\ptexslash + \fi\fi\fi + \aftersmartic +} + +% like \smartslanted except unconditionally uses \ttsl, and no ic. +% @var is set to this for defun arguments. +\def\ttslanted#1{{\ttsl #1}} + +% @cite is like \smartslanted except unconditionally use \sl. We never want +% ttsl for book titles, do we? +\def\cite#1{{\sl #1}\futurelet\next\smartitaliccorrection} + +\def\aftersmartic{} +\def\var#1{% + \let\saveaftersmartic = \aftersmartic + \def\aftersmartic{\null\let\aftersmartic=\saveaftersmartic}% + \smartslanted{#1}% +} + +\let\i=\smartitalic +\let\slanted=\smartslanted +\let\dfn=\smartslanted +\let\emph=\smartitalic + +% Explicit font changes: @r, @sc, undocumented @ii. +\def\r#1{{\rm #1}} % roman font +\def\sc#1{{\smallcaps#1}} % smallcaps font +\def\ii#1{{\it #1}} % italic font + +% @b, explicit bold. Also @strong. +\def\b#1{{\bf #1}} +\let\strong=\b + +% @sansserif, explicit sans. +\def\sansserif#1{{\sf #1}} + +% We can't just use \exhyphenpenalty, because that only has effect at +% the end of a paragraph. Restore normal hyphenation at the end of the +% group within which \nohyphenation is presumably called. +% +\def\nohyphenation{\hyphenchar\font = -1 \aftergroup\restorehyphenation} +\def\restorehyphenation{\hyphenchar\font = `- } + +% Set sfcode to normal for the chars that usually have another value. +% Can't use plain's \frenchspacing because it uses the `\x notation, and +% sometimes \x has an active definition that messes things up. +% +\catcode`@=11 + \def\plainfrenchspacing{% + \sfcode\dotChar =\@m \sfcode\questChar=\@m \sfcode\exclamChar=\@m + \sfcode\colonChar=\@m \sfcode\semiChar =\@m \sfcode\commaChar =\@m + \def\endofsentencespacefactor{1000}% for @. and friends + } + \def\plainnonfrenchspacing{% + \sfcode`\.3000\sfcode`\?3000\sfcode`\!3000 + \sfcode`\:2000\sfcode`\;1500\sfcode`\,1250 + \def\endofsentencespacefactor{3000}% for @. and friends + } +\catcode`@=\other +\def\endofsentencespacefactor{3000}% default + +% @t, explicit typewriter. +\def\t#1{% + {\tt \rawbackslash \plainfrenchspacing #1}% + \null +} + +% @samp. +\def\samp#1{{\setupmarkupstyle{samp}\lq\tclose{#1}\rq\null}} + +% definition of @key that produces a lozenge. Doesn't adjust to text size. +%\setfont\keyrm\rmshape{8}{1000}{OT1} +%\font\keysy=cmsy9 +%\def\key#1{{\keyrm\textfont2=\keysy \leavevmode\hbox{% +% \raise0.4pt\hbox{\angleleft}\kern-.08em\vtop{% +% \vbox{\hrule\kern-0.4pt +% \hbox{\raise0.4pt\hbox{\vphantom{\angleleft}}#1}}% +% \kern-0.4pt\hrule}% +% \kern-.06em\raise0.4pt\hbox{\angleright}}}} + +% definition of @key with no lozenge. If the current font is already +% monospace, don't change it; that way, we respect @kbdinputstyle. But +% if it isn't monospace, then use \tt. +% +\def\key#1{{\setupmarkupstyle{key}% + \nohyphenation + \ifmonospace\else\tt\fi + #1}\null} + +% ctrl is no longer a Texinfo command. +\def\ctrl #1{{\tt \rawbackslash \hat}#1} + +% @file, @option are the same as @samp. +\let\file=\samp +\let\option=\samp + +% @code is a modification of @t, +% which makes spaces the same size as normal in the surrounding text. +\def\tclose#1{% + {% + % Change normal interword space to be same as for the current font. + \spaceskip = \fontdimen2\font + % + % Switch to typewriter. + \tt + % + % But `\ ' produces the large typewriter interword space. + \def\ {{\spaceskip = 0pt{} }}% + % + % Turn off hyphenation. + \nohyphenation + % + \rawbackslash + \plainfrenchspacing + #1% + }% + \null % reset spacefactor to 1000 +} + +% We *must* turn on hyphenation at `-' and `_' in @code. +% Otherwise, it is too hard to avoid overfull hboxes +% in the Emacs manual, the Library manual, etc. + +% Unfortunately, TeX uses one parameter (\hyphenchar) to control +% both hyphenation at - and hyphenation within words. +% We must therefore turn them both off (\tclose does that) +% and arrange explicitly to hyphenate at a dash. +% -- rms. +{ + \catcode`\-=\active \catcode`\_=\active + \catcode`\'=\active \catcode`\`=\active + \global\let'=\rq \global\let`=\lq % default definitions + % + \global\def\code{\begingroup + \setupmarkupstyle{code}% + % The following should really be moved into \setupmarkupstyle handlers. + \catcode\dashChar=\active \catcode\underChar=\active + \ifallowcodebreaks + \let-\codedash + \let_\codeunder + \else + \let-\realdash + \let_\realunder + \fi + \codex + } +} + +\def\codex #1{\tclose{#1}\endgroup} + +\def\realdash{-} +\def\codedash{-\discretionary{}{}{}} +\def\codeunder{% + % this is all so @math{@code{var_name}+1} can work. In math mode, _ + % is "active" (mathcode"8000) and \normalunderscore (or \char95, etc.) + % will therefore expand the active definition of _, which is us + % (inside @code that is), therefore an endless loop. + \ifusingtt{\ifmmode + \mathchar"075F % class 0=ordinary, family 7=ttfam, pos 0x5F=_. + \else\normalunderscore \fi + \discretionary{}{}{}}% + {\_}% +} + +% An additional complication: the above will allow breaks after, e.g., +% each of the four underscores in __typeof__. This is undesirable in +% some manuals, especially if they don't have long identifiers in +% general. @allowcodebreaks provides a way to control this. +% +\newif\ifallowcodebreaks \allowcodebreakstrue + +\def\keywordtrue{true} +\def\keywordfalse{false} + +\parseargdef\allowcodebreaks{% + \def\txiarg{#1}% + \ifx\txiarg\keywordtrue + \allowcodebreakstrue + \else\ifx\txiarg\keywordfalse + \allowcodebreaksfalse + \else + \errhelp = \EMsimple + \errmessage{Unknown @allowcodebreaks option `\txiarg', must be true|false}% + \fi\fi +} + +% @uref (abbreviation for `urlref') takes an optional (comma-separated) +% second argument specifying the text to display and an optional third +% arg as text to display instead of (rather than in addition to) the url +% itself. First (mandatory) arg is the url. +% (This \urefnobreak definition isn't used now, leaving it for a while +% for comparison.) +\def\urefnobreak#1{\dourefnobreak #1,,,\finish} +\def\dourefnobreak#1,#2,#3,#4\finish{\begingroup + \unsepspaces + \pdfurl{#1}% + \setbox0 = \hbox{\ignorespaces #3}% + \ifdim\wd0 > 0pt + \unhbox0 % third arg given, show only that + \else + \setbox0 = \hbox{\ignorespaces #2}% + \ifdim\wd0 > 0pt + \ifpdf + \unhbox0 % PDF: 2nd arg given, show only it + \else + \unhbox0\ (\code{#1})% DVI: 2nd arg given, show both it and url + \fi + \else + \code{#1}% only url given, so show it + \fi + \fi + \endlink +\endgroup} + +% This \urefbreak definition is the active one. +\def\urefbreak{\begingroup \urefcatcodes \dourefbreak} +\let\uref=\urefbreak +\def\dourefbreak#1{\urefbreakfinish #1,,,\finish} +\def\urefbreakfinish#1,#2,#3,#4\finish{% doesn't work in @example + \unsepspaces + \pdfurl{#1}% + \setbox0 = \hbox{\ignorespaces #3}% + \ifdim\wd0 > 0pt + \unhbox0 % third arg given, show only that + \else + \setbox0 = \hbox{\ignorespaces #2}% + \ifdim\wd0 > 0pt + \ifpdf + \unhbox0 % PDF: 2nd arg given, show only it + \else + \unhbox0\ (\urefcode{#1})% DVI: 2nd arg given, show both it and url + \fi + \else + \urefcode{#1}% only url given, so show it + \fi + \fi + \endlink +\endgroup} + +% Allow line breaks around only a few characters (only). +\def\urefcatcodes{% + \catcode\ampChar=\active \catcode\dotChar=\active + \catcode\hashChar=\active \catcode\questChar=\active + \catcode\slashChar=\active +} +{ + \urefcatcodes + % + \global\def\urefcode{\begingroup + \setupmarkupstyle{code}% + \urefcatcodes + \let&\urefcodeamp + \let.\urefcodedot + \let#\urefcodehash + \let?\urefcodequest + \let/\urefcodeslash + \codex + } + % + % By default, they are just regular characters. + \global\def&{\normalamp} + \global\def.{\normaldot} + \global\def#{\normalhash} + \global\def?{\normalquest} + \global\def/{\normalslash} +} + +% we put a little stretch before and after the breakable chars, to help +% line breaking of long url's. The unequal skips make look better in +% cmtt at least, especially for dots. +\def\urefprestretch{\urefprebreak \hskip0pt plus.13em } +\def\urefpoststretch{\urefpostbreak \hskip0pt plus.1em } +% +\def\urefcodeamp{\urefprestretch \&\urefpoststretch} +\def\urefcodedot{\urefprestretch .\urefpoststretch} +\def\urefcodehash{\urefprestretch \#\urefpoststretch} +\def\urefcodequest{\urefprestretch ?\urefpoststretch} +\def\urefcodeslash{\futurelet\next\urefcodeslashfinish} +{ + \catcode`\/=\active + \global\def\urefcodeslashfinish{% + \urefprestretch \slashChar + % Allow line break only after the final / in a sequence of + % slashes, to avoid line break between the slashes in http://. + \ifx\next/\else \urefpoststretch \fi + } +} + +% One more complication: by default we'll break after the special +% characters, but some people like to break before the special chars, so +% allow that. Also allow no breaking at all, for manual control. +% +\parseargdef\urefbreakstyle{% + \def\txiarg{#1}% + \ifx\txiarg\wordnone + \def\urefprebreak{\nobreak}\def\urefpostbreak{\nobreak} + \else\ifx\txiarg\wordbefore + \def\urefprebreak{\allowbreak}\def\urefpostbreak{\nobreak} + \else\ifx\txiarg\wordafter + \def\urefprebreak{\nobreak}\def\urefpostbreak{\allowbreak} + \else + \errhelp = \EMsimple + \errmessage{Unknown @urefbreakstyle setting `\txiarg'}% + \fi\fi\fi +} +\def\wordafter{after} +\def\wordbefore{before} +\def\wordnone{none} + +\urefbreakstyle after + +% @url synonym for @uref, since that's how everyone uses it. +% +\let\url=\uref + +% rms does not like angle brackets --karl, 17may97. +% So now @email is just like @uref, unless we are pdf. +% +%\def\email#1{\angleleft{\tt #1}\angleright} +\ifpdf + \def\email#1{\doemail#1,,\finish} + \def\doemail#1,#2,#3\finish{\begingroup + \unsepspaces + \pdfurl{mailto:#1}% + \setbox0 = \hbox{\ignorespaces #2}% + \ifdim\wd0>0pt\unhbox0\else\code{#1}\fi + \endlink + \endgroup} +\else + \let\email=\uref +\fi + +% @kbd is like @code, except that if the argument is just one @key command, +% then @kbd has no effect. +\def\kbd#1{{\setupmarkupstyle{kbd}\def\look{#1}\expandafter\kbdfoo\look??\par}} + +% @kbdinputstyle -- arg is `distinct' (@kbd uses slanted tty font always), +% `example' (@kbd uses ttsl only inside of @example and friends), +% or `code' (@kbd uses normal tty font always). +\parseargdef\kbdinputstyle{% + \def\txiarg{#1}% + \ifx\txiarg\worddistinct + \gdef\kbdexamplefont{\ttsl}\gdef\kbdfont{\ttsl}% + \else\ifx\txiarg\wordexample + \gdef\kbdexamplefont{\ttsl}\gdef\kbdfont{\tt}% + \else\ifx\txiarg\wordcode + \gdef\kbdexamplefont{\tt}\gdef\kbdfont{\tt}% + \else + \errhelp = \EMsimple + \errmessage{Unknown @kbdinputstyle setting `\txiarg'}% + \fi\fi\fi +} +\def\worddistinct{distinct} +\def\wordexample{example} +\def\wordcode{code} + +% Default is `distinct'. +\kbdinputstyle distinct + +\def\xkey{\key} +\def\kbdfoo#1#2#3\par{\def\one{#1}\def\three{#3}\def\threex{??}% +\ifx\one\xkey\ifx\threex\three \key{#2}% +\else{\tclose{\kbdfont\setupmarkupstyle{kbd}\look}}\fi +\else{\tclose{\kbdfont\setupmarkupstyle{kbd}\look}}\fi} + +% For @indicateurl, @env, @command quotes seem unnecessary, so use \code. +\let\indicateurl=\code +\let\env=\code +\let\command=\code + +% @clicksequence{File @click{} Open ...} +\def\clicksequence#1{\begingroup #1\endgroup} + +% @clickstyle @arrow (by default) +\parseargdef\clickstyle{\def\click{#1}} +\def\click{\arrow} + +% Typeset a dimension, e.g., `in' or `pt'. The only reason for the +% argument is to make the input look right: @dmn{pt} instead of @dmn{}pt. +% +\def\dmn#1{\thinspace #1} + +% @l was never documented to mean ``switch to the Lisp font'', +% and it is not used as such in any manual I can find. We need it for +% Polish suppressed-l. --karl, 22sep96. +%\def\l#1{{\li #1}\null} + +% @acronym for "FBI", "NATO", and the like. +% We print this one point size smaller, since it's intended for +% all-uppercase. +% +\def\acronym#1{\doacronym #1,,\finish} +\def\doacronym#1,#2,#3\finish{% + {\selectfonts\lsize #1}% + \def\temp{#2}% + \ifx\temp\empty \else + \space ({\unsepspaces \ignorespaces \temp \unskip})% + \fi + \null % reset \spacefactor=1000 +} + +% @abbr for "Comput. J." and the like. +% No font change, but don't do end-of-sentence spacing. +% +\def\abbr#1{\doabbr #1,,\finish} +\def\doabbr#1,#2,#3\finish{% + {\plainfrenchspacing #1}% + \def\temp{#2}% + \ifx\temp\empty \else + \space ({\unsepspaces \ignorespaces \temp \unskip})% + \fi + \null % reset \spacefactor=1000 +} + +% @asis just yields its argument. Used with @table, for example. +% +\def\asis#1{#1} + +% @math outputs its argument in math mode. +% +% One complication: _ usually means subscripts, but it could also mean +% an actual _ character, as in @math{@var{some_variable} + 1}. So make +% _ active, and distinguish by seeing if the current family is \slfam, +% which is what @var uses. +{ + \catcode`\_ = \active + \gdef\mathunderscore{% + \catcode`\_=\active + \def_{\ifnum\fam=\slfam \_\else\sb\fi}% + } +} +% Another complication: we want \\ (and @\) to output a math (or tt) \. +% FYI, plain.tex uses \\ as a temporary control sequence (for no +% particular reason), but this is not advertised and we don't care. +% +% The \mathchar is class=0=ordinary, family=7=ttfam, position=5C=\. +\def\mathbackslash{\ifnum\fam=\ttfam \mathchar"075C \else\backslash \fi} +% +\def\math{% + \tex + \mathunderscore + \let\\ = \mathbackslash + \mathactive + % make the texinfo accent commands work in math mode + \let\"=\ddot + \let\'=\acute + \let\==\bar + \let\^=\hat + \let\`=\grave + \let\u=\breve + \let\v=\check + \let\~=\tilde + \let\dotaccent=\dot + $\finishmath +} +\def\finishmath#1{#1$\endgroup} % Close the group opened by \tex. + +% Some active characters (such as <) are spaced differently in math. +% We have to reset their definitions in case the @math was an argument +% to a command which sets the catcodes (such as @item or @section). +% +{ + \catcode`^ = \active + \catcode`< = \active + \catcode`> = \active + \catcode`+ = \active + \catcode`' = \active + \gdef\mathactive{% + \let^ = \ptexhat + \let< = \ptexless + \let> = \ptexgtr + \let+ = \ptexplus + \let' = \ptexquoteright + } +} + +% @inlinefmt{FMTNAME,PROCESSED-TEXT} and @inlineraw{FMTNAME,RAW-TEXT}. +% Ignore unless FMTNAME == tex; then it is like @iftex and @tex, +% except specified as a normal braced arg, so no newlines to worry about. +% +\def\outfmtnametex{tex} +% +\long\def\inlinefmt#1{\doinlinefmt #1,\finish} +\long\def\doinlinefmt#1,#2,\finish{% + \def\inlinefmtname{#1}% + \ifx\inlinefmtname\outfmtnametex \ignorespaces #2\fi +} +% For raw, must switch into @tex before parsing the argument, to avoid +% setting catcodes prematurely. Doing it this way means that, for +% example, @inlineraw{html, foo{bar} gets a parse error instead of being +% ignored. But this isn't important because if people want a literal +% *right* brace they would have to use a command anyway, so they may as +% well use a command to get a left brace too. We could re-use the +% delimiter character idea from \verb, but it seems like overkill. +% +\long\def\inlineraw{\tex \doinlineraw} +\long\def\doinlineraw#1{\doinlinerawtwo #1,\finish} +\def\doinlinerawtwo#1,#2,\finish{% + \def\inlinerawname{#1}% + \ifx\inlinerawname\outfmtnametex \ignorespaces #2\fi + \endgroup % close group opened by \tex. +} + + +\message{glyphs,} +% and logos. + +% @@ prints an @, as does @atchar{}. +\def\@{\char64 } +\let\atchar=\@ + +% @{ @} @lbracechar{} @rbracechar{} all generate brace characters. +% Unless we're in typewriter, use \ecfont because the CM text fonts do +% not have braces, and we don't want to switch into math. +\def\mylbrace{{\ifmonospace\else\ecfont\fi \char123}} +\def\myrbrace{{\ifmonospace\else\ecfont\fi \char125}} +\let\{=\mylbrace \let\lbracechar=\{ +\let\}=\myrbrace \let\rbracechar=\} +\begingroup + % Definitions to produce \{ and \} commands for indices, + % and @{ and @} for the aux/toc files. + \catcode`\{ = \other \catcode`\} = \other + \catcode`\[ = 1 \catcode`\] = 2 + \catcode`\! = 0 \catcode`\\ = \other + !gdef!lbracecmd[\{]% + !gdef!rbracecmd[\}]% + !gdef!lbraceatcmd[@{]% + !gdef!rbraceatcmd[@}]% +!endgroup + +% @comma{} to avoid , parsing problems. +\let\comma = , + +% Accents: @, @dotaccent @ringaccent @ubaraccent @udotaccent +% Others are defined by plain TeX: @` @' @" @^ @~ @= @u @v @H. +\let\, = \ptexc +\let\dotaccent = \ptexdot +\def\ringaccent#1{{\accent23 #1}} +\let\tieaccent = \ptext +\let\ubaraccent = \ptexb +\let\udotaccent = \d + +% Other special characters: @questiondown @exclamdown @ordf @ordm +% Plain TeX defines: @AA @AE @O @OE @L (plus lowercase versions) @ss. +\def\questiondown{?`} +\def\exclamdown{!`} +\def\ordf{\leavevmode\raise1ex\hbox{\selectfonts\lllsize \underbar{a}}} +\def\ordm{\leavevmode\raise1ex\hbox{\selectfonts\lllsize \underbar{o}}} + +% Dotless i and dotless j, used for accents. +\def\imacro{i} +\def\jmacro{j} +\def\dotless#1{% + \def\temp{#1}% + \ifx\temp\imacro \ifmmode\imath \else\ptexi \fi + \else\ifx\temp\jmacro \ifmmode\jmath \else\j \fi + \else \errmessage{@dotless can be used only with i or j}% + \fi\fi +} + +% The \TeX{} logo, as in plain, but resetting the spacing so that a +% period following counts as ending a sentence. (Idea found in latex.) +% +\edef\TeX{\TeX \spacefactor=1000 } + +% @LaTeX{} logo. Not quite the same results as the definition in +% latex.ltx, since we use a different font for the raised A; it's most +% convenient for us to use an explicitly smaller font, rather than using +% the \scriptstyle font (since we don't reset \scriptstyle and +% \scriptscriptstyle). +% +\def\LaTeX{% + L\kern-.36em + {\setbox0=\hbox{T}% + \vbox to \ht0{\hbox{% + \ifx\textnominalsize\xwordpt + % for 10pt running text, \lllsize (8pt) is too small for the A in LaTeX. + % Revert to plain's \scriptsize, which is 7pt. + \count255=\the\fam $\fam\count255 \scriptstyle A$% + \else + % For 11pt, we can use our lllsize. + \selectfonts\lllsize A% + \fi + }% + \vss + }}% + \kern-.15em + \TeX +} + +% Some math mode symbols. +\def\bullet{$\ptexbullet$} +\def\geq{\ifmmode \ge\else $\ge$\fi} +\def\leq{\ifmmode \le\else $\le$\fi} +\def\minus{\ifmmode -\else $-$\fi} + +% @dots{} outputs an ellipsis using the current font. +% We do .5em per period so that it has the same spacing in the cm +% typewriter fonts as three actual period characters; on the other hand, +% in other typewriter fonts three periods are wider than 1.5em. So do +% whichever is larger. +% +\def\dots{% + \leavevmode + \setbox0=\hbox{...}% get width of three periods + \ifdim\wd0 > 1.5em + \dimen0 = \wd0 + \else + \dimen0 = 1.5em + \fi + \hbox to \dimen0{% + \hskip 0pt plus.25fil + .\hskip 0pt plus1fil + .\hskip 0pt plus1fil + .\hskip 0pt plus.5fil + }% +} + +% @enddots{} is an end-of-sentence ellipsis. +% +\def\enddots{% + \dots + \spacefactor=\endofsentencespacefactor +} + +% @point{}, @result{}, @expansion{}, @print{}, @equiv{}. +% +% Since these characters are used in examples, they should be an even number of +% \tt widths. Each \tt character is 1en, so two makes it 1em. +% +\def\point{$\star$} +\def\arrow{\leavevmode\raise.05ex\hbox to 1em{\hfil$\rightarrow$\hfil}} +\def\result{\leavevmode\raise.05ex\hbox to 1em{\hfil$\Rightarrow$\hfil}} +\def\expansion{\leavevmode\hbox to 1em{\hfil$\mapsto$\hfil}} +\def\print{\leavevmode\lower.1ex\hbox to 1em{\hfil$\dashv$\hfil}} +\def\equiv{\leavevmode\hbox to 1em{\hfil$\ptexequiv$\hfil}} + +% The @error{} command. +% Adapted from the TeXbook's \boxit. +% +\newbox\errorbox +% +{\tentt \global\dimen0 = 3em}% Width of the box. +\dimen2 = .55pt % Thickness of rules +% The text. (`r' is open on the right, `e' somewhat less so on the left.) +\setbox0 = \hbox{\kern-.75pt \reducedsf \putworderror\kern-1.5pt} +% +\setbox\errorbox=\hbox to \dimen0{\hfil + \hsize = \dimen0 \advance\hsize by -5.8pt % Space to left+right. + \advance\hsize by -2\dimen2 % Rules. + \vbox{% + \hrule height\dimen2 + \hbox{\vrule width\dimen2 \kern3pt % Space to left of text. + \vtop{\kern2.4pt \box0 \kern2.4pt}% Space above/below. + \kern3pt\vrule width\dimen2}% Space to right. + \hrule height\dimen2} + \hfil} +% +\def\error{\leavevmode\lower.7ex\copy\errorbox} + +% @pounds{} is a sterling sign, which Knuth put in the CM italic font. +% +\def\pounds{{\it\$}} + +% @euro{} comes from a separate font, depending on the current style. +% We use the free feym* fonts from the eurosym package by Henrik +% Theiling, which support regular, slanted, bold and bold slanted (and +% "outlined" (blackboard board, sort of) versions, which we don't need). +% It is available from http://www.ctan.org/tex-archive/fonts/eurosym. +% +% Although only regular is the truly official Euro symbol, we ignore +% that. The Euro is designed to be slightly taller than the regular +% font height. +% +% feymr - regular +% feymo - slanted +% feybr - bold +% feybo - bold slanted +% +% There is no good (free) typewriter version, to my knowledge. +% A feymr10 euro is ~7.3pt wide, while a normal cmtt10 char is ~5.25pt wide. +% Hmm. +% +% Also doesn't work in math. Do we need to do math with euro symbols? +% Hope not. +% +% +\def\euro{{\eurofont e}} +\def\eurofont{% + % We set the font at each command, rather than predefining it in + % \textfonts and the other font-switching commands, so that + % installations which never need the symbol don't have to have the + % font installed. + % + % There is only one designed size (nominal 10pt), so we always scale + % that to the current nominal size. + % + % By the way, simply using "at 1em" works for cmr10 and the like, but + % does not work for cmbx10 and other extended/shrunken fonts. + % + \def\eurosize{\csname\curfontsize nominalsize\endcsname}% + % + \ifx\curfontstyle\bfstylename + % bold: + \font\thiseurofont = \ifusingit{feybo10}{feybr10} at \eurosize + \else + % regular: + \font\thiseurofont = \ifusingit{feymo10}{feymr10} at \eurosize + \fi + \thiseurofont +} + +% Glyphs from the EC fonts. We don't use \let for the aliases, because +% sometimes we redefine the original macro, and the alias should reflect +% the redefinition. +% +% Use LaTeX names for the Icelandic letters. +\def\DH{{\ecfont \char"D0}} % Eth +\def\dh{{\ecfont \char"F0}} % eth +\def\TH{{\ecfont \char"DE}} % Thorn +\def\th{{\ecfont \char"FE}} % thorn +% +\def\guillemetleft{{\ecfont \char"13}} +\def\guillemotleft{\guillemetleft} +\def\guillemetright{{\ecfont \char"14}} +\def\guillemotright{\guillemetright} +\def\guilsinglleft{{\ecfont \char"0E}} +\def\guilsinglright{{\ecfont \char"0F}} +\def\quotedblbase{{\ecfont \char"12}} +\def\quotesinglbase{{\ecfont \char"0D}} +% +% This positioning is not perfect (see the ogonek LaTeX package), but +% we have the precomposed glyphs for the most common cases. We put the +% tests to use those glyphs in the single \ogonek macro so we have fewer +% dummy definitions to worry about for index entries, etc. +% +% ogonek is also used with other letters in Lithuanian (IOU), but using +% the precomposed glyphs for those is not so easy since they aren't in +% the same EC font. +\def\ogonek#1{{% + \def\temp{#1}% + \ifx\temp\macrocharA\Aogonek + \else\ifx\temp\macrochara\aogonek + \else\ifx\temp\macrocharE\Eogonek + \else\ifx\temp\macrochare\eogonek + \else + \ecfont \setbox0=\hbox{#1}% + \ifdim\ht0=1ex\accent"0C #1% + \else\ooalign{\unhbox0\crcr\hidewidth\char"0C \hidewidth}% + \fi + \fi\fi\fi\fi + }% +} +\def\Aogonek{{\ecfont \char"81}}\def\macrocharA{A} +\def\aogonek{{\ecfont \char"A1}}\def\macrochara{a} +\def\Eogonek{{\ecfont \char"86}}\def\macrocharE{E} +\def\eogonek{{\ecfont \char"A6}}\def\macrochare{e} +% +% Use the ec* fonts (cm-super in outline format) for non-CM glyphs. +\def\ecfont{% + % We can't distinguish serif/sans and italic/slanted, but this + % is used for crude hacks anyway (like adding French and German + % quotes to documents typeset with CM, where we lose kerning), so + % hopefully nobody will notice/care. + \edef\ecsize{\csname\curfontsize ecsize\endcsname}% + \edef\nominalsize{\csname\curfontsize nominalsize\endcsname}% + \ifx\curfontstyle\bfstylename + % bold: + \font\thisecfont = ecb\ifusingit{i}{x}\ecsize \space at \nominalsize + \else + % regular: + \font\thisecfont = ec\ifusingit{ti}{rm}\ecsize \space at \nominalsize + \fi + \thisecfont +} + +% @registeredsymbol - R in a circle. The font for the R should really +% be smaller yet, but lllsize is the best we can do for now. +% Adapted from the plain.tex definition of \copyright. +% +\def\registeredsymbol{% + $^{{\ooalign{\hfil\raise.07ex\hbox{\selectfonts\lllsize R}% + \hfil\crcr\Orb}}% + }$% +} + +% @textdegree - the normal degrees sign. +% +\def\textdegree{$^\circ$} + +% Laurent Siebenmann reports \Orb undefined with: +% Textures 1.7.7 (preloaded format=plain 93.10.14) (68K) 16 APR 2004 02:38 +% so we'll define it if necessary. +% +\ifx\Orb\thisisundefined +\def\Orb{\mathhexbox20D} +\fi + +% Quotes. +\chardef\quotedblleft="5C +\chardef\quotedblright=`\" +\chardef\quoteleft=`\` +\chardef\quoteright=`\' + + +\message{page headings,} + +\newskip\titlepagetopglue \titlepagetopglue = 1.5in +\newskip\titlepagebottomglue \titlepagebottomglue = 2pc + +% First the title page. Must do @settitle before @titlepage. +\newif\ifseenauthor +\newif\iffinishedtitlepage + +% Do an implicit @contents or @shortcontents after @end titlepage if the +% user says @setcontentsaftertitlepage or @setshortcontentsaftertitlepage. +% +\newif\ifsetcontentsaftertitlepage + \let\setcontentsaftertitlepage = \setcontentsaftertitlepagetrue +\newif\ifsetshortcontentsaftertitlepage + \let\setshortcontentsaftertitlepage = \setshortcontentsaftertitlepagetrue + +\parseargdef\shorttitlepage{% + \begingroup \hbox{}\vskip 1.5in \chaprm \centerline{#1}% + \endgroup\page\hbox{}\page} + +\envdef\titlepage{% + % Open one extra group, as we want to close it in the middle of \Etitlepage. + \begingroup + \parindent=0pt \textfonts + % Leave some space at the very top of the page. + \vglue\titlepagetopglue + % No rule at page bottom unless we print one at the top with @title. + \finishedtitlepagetrue + % + % Most title ``pages'' are actually two pages long, with space + % at the top of the second. We don't want the ragged left on the second. + \let\oldpage = \page + \def\page{% + \iffinishedtitlepage\else + \finishtitlepage + \fi + \let\page = \oldpage + \page + \null + }% +} + +\def\Etitlepage{% + \iffinishedtitlepage\else + \finishtitlepage + \fi + % It is important to do the page break before ending the group, + % because the headline and footline are only empty inside the group. + % If we use the new definition of \page, we always get a blank page + % after the title page, which we certainly don't want. + \oldpage + \endgroup + % + % Need this before the \...aftertitlepage checks so that if they are + % in effect the toc pages will come out with page numbers. + \HEADINGSon + % + % If they want short, they certainly want long too. + \ifsetshortcontentsaftertitlepage + \shortcontents + \contents + \global\let\shortcontents = \relax + \global\let\contents = \relax + \fi + % + \ifsetcontentsaftertitlepage + \contents + \global\let\contents = \relax + \global\let\shortcontents = \relax + \fi +} + +\def\finishtitlepage{% + \vskip4pt \hrule height 2pt width \hsize + \vskip\titlepagebottomglue + \finishedtitlepagetrue +} + +% Macros to be used within @titlepage: + +\let\subtitlerm=\tenrm +\def\subtitlefont{\subtitlerm \normalbaselineskip = 13pt \normalbaselines} + +\parseargdef\title{% + \checkenv\titlepage + \leftline{\titlefonts\rmisbold #1} + % print a rule at the page bottom also. + \finishedtitlepagefalse + \vskip4pt \hrule height 4pt width \hsize \vskip4pt +} + +\parseargdef\subtitle{% + \checkenv\titlepage + {\subtitlefont \rightline{#1}}% +} + +% @author should come last, but may come many times. +% It can also be used inside @quotation. +% +\parseargdef\author{% + \def\temp{\quotation}% + \ifx\thisenv\temp + \def\quotationauthor{#1}% printed in \Equotation. + \else + \checkenv\titlepage + \ifseenauthor\else \vskip 0pt plus 1filll \seenauthortrue \fi + {\secfonts\rmisbold \leftline{#1}}% + \fi +} + + +% Set up page headings and footings. + +\let\thispage=\folio + +\newtoks\evenheadline % headline on even pages +\newtoks\oddheadline % headline on odd pages +\newtoks\evenfootline % footline on even pages +\newtoks\oddfootline % footline on odd pages + +% Now make TeX use those variables +\headline={{\textfonts\rm \ifodd\pageno \the\oddheadline + \else \the\evenheadline \fi}} +\footline={{\textfonts\rm \ifodd\pageno \the\oddfootline + \else \the\evenfootline \fi}\HEADINGShook} +\let\HEADINGShook=\relax + +% Commands to set those variables. +% For example, this is what @headings on does +% @evenheading @thistitle|@thispage|@thischapter +% @oddheading @thischapter|@thispage|@thistitle +% @evenfooting @thisfile|| +% @oddfooting ||@thisfile + + +\def\evenheading{\parsearg\evenheadingxxx} +\def\evenheadingxxx #1{\evenheadingyyy #1\|\|\|\|\finish} +\def\evenheadingyyy #1\|#2\|#3\|#4\finish{% +\global\evenheadline={\rlap{\centerline{#2}}\line{#1\hfil#3}}} + +\def\oddheading{\parsearg\oddheadingxxx} +\def\oddheadingxxx #1{\oddheadingyyy #1\|\|\|\|\finish} +\def\oddheadingyyy #1\|#2\|#3\|#4\finish{% +\global\oddheadline={\rlap{\centerline{#2}}\line{#1\hfil#3}}} + +\parseargdef\everyheading{\oddheadingxxx{#1}\evenheadingxxx{#1}}% + +\def\evenfooting{\parsearg\evenfootingxxx} +\def\evenfootingxxx #1{\evenfootingyyy #1\|\|\|\|\finish} +\def\evenfootingyyy #1\|#2\|#3\|#4\finish{% +\global\evenfootline={\rlap{\centerline{#2}}\line{#1\hfil#3}}} + +\def\oddfooting{\parsearg\oddfootingxxx} +\def\oddfootingxxx #1{\oddfootingyyy #1\|\|\|\|\finish} +\def\oddfootingyyy #1\|#2\|#3\|#4\finish{% + \global\oddfootline = {\rlap{\centerline{#2}}\line{#1\hfil#3}}% + % + % Leave some space for the footline. Hopefully ok to assume + % @evenfooting will not be used by itself. + \global\advance\pageheight by -12pt + \global\advance\vsize by -12pt +} + +\parseargdef\everyfooting{\oddfootingxxx{#1}\evenfootingxxx{#1}} + +% @evenheadingmarks top \thischapter <- chapter at the top of a page +% @evenheadingmarks bottom \thischapter <- chapter at the bottom of a page +% +% The same set of arguments for: +% +% @oddheadingmarks +% @evenfootingmarks +% @oddfootingmarks +% @everyheadingmarks +% @everyfootingmarks + +\def\evenheadingmarks{\headingmarks{even}{heading}} +\def\oddheadingmarks{\headingmarks{odd}{heading}} +\def\evenfootingmarks{\headingmarks{even}{footing}} +\def\oddfootingmarks{\headingmarks{odd}{footing}} +\def\everyheadingmarks#1 {\headingmarks{even}{heading}{#1} + \headingmarks{odd}{heading}{#1} } +\def\everyfootingmarks#1 {\headingmarks{even}{footing}{#1} + \headingmarks{odd}{footing}{#1} } +% #1 = even/odd, #2 = heading/footing, #3 = top/bottom. +\def\headingmarks#1#2#3 {% + \expandafter\let\expandafter\temp \csname get#3headingmarks\endcsname + \global\expandafter\let\csname get#1#2marks\endcsname \temp +} + +\everyheadingmarks bottom +\everyfootingmarks bottom + +% @headings double turns headings on for double-sided printing. +% @headings single turns headings on for single-sided printing. +% @headings off turns them off. +% @headings on same as @headings double, retained for compatibility. +% @headings after turns on double-sided headings after this page. +% @headings doubleafter turns on double-sided headings after this page. +% @headings singleafter turns on single-sided headings after this page. +% By default, they are off at the start of a document, +% and turned `on' after @end titlepage. + +\def\headings #1 {\csname HEADINGS#1\endcsname} + +\def\headingsoff{% non-global headings elimination + \evenheadline={\hfil}\evenfootline={\hfil}% + \oddheadline={\hfil}\oddfootline={\hfil}% +} + +\def\HEADINGSoff{{\globaldefs=1 \headingsoff}} % global setting +\HEADINGSoff % it's the default + +% When we turn headings on, set the page number to 1. +% For double-sided printing, put current file name in lower left corner, +% chapter name on inside top of right hand pages, document +% title on inside top of left hand pages, and page numbers on outside top +% edge of all pages. +\def\HEADINGSdouble{% +\global\pageno=1 +\global\evenfootline={\hfil} +\global\oddfootline={\hfil} +\global\evenheadline={\line{\folio\hfil\thistitle}} +\global\oddheadline={\line{\thischapter\hfil\folio}} +\global\let\contentsalignmacro = \chapoddpage +} +\let\contentsalignmacro = \chappager + +% For single-sided printing, chapter title goes across top left of page, +% page number on top right. +\def\HEADINGSsingle{% +\global\pageno=1 +\global\evenfootline={\hfil} +\global\oddfootline={\hfil} +\global\evenheadline={\line{\thischapter\hfil\folio}} +\global\oddheadline={\line{\thischapter\hfil\folio}} +\global\let\contentsalignmacro = \chappager +} +\def\HEADINGSon{\HEADINGSdouble} + +\def\HEADINGSafter{\let\HEADINGShook=\HEADINGSdoublex} +\let\HEADINGSdoubleafter=\HEADINGSafter +\def\HEADINGSdoublex{% +\global\evenfootline={\hfil} +\global\oddfootline={\hfil} +\global\evenheadline={\line{\folio\hfil\thistitle}} +\global\oddheadline={\line{\thischapter\hfil\folio}} +\global\let\contentsalignmacro = \chapoddpage +} + +\def\HEADINGSsingleafter{\let\HEADINGShook=\HEADINGSsinglex} +\def\HEADINGSsinglex{% +\global\evenfootline={\hfil} +\global\oddfootline={\hfil} +\global\evenheadline={\line{\thischapter\hfil\folio}} +\global\oddheadline={\line{\thischapter\hfil\folio}} +\global\let\contentsalignmacro = \chappager +} + +% Subroutines used in generating headings +% This produces Day Month Year style of output. +% Only define if not already defined, in case a txi-??.tex file has set +% up a different format (e.g., txi-cs.tex does this). +\ifx\today\thisisundefined +\def\today{% + \number\day\space + \ifcase\month + \or\putwordMJan\or\putwordMFeb\or\putwordMMar\or\putwordMApr + \or\putwordMMay\or\putwordMJun\or\putwordMJul\or\putwordMAug + \or\putwordMSep\or\putwordMOct\or\putwordMNov\or\putwordMDec + \fi + \space\number\year} +\fi + +% @settitle line... specifies the title of the document, for headings. +% It generates no output of its own. +\def\thistitle{\putwordNoTitle} +\def\settitle{\parsearg{\gdef\thistitle}} + + +\message{tables,} +% Tables -- @table, @ftable, @vtable, @item(x). + +% default indentation of table text +\newdimen\tableindent \tableindent=.8in +% default indentation of @itemize and @enumerate text +\newdimen\itemindent \itemindent=.3in +% margin between end of table item and start of table text. +\newdimen\itemmargin \itemmargin=.1in + +% used internally for \itemindent minus \itemmargin +\newdimen\itemmax + +% Note @table, @ftable, and @vtable define @item, @itemx, etc., with +% these defs. +% They also define \itemindex +% to index the item name in whatever manner is desired (perhaps none). + +\newif\ifitemxneedsnegativevskip + +\def\itemxpar{\par\ifitemxneedsnegativevskip\nobreak\vskip-\parskip\nobreak\fi} + +\def\internalBitem{\smallbreak \parsearg\itemzzz} +\def\internalBitemx{\itemxpar \parsearg\itemzzz} + +\def\itemzzz #1{\begingroup % + \advance\hsize by -\rightskip + \advance\hsize by -\tableindent + \setbox0=\hbox{\itemindicate{#1}}% + \itemindex{#1}% + \nobreak % This prevents a break before @itemx. + % + % If the item text does not fit in the space we have, put it on a line + % by itself, and do not allow a page break either before or after that + % line. We do not start a paragraph here because then if the next + % command is, e.g., @kindex, the whatsit would get put into the + % horizontal list on a line by itself, resulting in extra blank space. + \ifdim \wd0>\itemmax + % + % Make this a paragraph so we get the \parskip glue and wrapping, + % but leave it ragged-right. + \begingroup + \advance\leftskip by-\tableindent + \advance\hsize by\tableindent + \advance\rightskip by0pt plus1fil\relax + \leavevmode\unhbox0\par + \endgroup + % + % We're going to be starting a paragraph, but we don't want the + % \parskip glue -- logically it's part of the @item we just started. + \nobreak \vskip-\parskip + % + % Stop a page break at the \parskip glue coming up. However, if + % what follows is an environment such as @example, there will be no + % \parskip glue; then the negative vskip we just inserted would + % cause the example and the item to crash together. So we use this + % bizarre value of 10001 as a signal to \aboveenvbreak to insert + % \parskip glue after all. Section titles are handled this way also. + % + \penalty 10001 + \endgroup + \itemxneedsnegativevskipfalse + \else + % The item text fits into the space. Start a paragraph, so that the + % following text (if any) will end up on the same line. + \noindent + % Do this with kerns and \unhbox so that if there is a footnote in + % the item text, it can migrate to the main vertical list and + % eventually be printed. + \nobreak\kern-\tableindent + \dimen0 = \itemmax \advance\dimen0 by \itemmargin \advance\dimen0 by -\wd0 + \unhbox0 + \nobreak\kern\dimen0 + \endgroup + \itemxneedsnegativevskiptrue + \fi +} + +\def\item{\errmessage{@item while not in a list environment}} +\def\itemx{\errmessage{@itemx while not in a list environment}} + +% @table, @ftable, @vtable. +\envdef\table{% + \let\itemindex\gobble + \tablecheck{table}% +} +\envdef\ftable{% + \def\itemindex ##1{\doind {fn}{\code{##1}}}% + \tablecheck{ftable}% +} +\envdef\vtable{% + \def\itemindex ##1{\doind {vr}{\code{##1}}}% + \tablecheck{vtable}% +} +\def\tablecheck#1{% + \ifnum \the\catcode`\^^M=\active + \endgroup + \errmessage{This command won't work in this context; perhaps the problem is + that we are \inenvironment\thisenv}% + \def\next{\doignore{#1}}% + \else + \let\next\tablex + \fi + \next +} +\def\tablex#1{% + \def\itemindicate{#1}% + \parsearg\tabley +} +\def\tabley#1{% + {% + \makevalueexpandable + \edef\temp{\noexpand\tablez #1\space\space\space}% + \expandafter + }\temp \endtablez +} +\def\tablez #1 #2 #3 #4\endtablez{% + \aboveenvbreak + \ifnum 0#1>0 \advance \leftskip by #1\mil \fi + \ifnum 0#2>0 \tableindent=#2\mil \fi + \ifnum 0#3>0 \advance \rightskip by #3\mil \fi + \itemmax=\tableindent + \advance \itemmax by -\itemmargin + \advance \leftskip by \tableindent + \exdentamount=\tableindent + \parindent = 0pt + \parskip = \smallskipamount + \ifdim \parskip=0pt \parskip=2pt \fi + \let\item = \internalBitem + \let\itemx = \internalBitemx +} +\def\Etable{\endgraf\afterenvbreak} +\let\Eftable\Etable +\let\Evtable\Etable +\let\Eitemize\Etable +\let\Eenumerate\Etable + +% This is the counter used by @enumerate, which is really @itemize + +\newcount \itemno + +\envdef\itemize{\parsearg\doitemize} + +\def\doitemize#1{% + \aboveenvbreak + \itemmax=\itemindent + \advance\itemmax by -\itemmargin + \advance\leftskip by \itemindent + \exdentamount=\itemindent + \parindent=0pt + \parskip=\smallskipamount + \ifdim\parskip=0pt \parskip=2pt \fi + % + % Try typesetting the item mark that if the document erroneously says + % something like @itemize @samp (intending @table), there's an error + % right away at the @itemize. It's not the best error message in the + % world, but it's better than leaving it to the @item. This means if + % the user wants an empty mark, they have to say @w{} not just @w. + \def\itemcontents{#1}% + \setbox0 = \hbox{\itemcontents}% + % + % @itemize with no arg is equivalent to @itemize @bullet. + \ifx\itemcontents\empty\def\itemcontents{\bullet}\fi + % + \let\item=\itemizeitem +} + +% Definition of @item while inside @itemize and @enumerate. +% +\def\itemizeitem{% + \advance\itemno by 1 % for enumerations + {\let\par=\endgraf \smallbreak}% reasonable place to break + {% + % If the document has an @itemize directly after a section title, a + % \nobreak will be last on the list, and \sectionheading will have + % done a \vskip-\parskip. In that case, we don't want to zero + % parskip, or the item text will crash with the heading. On the + % other hand, when there is normal text preceding the item (as there + % usually is), we do want to zero parskip, or there would be too much + % space. In that case, we won't have a \nobreak before. At least + % that's the theory. + \ifnum\lastpenalty<10000 \parskip=0in \fi + \noindent + \hbox to 0pt{\hss \itemcontents \kern\itemmargin}% + % + \vadjust{\penalty 1200}}% not good to break after first line of item. + \flushcr +} + +% \splitoff TOKENS\endmark defines \first to be the first token in +% TOKENS, and \rest to be the remainder. +% +\def\splitoff#1#2\endmark{\def\first{#1}\def\rest{#2}}% + +% Allow an optional argument of an uppercase letter, lowercase letter, +% or number, to specify the first label in the enumerated list. No +% argument is the same as `1'. +% +\envparseargdef\enumerate{\enumeratey #1 \endenumeratey} +\def\enumeratey #1 #2\endenumeratey{% + % If we were given no argument, pretend we were given `1'. + \def\thearg{#1}% + \ifx\thearg\empty \def\thearg{1}\fi + % + % Detect if the argument is a single token. If so, it might be a + % letter. Otherwise, the only valid thing it can be is a number. + % (We will always have one token, because of the test we just made. + % This is a good thing, since \splitoff doesn't work given nothing at + % all -- the first parameter is undelimited.) + \expandafter\splitoff\thearg\endmark + \ifx\rest\empty + % Only one token in the argument. It could still be anything. + % A ``lowercase letter'' is one whose \lccode is nonzero. + % An ``uppercase letter'' is one whose \lccode is both nonzero, and + % not equal to itself. + % Otherwise, we assume it's a number. + % + % We need the \relax at the end of the \ifnum lines to stop TeX from + % continuing to look for a . + % + \ifnum\lccode\expandafter`\thearg=0\relax + \numericenumerate % a number (we hope) + \else + % It's a letter. + \ifnum\lccode\expandafter`\thearg=\expandafter`\thearg\relax + \lowercaseenumerate % lowercase letter + \else + \uppercaseenumerate % uppercase letter + \fi + \fi + \else + % Multiple tokens in the argument. We hope it's a number. + \numericenumerate + \fi +} + +% An @enumerate whose labels are integers. The starting integer is +% given in \thearg. +% +\def\numericenumerate{% + \itemno = \thearg + \startenumeration{\the\itemno}% +} + +% The starting (lowercase) letter is in \thearg. +\def\lowercaseenumerate{% + \itemno = \expandafter`\thearg + \startenumeration{% + % Be sure we're not beyond the end of the alphabet. + \ifnum\itemno=0 + \errmessage{No more lowercase letters in @enumerate; get a bigger + alphabet}% + \fi + \char\lccode\itemno + }% +} + +% The starting (uppercase) letter is in \thearg. +\def\uppercaseenumerate{% + \itemno = \expandafter`\thearg + \startenumeration{% + % Be sure we're not beyond the end of the alphabet. + \ifnum\itemno=0 + \errmessage{No more uppercase letters in @enumerate; get a bigger + alphabet} + \fi + \char\uccode\itemno + }% +} + +% Call \doitemize, adding a period to the first argument and supplying the +% common last two arguments. Also subtract one from the initial value in +% \itemno, since @item increments \itemno. +% +\def\startenumeration#1{% + \advance\itemno by -1 + \doitemize{#1.}\flushcr +} + +% @alphaenumerate and @capsenumerate are abbreviations for giving an arg +% to @enumerate. +% +\def\alphaenumerate{\enumerate{a}} +\def\capsenumerate{\enumerate{A}} +\def\Ealphaenumerate{\Eenumerate} +\def\Ecapsenumerate{\Eenumerate} + + +% @multitable macros +% Amy Hendrickson, 8/18/94, 3/6/96 +% +% @multitable ... @end multitable will make as many columns as desired. +% Contents of each column will wrap at width given in preamble. Width +% can be specified either with sample text given in a template line, +% or in percent of \hsize, the current width of text on page. + +% Table can continue over pages but will only break between lines. + +% To make preamble: +% +% Either define widths of columns in terms of percent of \hsize: +% @multitable @columnfractions .25 .3 .45 +% @item ... +% +% Numbers following @columnfractions are the percent of the total +% current hsize to be used for each column. You may use as many +% columns as desired. + + +% Or use a template: +% @multitable {Column 1 template} {Column 2 template} {Column 3 template} +% @item ... +% using the widest term desired in each column. + +% Each new table line starts with @item, each subsequent new column +% starts with @tab. Empty columns may be produced by supplying @tab's +% with nothing between them for as many times as empty columns are needed, +% ie, @tab@tab@tab will produce two empty columns. + +% @item, @tab do not need to be on their own lines, but it will not hurt +% if they are. + +% Sample multitable: + +% @multitable {Column 1 template} {Column 2 template} {Column 3 template} +% @item first col stuff @tab second col stuff @tab third col +% @item +% first col stuff +% @tab +% second col stuff +% @tab +% third col +% @item first col stuff @tab second col stuff +% @tab Many paragraphs of text may be used in any column. +% +% They will wrap at the width determined by the template. +% @item@tab@tab This will be in third column. +% @end multitable + +% Default dimensions may be reset by user. +% @multitableparskip is vertical space between paragraphs in table. +% @multitableparindent is paragraph indent in table. +% @multitablecolmargin is horizontal space to be left between columns. +% @multitablelinespace is space to leave between table items, baseline +% to baseline. +% 0pt means it depends on current normal line spacing. +% +\newskip\multitableparskip +\newskip\multitableparindent +\newdimen\multitablecolspace +\newskip\multitablelinespace +\multitableparskip=0pt +\multitableparindent=6pt +\multitablecolspace=12pt +\multitablelinespace=0pt + +% Macros used to set up halign preamble: +% +\let\endsetuptable\relax +\def\xendsetuptable{\endsetuptable} +\let\columnfractions\relax +\def\xcolumnfractions{\columnfractions} +\newif\ifsetpercent + +% #1 is the @columnfraction, usually a decimal number like .5, but might +% be just 1. We just use it, whatever it is. +% +\def\pickupwholefraction#1 {% + \global\advance\colcount by 1 + \expandafter\xdef\csname col\the\colcount\endcsname{#1\hsize}% + \setuptable +} + +\newcount\colcount +\def\setuptable#1{% + \def\firstarg{#1}% + \ifx\firstarg\xendsetuptable + \let\go = \relax + \else + \ifx\firstarg\xcolumnfractions + \global\setpercenttrue + \else + \ifsetpercent + \let\go\pickupwholefraction + \else + \global\advance\colcount by 1 + \setbox0=\hbox{#1\unskip\space}% Add a normal word space as a + % separator; typically that is always in the input, anyway. + \expandafter\xdef\csname col\the\colcount\endcsname{\the\wd0}% + \fi + \fi + \ifx\go\pickupwholefraction + % Put the argument back for the \pickupwholefraction call, so + % we'll always have a period there to be parsed. + \def\go{\pickupwholefraction#1}% + \else + \let\go = \setuptable + \fi% + \fi + \go +} + +% multitable-only commands. +% +% @headitem starts a heading row, which we typeset in bold. +% Assignments have to be global since we are inside the implicit group +% of an alignment entry. \everycr resets \everytab so we don't have to +% undo it ourselves. +\def\headitemfont{\b}% for people to use in the template row; not changeable +\def\headitem{% + \checkenv\multitable + \crcr + \global\everytab={\bf}% can't use \headitemfont since the parsing differs + \the\everytab % for the first item +}% +% +% A \tab used to include \hskip1sp. But then the space in a template +% line is not enough. That is bad. So let's go back to just `&' until +% we again encounter the problem the 1sp was intended to solve. +% --karl, nathan@acm.org, 20apr99. +\def\tab{\checkenv\multitable &\the\everytab}% + +% @multitable ... @end multitable definitions: +% +\newtoks\everytab % insert after every tab. +% +\envdef\multitable{% + \vskip\parskip + \startsavinginserts + % + % @item within a multitable starts a normal row. + % We use \def instead of \let so that if one of the multitable entries + % contains an @itemize, we don't choke on the \item (seen as \crcr aka + % \endtemplate) expanding \doitemize. + \def\item{\crcr}% + % + \tolerance=9500 + \hbadness=9500 + \setmultitablespacing + \parskip=\multitableparskip + \parindent=\multitableparindent + \overfullrule=0pt + \global\colcount=0 + % + \everycr = {% + \noalign{% + \global\everytab={}% + \global\colcount=0 % Reset the column counter. + % Check for saved footnotes, etc. + \checkinserts + % Keeps underfull box messages off when table breaks over pages. + %\filbreak + % Maybe so, but it also creates really weird page breaks when the + % table breaks over pages. Wouldn't \vfil be better? Wait until the + % problem manifests itself, so it can be fixed for real --karl. + }% + }% + % + \parsearg\domultitable +} +\def\domultitable#1{% + % To parse everything between @multitable and @item: + \setuptable#1 \endsetuptable + % + % This preamble sets up a generic column definition, which will + % be used as many times as user calls for columns. + % \vtop will set a single line and will also let text wrap and + % continue for many paragraphs if desired. + \halign\bgroup &% + \global\advance\colcount by 1 + \multistrut + \vtop{% + % Use the current \colcount to find the correct column width: + \hsize=\expandafter\csname col\the\colcount\endcsname + % + % In order to keep entries from bumping into each other + % we will add a \leftskip of \multitablecolspace to all columns after + % the first one. + % + % If a template has been used, we will add \multitablecolspace + % to the width of each template entry. + % + % If the user has set preamble in terms of percent of \hsize we will + % use that dimension as the width of the column, and the \leftskip + % will keep entries from bumping into each other. Table will start at + % left margin and final column will justify at right margin. + % + % Make sure we don't inherit \rightskip from the outer environment. + \rightskip=0pt + \ifnum\colcount=1 + % The first column will be indented with the surrounding text. + \advance\hsize by\leftskip + \else + \ifsetpercent \else + % If user has not set preamble in terms of percent of \hsize + % we will advance \hsize by \multitablecolspace. + \advance\hsize by \multitablecolspace + \fi + % In either case we will make \leftskip=\multitablecolspace: + \leftskip=\multitablecolspace + \fi + % Ignoring space at the beginning and end avoids an occasional spurious + % blank line, when TeX decides to break the line at the space before the + % box from the multistrut, so the strut ends up on a line by itself. + % For example: + % @multitable @columnfractions .11 .89 + % @item @code{#} + % @tab Legal holiday which is valid in major parts of the whole country. + % Is automatically provided with highlighting sequences respectively + % marking characters. + \noindent\ignorespaces##\unskip\multistrut + }\cr +} +\def\Emultitable{% + \crcr + \egroup % end the \halign + \global\setpercentfalse +} + +\def\setmultitablespacing{% + \def\multistrut{\strut}% just use the standard line spacing + % + % Compute \multitablelinespace (if not defined by user) for use in + % \multitableparskip calculation. We used define \multistrut based on + % this, but (ironically) that caused the spacing to be off. + % See bug-texinfo report from Werner Lemberg, 31 Oct 2004 12:52:20 +0100. +\ifdim\multitablelinespace=0pt +\setbox0=\vbox{X}\global\multitablelinespace=\the\baselineskip +\global\advance\multitablelinespace by-\ht0 +\fi +% Test to see if parskip is larger than space between lines of +% table. If not, do nothing. +% If so, set to same dimension as multitablelinespace. +\ifdim\multitableparskip>\multitablelinespace +\global\multitableparskip=\multitablelinespace +\global\advance\multitableparskip-7pt % to keep parskip somewhat smaller + % than skip between lines in the table. +\fi% +\ifdim\multitableparskip=0pt +\global\multitableparskip=\multitablelinespace +\global\advance\multitableparskip-7pt % to keep parskip somewhat smaller + % than skip between lines in the table. +\fi} + + +\message{conditionals,} + +% @iftex, @ifnotdocbook, @ifnothtml, @ifnotinfo, @ifnotplaintext, +% @ifnotxml always succeed. They currently do nothing; we don't +% attempt to check whether the conditionals are properly nested. But we +% have to remember that they are conditionals, so that @end doesn't +% attempt to close an environment group. +% +\def\makecond#1{% + \expandafter\let\csname #1\endcsname = \relax + \expandafter\let\csname iscond.#1\endcsname = 1 +} +\makecond{iftex} +\makecond{ifnotdocbook} +\makecond{ifnothtml} +\makecond{ifnotinfo} +\makecond{ifnotplaintext} +\makecond{ifnotxml} + +% Ignore @ignore, @ifhtml, @ifinfo, and the like. +% +\def\direntry{\doignore{direntry}} +\def\documentdescription{\doignore{documentdescription}} +\def\docbook{\doignore{docbook}} +\def\html{\doignore{html}} +\def\ifdocbook{\doignore{ifdocbook}} +\def\ifhtml{\doignore{ifhtml}} +\def\ifinfo{\doignore{ifinfo}} +\def\ifnottex{\doignore{ifnottex}} +\def\ifplaintext{\doignore{ifplaintext}} +\def\ifxml{\doignore{ifxml}} +\def\ignore{\doignore{ignore}} +\def\menu{\doignore{menu}} +\def\xml{\doignore{xml}} + +% Ignore text until a line `@end #1', keeping track of nested conditionals. +% +% A count to remember the depth of nesting. +\newcount\doignorecount + +\def\doignore#1{\begingroup + % Scan in ``verbatim'' mode: + \obeylines + \catcode`\@ = \other + \catcode`\{ = \other + \catcode`\} = \other + % + % Make sure that spaces turn into tokens that match what \doignoretext wants. + \spaceisspace + % + % Count number of #1's that we've seen. + \doignorecount = 0 + % + % Swallow text until we reach the matching `@end #1'. + \dodoignore{#1}% +} + +{ \catcode`_=11 % We want to use \_STOP_ which cannot appear in texinfo source. + \obeylines % + % + \gdef\dodoignore#1{% + % #1 contains the command name as a string, e.g., `ifinfo'. + % + % Define a command to find the next `@end #1'. + \long\def\doignoretext##1^^M@end #1{% + \doignoretextyyy##1^^M@#1\_STOP_}% + % + % And this command to find another #1 command, at the beginning of a + % line. (Otherwise, we would consider a line `@c @ifset', for + % example, to count as an @ifset for nesting.) + \long\def\doignoretextyyy##1^^M@#1##2\_STOP_{\doignoreyyy{##2}\_STOP_}% + % + % And now expand that command. + \doignoretext ^^M% + }% +} + +\def\doignoreyyy#1{% + \def\temp{#1}% + \ifx\temp\empty % Nothing found. + \let\next\doignoretextzzz + \else % Found a nested condition, ... + \advance\doignorecount by 1 + \let\next\doignoretextyyy % ..., look for another. + % If we're here, #1 ends with ^^M\ifinfo (for example). + \fi + \next #1% the token \_STOP_ is present just after this macro. +} + +% We have to swallow the remaining "\_STOP_". +% +\def\doignoretextzzz#1{% + \ifnum\doignorecount = 0 % We have just found the outermost @end. + \let\next\enddoignore + \else % Still inside a nested condition. + \advance\doignorecount by -1 + \let\next\doignoretext % Look for the next @end. + \fi + \next +} + +% Finish off ignored text. +{ \obeylines% + % Ignore anything after the last `@end #1'; this matters in verbatim + % environments, where otherwise the newline after an ignored conditional + % would result in a blank line in the output. + \gdef\enddoignore#1^^M{\endgroup\ignorespaces}% +} + + +% @set VAR sets the variable VAR to an empty value. +% @set VAR REST-OF-LINE sets VAR to the value REST-OF-LINE. +% +% Since we want to separate VAR from REST-OF-LINE (which might be +% empty), we can't just use \parsearg; we have to insert a space of our +% own to delimit the rest of the line, and then take it out again if we +% didn't need it. +% We rely on the fact that \parsearg sets \catcode`\ =10. +% +\parseargdef\set{\setyyy#1 \endsetyyy} +\def\setyyy#1 #2\endsetyyy{% + {% + \makevalueexpandable + \def\temp{#2}% + \edef\next{\gdef\makecsname{SET#1}}% + \ifx\temp\empty + \next{}% + \else + \setzzz#2\endsetzzz + \fi + }% +} +% Remove the trailing space \setxxx inserted. +\def\setzzz#1 \endsetzzz{\next{#1}} + +% @clear VAR clears (i.e., unsets) the variable VAR. +% +\parseargdef\clear{% + {% + \makevalueexpandable + \global\expandafter\let\csname SET#1\endcsname=\relax + }% +} + +% @value{foo} gets the text saved in variable foo. +\def\value{\begingroup\makevalueexpandable\valuexxx} +\def\valuexxx#1{\expandablevalue{#1}\endgroup} +{ + \catcode`\- = \active \catcode`\_ = \active + % + \gdef\makevalueexpandable{% + \let\value = \expandablevalue + % We don't want these characters active, ... + \catcode`\-=\other \catcode`\_=\other + % ..., but we might end up with active ones in the argument if + % we're called from @code, as @code{@value{foo-bar_}}, though. + % So \let them to their normal equivalents. + \let-\realdash \let_\normalunderscore + } +} + +% We have this subroutine so that we can handle at least some @value's +% properly in indexes (we call \makevalueexpandable in \indexdummies). +% The command has to be fully expandable (if the variable is set), since +% the result winds up in the index file. This means that if the +% variable's value contains other Texinfo commands, it's almost certain +% it will fail (although perhaps we could fix that with sufficient work +% to do a one-level expansion on the result, instead of complete). +% +\def\expandablevalue#1{% + \expandafter\ifx\csname SET#1\endcsname\relax + {[No value for ``#1'']}% + \message{Variable `#1', used in @value, is not set.}% + \else + \csname SET#1\endcsname + \fi +} + +% @ifset VAR ... @end ifset reads the `...' iff VAR has been defined +% with @set. +% +% To get special treatment of `@end ifset,' call \makeond and the redefine. +% +\makecond{ifset} +\def\ifset{\parsearg{\doifset{\let\next=\ifsetfail}}} +\def\doifset#1#2{% + {% + \makevalueexpandable + \let\next=\empty + \expandafter\ifx\csname SET#2\endcsname\relax + #1% If not set, redefine \next. + \fi + \expandafter + }\next +} +\def\ifsetfail{\doignore{ifset}} + +% @ifclear VAR ... @end ifclear reads the `...' iff VAR has never been +% defined with @set, or has been undefined with @clear. +% +% The `\else' inside the `\doifset' parameter is a trick to reuse the +% above code: if the variable is not set, do nothing, if it is set, +% then redefine \next to \ifclearfail. +% +\makecond{ifclear} +\def\ifclear{\parsearg{\doifset{\else \let\next=\ifclearfail}}} +\def\ifclearfail{\doignore{ifclear}} + +% @dircategory CATEGORY -- specify a category of the dir file +% which this file should belong to. Ignore this in TeX. +\let\dircategory=\comment + +% @defininfoenclose. +\let\definfoenclose=\comment + + +\message{indexing,} +% Index generation facilities + +% Define \newwrite to be identical to plain tex's \newwrite +% except not \outer, so it can be used within macros and \if's. +\edef\newwrite{\makecsname{ptexnewwrite}} + +% \newindex {foo} defines an index named foo. +% It automatically defines \fooindex such that +% \fooindex ...rest of line... puts an entry in the index foo. +% It also defines \fooindfile to be the number of the output channel for +% the file that accumulates this index. The file's extension is foo. +% The name of an index should be no more than 2 characters long +% for the sake of vms. +% +\def\newindex#1{% + \iflinks + \expandafter\newwrite \csname#1indfile\endcsname + \openout \csname#1indfile\endcsname \jobname.#1 % Open the file + \fi + \expandafter\xdef\csname#1index\endcsname{% % Define @#1index + \noexpand\doindex{#1}} +} + +% @defindex foo == \newindex{foo} +% +\def\defindex{\parsearg\newindex} + +% Define @defcodeindex, like @defindex except put all entries in @code. +% +\def\defcodeindex{\parsearg\newcodeindex} +% +\def\newcodeindex#1{% + \iflinks + \expandafter\newwrite \csname#1indfile\endcsname + \openout \csname#1indfile\endcsname \jobname.#1 + \fi + \expandafter\xdef\csname#1index\endcsname{% + \noexpand\docodeindex{#1}}% +} + + +% @synindex foo bar makes index foo feed into index bar. +% Do this instead of @defindex foo if you don't want it as a separate index. +% +% @syncodeindex foo bar similar, but put all entries made for index foo +% inside @code. +% +\def\synindex#1 #2 {\dosynindex\doindex{#1}{#2}} +\def\syncodeindex#1 #2 {\dosynindex\docodeindex{#1}{#2}} + +% #1 is \doindex or \docodeindex, #2 the index getting redefined (foo), +% #3 the target index (bar). +\def\dosynindex#1#2#3{% + % Only do \closeout if we haven't already done it, else we'll end up + % closing the target index. + \expandafter \ifx\csname donesynindex#2\endcsname \relax + % The \closeout helps reduce unnecessary open files; the limit on the + % Acorn RISC OS is a mere 16 files. + \expandafter\closeout\csname#2indfile\endcsname + \expandafter\let\csname donesynindex#2\endcsname = 1 + \fi + % redefine \fooindfile: + \expandafter\let\expandafter\temp\expandafter=\csname#3indfile\endcsname + \expandafter\let\csname#2indfile\endcsname=\temp + % redefine \fooindex: + \expandafter\xdef\csname#2index\endcsname{\noexpand#1{#3}}% +} + +% Define \doindex, the driver for all \fooindex macros. +% Argument #1 is generated by the calling \fooindex macro, +% and it is "foo", the name of the index. + +% \doindex just uses \parsearg; it calls \doind for the actual work. +% This is because \doind is more useful to call from other macros. + +% There is also \dosubind {index}{topic}{subtopic} +% which makes an entry in a two-level index such as the operation index. + +\def\doindex#1{\edef\indexname{#1}\parsearg\singleindexer} +\def\singleindexer #1{\doind{\indexname}{#1}} + +% like the previous two, but they put @code around the argument. +\def\docodeindex#1{\edef\indexname{#1}\parsearg\singlecodeindexer} +\def\singlecodeindexer #1{\doind{\indexname}{\code{#1}}} + +% Take care of Texinfo commands that can appear in an index entry. +% Since there are some commands we want to expand, and others we don't, +% we have to laboriously prevent expansion for those that we don't. +% +\def\indexdummies{% + \escapechar = `\\ % use backslash in output files. + \def\@{@}% change to @@ when we switch to @ as escape char in index files. + \def\ {\realbackslash\space }% + % + % Need these unexpandable (because we define \tt as a dummy) + % definitions when @{ or @} appear in index entry text. Also, more + % complicated, when \tex is in effect and \{ is a \delimiter again. + % We can't use \lbracecmd and \rbracecmd because texindex assumes + % braces and backslashes are used only as delimiters. Perhaps we + % should define @lbrace and @rbrace commands a la @comma. + \def\{{{\tt\char123}}% + \def\}{{\tt\char125}}% + % + % I don't entirely understand this, but when an index entry is + % generated from a macro call, the \endinput which \scanmacro inserts + % causes processing to be prematurely terminated. This is, + % apparently, because \indexsorttmp is fully expanded, and \endinput + % is an expandable command. The redefinition below makes \endinput + % disappear altogether for that purpose -- although logging shows that + % processing continues to some further point. On the other hand, it + % seems \endinput does not hurt in the printed index arg, since that + % is still getting written without apparent harm. + % + % Sample source (mac-idx3.tex, reported by Graham Percival to + % help-texinfo, 22may06): + % @macro funindex {WORD} + % @findex xyz + % @end macro + % ... + % @funindex commtest + % + % The above is not enough to reproduce the bug, but it gives the flavor. + % + % Sample whatsit resulting: + % .@write3{\entry{xyz}{@folio }{@code {xyz@endinput }}} + % + % So: + \let\endinput = \empty + % + % Do the redefinitions. + \commondummies +} + +% For the aux and toc files, @ is the escape character. So we want to +% redefine everything using @ as the escape character (instead of +% \realbackslash, still used for index files). When everything uses @, +% this will be simpler. +% +\def\atdummies{% + \def\@{@@}% + \def\ {@ }% + \let\{ = \lbraceatcmd + \let\} = \rbraceatcmd + % + % Do the redefinitions. + \commondummies + \otherbackslash +} + +% Called from \indexdummies and \atdummies. +% +\def\commondummies{% + % + % \definedummyword defines \#1 as \string\#1\space, thus effectively + % preventing its expansion. This is used only for control words, + % not control letters, because the \space would be incorrect for + % control characters, but is needed to separate the control word + % from whatever follows. + % + % For control letters, we have \definedummyletter, which omits the + % space. + % + % These can be used both for control words that take an argument and + % those that do not. If it is followed by {arg} in the input, then + % that will dutifully get written to the index (or wherever). + % + \def\definedummyword ##1{\def##1{\string##1\space}}% + \def\definedummyletter##1{\def##1{\string##1}}% + \let\definedummyaccent\definedummyletter + % + \commondummiesnofonts + % + \definedummyletter\_% + \definedummyletter\-% + % + % Non-English letters. + \definedummyword\AA + \definedummyword\AE + \definedummyword\DH + \definedummyword\L + \definedummyword\O + \definedummyword\OE + \definedummyword\TH + \definedummyword\aa + \definedummyword\ae + \definedummyword\dh + \definedummyword\exclamdown + \definedummyword\l + \definedummyword\o + \definedummyword\oe + \definedummyword\ordf + \definedummyword\ordm + \definedummyword\questiondown + \definedummyword\ss + \definedummyword\th + % + % Although these internal commands shouldn't show up, sometimes they do. + \definedummyword\bf + \definedummyword\gtr + \definedummyword\hat + \definedummyword\less + \definedummyword\sf + \definedummyword\sl + \definedummyword\tclose + \definedummyword\tt + % + \definedummyword\LaTeX + \definedummyword\TeX + % + % Assorted special characters. + \definedummyword\arrow + \definedummyword\bullet + \definedummyword\comma + \definedummyword\copyright + \definedummyword\registeredsymbol + \definedummyword\dots + \definedummyword\enddots + \definedummyword\entrybreak + \definedummyword\equiv + \definedummyword\error + \definedummyword\euro + \definedummyword\expansion + \definedummyword\geq + \definedummyword\guillemetleft + \definedummyword\guillemetright + \definedummyword\guilsinglleft + \definedummyword\guilsinglright + \definedummyword\leq + \definedummyword\minus + \definedummyword\ogonek + \definedummyword\pounds + \definedummyword\point + \definedummyword\print + \definedummyword\quotedblbase + \definedummyword\quotedblleft + \definedummyword\quotedblright + \definedummyword\quoteleft + \definedummyword\quoteright + \definedummyword\quotesinglbase + \definedummyword\result + \definedummyword\textdegree + % + % We want to disable all macros so that they are not expanded by \write. + \macrolist + % + \normalturnoffactive + % + % Handle some cases of @value -- where it does not contain any + % (non-fully-expandable) commands. + \makevalueexpandable +} + +% \commondummiesnofonts: common to \commondummies and \indexnofonts. +% +\def\commondummiesnofonts{% + % Control letters and accents. + \definedummyletter\!% + \definedummyaccent\"% + \definedummyaccent\'% + \definedummyletter\*% + \definedummyaccent\,% + \definedummyletter\.% + \definedummyletter\/% + \definedummyletter\:% + \definedummyaccent\=% + \definedummyletter\?% + \definedummyaccent\^% + \definedummyaccent\`% + \definedummyaccent\~% + \definedummyword\u + \definedummyword\v + \definedummyword\H + \definedummyword\dotaccent + \definedummyword\ogonek + \definedummyword\ringaccent + \definedummyword\tieaccent + \definedummyword\ubaraccent + \definedummyword\udotaccent + \definedummyword\dotless + % + % Texinfo font commands. + \definedummyword\b + \definedummyword\i + \definedummyword\r + \definedummyword\sansserif + \definedummyword\sc + \definedummyword\slanted + \definedummyword\t + % + % Commands that take arguments. + \definedummyword\acronym + \definedummyword\anchor + \definedummyword\cite + \definedummyword\code + \definedummyword\command + \definedummyword\dfn + \definedummyword\dmn + \definedummyword\email + \definedummyword\emph + \definedummyword\env + \definedummyword\file + \definedummyword\indicateurl + \definedummyword\kbd + \definedummyword\key + \definedummyword\math + \definedummyword\option + \definedummyword\pxref + \definedummyword\ref + \definedummyword\samp + \definedummyword\strong + \definedummyword\tie + \definedummyword\uref + \definedummyword\url + \definedummyword\var + \definedummyword\verb + \definedummyword\w + \definedummyword\xref +} + +% \indexnofonts is used when outputting the strings to sort the index +% by, and when constructing control sequence names. It eliminates all +% control sequences and just writes whatever the best ASCII sort string +% would be for a given command (usually its argument). +% +\def\indexnofonts{% + % Accent commands should become @asis. + \def\definedummyaccent##1{\let##1\asis}% + % We can just ignore other control letters. + \def\definedummyletter##1{\let##1\empty}% + % All control words become @asis by default; overrides below. + \let\definedummyword\definedummyaccent + % + \commondummiesnofonts + % + % Don't no-op \tt, since it isn't a user-level command + % and is used in the definitions of the active chars like <, >, |, etc. + % Likewise with the other plain tex font commands. + %\let\tt=\asis + % + \def\ { }% + \def\@{@}% + \def\_{\normalunderscore}% + \def\-{}% @- shouldn't affect sorting + % + % Unfortunately, texindex is not prepared to handle braces in the + % content at all. So for index sorting, we map @{ and @} to strings + % starting with |, since that ASCII character is between ASCII { and }. + \def\{{|a}% + \def\}{|b}% + % + % Non-English letters. + \def\AA{AA}% + \def\AE{AE}% + \def\DH{DZZ}% + \def\L{L}% + \def\OE{OE}% + \def\O{O}% + \def\TH{ZZZ}% + \def\aa{aa}% + \def\ae{ae}% + \def\dh{dzz}% + \def\exclamdown{!}% + \def\l{l}% + \def\oe{oe}% + \def\ordf{a}% + \def\ordm{o}% + \def\o{o}% + \def\questiondown{?}% + \def\ss{ss}% + \def\th{zzz}% + % + \def\LaTeX{LaTeX}% + \def\TeX{TeX}% + % + % Assorted special characters. + % (The following {} will end up in the sort string, but that's ok.) + \def\arrow{->}% + \def\bullet{bullet}% + \def\comma{,}% + \def\copyright{copyright}% + \def\dots{...}% + \def\enddots{...}% + \def\equiv{==}% + \def\error{error}% + \def\euro{euro}% + \def\expansion{==>}% + \def\geq{>=}% + \def\guillemetleft{<<}% + \def\guillemetright{>>}% + \def\guilsinglleft{<}% + \def\guilsinglright{>}% + \def\leq{<=}% + \def\minus{-}% + \def\point{.}% + \def\pounds{pounds}% + \def\print{-|}% + \def\quotedblbase{"}% + \def\quotedblleft{"}% + \def\quotedblright{"}% + \def\quoteleft{`}% + \def\quoteright{'}% + \def\quotesinglbase{,}% + \def\registeredsymbol{R}% + \def\result{=>}% + \def\textdegree{o}% + % + \expandafter\ifx\csname SETtxiindexlquoteignore\endcsname\relax + \else \indexlquoteignore \fi + % + % We need to get rid of all macros, leaving only the arguments (if present). + % Of course this is not nearly correct, but it is the best we can do for now. + % makeinfo does not expand macros in the argument to @deffn, which ends up + % writing an index entry, and texindex isn't prepared for an index sort entry + % that starts with \. + % + % Since macro invocations are followed by braces, we can just redefine them + % to take a single TeX argument. The case of a macro invocation that + % goes to end-of-line is not handled. + % + \macrolist +} + +% Undocumented (for FSFS 2nd ed.): @set txiindexlquoteignore makes us +% ignore left quotes in the sort term. +{\catcode`\`=\active + \gdef\indexlquoteignore{\let`=\empty}} + +\let\indexbackslash=0 %overridden during \printindex. +\let\SETmarginindex=\relax % put index entries in margin (undocumented)? + +% Most index entries go through here, but \dosubind is the general case. +% #1 is the index name, #2 is the entry text. +\def\doind#1#2{\dosubind{#1}{#2}{}} + +% Workhorse for all \fooindexes. +% #1 is name of index, #2 is stuff to put there, #3 is subentry -- +% empty if called from \doind, as we usually are (the main exception +% is with most defuns, which call us directly). +% +\def\dosubind#1#2#3{% + \iflinks + {% + % Store the main index entry text (including the third arg). + \toks0 = {#2}% + % If third arg is present, precede it with a space. + \def\thirdarg{#3}% + \ifx\thirdarg\empty \else + \toks0 = \expandafter{\the\toks0 \space #3}% + \fi + % + \edef\writeto{\csname#1indfile\endcsname}% + % + \safewhatsit\dosubindwrite + }% + \fi +} + +% Write the entry in \toks0 to the index file: +% +\def\dosubindwrite{% + % Put the index entry in the margin if desired. + \ifx\SETmarginindex\relax\else + \insert\margin{\hbox{\vrule height8pt depth3pt width0pt \the\toks0}}% + \fi + % + % Remember, we are within a group. + \indexdummies % Must do this here, since \bf, etc expand at this stage + \def\backslashcurfont{\indexbackslash}% \indexbackslash isn't defined now + % so it will be output as is; and it will print as backslash. + % + % Process the index entry with all font commands turned off, to + % get the string to sort by. + {\indexnofonts + \edef\temp{\the\toks0}% need full expansion + \xdef\indexsorttmp{\temp}% + }% + % + % Set up the complete index entry, with both the sort key and + % the original text, including any font commands. We write + % three arguments to \entry to the .?? file (four in the + % subentry case), texindex reduces to two when writing the .??s + % sorted result. + \edef\temp{% + \write\writeto{% + \string\entry{\indexsorttmp}{\noexpand\folio}{\the\toks0}}% + }% + \temp +} + +% Take care of unwanted page breaks/skips around a whatsit: +% +% If a skip is the last thing on the list now, preserve it +% by backing up by \lastskip, doing the \write, then inserting +% the skip again. Otherwise, the whatsit generated by the +% \write or \pdfdest will make \lastskip zero. The result is that +% sequences like this: +% @end defun +% @tindex whatever +% @defun ... +% will have extra space inserted, because the \medbreak in the +% start of the @defun won't see the skip inserted by the @end of +% the previous defun. +% +% But don't do any of this if we're not in vertical mode. We +% don't want to do a \vskip and prematurely end a paragraph. +% +% Avoid page breaks due to these extra skips, too. +% +% But wait, there is a catch there: +% We'll have to check whether \lastskip is zero skip. \ifdim is not +% sufficient for this purpose, as it ignores stretch and shrink parts +% of the skip. The only way seems to be to check the textual +% representation of the skip. +% +% The following is almost like \def\zeroskipmacro{0.0pt} except that +% the ``p'' and ``t'' characters have catcode \other, not 11 (letter). +% +\edef\zeroskipmacro{\expandafter\the\csname z@skip\endcsname} +% +\newskip\whatsitskip +\newcount\whatsitpenalty +% +% ..., ready, GO: +% +\def\safewhatsit#1{\ifhmode + #1% + \else + % \lastskip and \lastpenalty cannot both be nonzero simultaneously. + \whatsitskip = \lastskip + \edef\lastskipmacro{\the\lastskip}% + \whatsitpenalty = \lastpenalty + % + % If \lastskip is nonzero, that means the last item was a + % skip. And since a skip is discardable, that means this + % -\whatsitskip glue we're inserting is preceded by a + % non-discardable item, therefore it is not a potential + % breakpoint, therefore no \nobreak needed. + \ifx\lastskipmacro\zeroskipmacro + \else + \vskip-\whatsitskip + \fi + % + #1% + % + \ifx\lastskipmacro\zeroskipmacro + % If \lastskip was zero, perhaps the last item was a penalty, and + % perhaps it was >=10000, e.g., a \nobreak. In that case, we want + % to re-insert the same penalty (values >10000 are used for various + % signals); since we just inserted a non-discardable item, any + % following glue (such as a \parskip) would be a breakpoint. For example: + % @deffn deffn-whatever + % @vindex index-whatever + % Description. + % would allow a break between the index-whatever whatsit + % and the "Description." paragraph. + \ifnum\whatsitpenalty>9999 \penalty\whatsitpenalty \fi + \else + % On the other hand, if we had a nonzero \lastskip, + % this make-up glue would be preceded by a non-discardable item + % (the whatsit from the \write), so we must insert a \nobreak. + \nobreak\vskip\whatsitskip + \fi +\fi} + +% The index entry written in the file actually looks like +% \entry {sortstring}{page}{topic} +% or +% \entry {sortstring}{page}{topic}{subtopic} +% The texindex program reads in these files and writes files +% containing these kinds of lines: +% \initial {c} +% before the first topic whose initial is c +% \entry {topic}{pagelist} +% for a topic that is used without subtopics +% \primary {topic} +% for the beginning of a topic that is used with subtopics +% \secondary {subtopic}{pagelist} +% for each subtopic. + +% Define the user-accessible indexing commands +% @findex, @vindex, @kindex, @cindex. + +\def\findex {\fnindex} +\def\kindex {\kyindex} +\def\cindex {\cpindex} +\def\vindex {\vrindex} +\def\tindex {\tpindex} +\def\pindex {\pgindex} + +\def\cindexsub {\begingroup\obeylines\cindexsub} +{\obeylines % +\gdef\cindexsub "#1" #2^^M{\endgroup % +\dosubind{cp}{#2}{#1}}} + +% Define the macros used in formatting output of the sorted index material. + +% @printindex causes a particular index (the ??s file) to get printed. +% It does not print any chapter heading (usually an @unnumbered). +% +\parseargdef\printindex{\begingroup + \dobreak \chapheadingskip{10000}% + % + \smallfonts \rm + \tolerance = 9500 + \plainfrenchspacing + \everypar = {}% don't want the \kern\-parindent from indentation suppression. + % + % See if the index file exists and is nonempty. + % Change catcode of @ here so that if the index file contains + % \initial {@} + % as its first line, TeX doesn't complain about mismatched braces + % (because it thinks @} is a control sequence). + \catcode`\@ = 11 + \openin 1 \jobname.#1s + \ifeof 1 + % \enddoublecolumns gets confused if there is no text in the index, + % and it loses the chapter title and the aux file entries for the + % index. The easiest way to prevent this problem is to make sure + % there is some text. + \putwordIndexNonexistent + \else + % + % If the index file exists but is empty, then \openin leaves \ifeof + % false. We have to make TeX try to read something from the file, so + % it can discover if there is anything in it. + \read 1 to \temp + \ifeof 1 + \putwordIndexIsEmpty + \else + % Index files are almost Texinfo source, but we use \ as the escape + % character. It would be better to use @, but that's too big a change + % to make right now. + \def\indexbackslash{\backslashcurfont}% + \catcode`\\ = 0 + \escapechar = `\\ + \begindoublecolumns + \input \jobname.#1s + \enddoublecolumns + \fi + \fi + \closein 1 +\endgroup} + +% These macros are used by the sorted index file itself. +% Change them to control the appearance of the index. + +\def\initial#1{{% + % Some minor font changes for the special characters. + \let\tentt=\sectt \let\tt=\sectt \let\sf=\sectt + % + % Remove any glue we may have, we'll be inserting our own. + \removelastskip + % + % We like breaks before the index initials, so insert a bonus. + \nobreak + \vskip 0pt plus 3\baselineskip + \penalty 0 + \vskip 0pt plus -3\baselineskip + % + % Typeset the initial. Making this add up to a whole number of + % baselineskips increases the chance of the dots lining up from column + % to column. It still won't often be perfect, because of the stretch + % we need before each entry, but it's better. + % + % No shrink because it confuses \balancecolumns. + \vskip 1.67\baselineskip plus .5\baselineskip + \leftline{\secbf #1}% + % Do our best not to break after the initial. + \nobreak + \vskip .33\baselineskip plus .1\baselineskip +}} + +% \entry typesets a paragraph consisting of the text (#1), dot leaders, and +% then page number (#2) flushed to the right margin. It is used for index +% and table of contents entries. The paragraph is indented by \leftskip. +% +% A straightforward implementation would start like this: +% \def\entry#1#2{... +% But this freezes the catcodes in the argument, and can cause problems to +% @code, which sets - active. This problem was fixed by a kludge--- +% ``-'' was active throughout whole index, but this isn't really right. +% The right solution is to prevent \entry from swallowing the whole text. +% --kasal, 21nov03 +\def\entry{% + \begingroup + % + % Start a new paragraph if necessary, so our assignments below can't + % affect previous text. + \par + % + % Do not fill out the last line with white space. + \parfillskip = 0in + % + % No extra space above this paragraph. + \parskip = 0in + % + % Do not prefer a separate line ending with a hyphen to fewer lines. + \finalhyphendemerits = 0 + % + % \hangindent is only relevant when the entry text and page number + % don't both fit on one line. In that case, bob suggests starting the + % dots pretty far over on the line. Unfortunately, a large + % indentation looks wrong when the entry text itself is broken across + % lines. So we use a small indentation and put up with long leaders. + % + % \hangafter is reset to 1 (which is the value we want) at the start + % of each paragraph, so we need not do anything with that. + \hangindent = 2em + % + % When the entry text needs to be broken, just fill out the first line + % with blank space. + \rightskip = 0pt plus1fil + % + % A bit of stretch before each entry for the benefit of balancing + % columns. + \vskip 0pt plus1pt + % + % When reading the text of entry, convert explicit line breaks + % from @* into spaces. The user might give these in long section + % titles, for instance. + \def\*{\unskip\space\ignorespaces}% + \def\entrybreak{\hfil\break}% + % + % Swallow the left brace of the text (first parameter): + \afterassignment\doentry + \let\temp = +} +\def\entrybreak{\unskip\space\ignorespaces}% +\def\doentry{% + \bgroup % Instead of the swallowed brace. + \noindent + \aftergroup\finishentry + % And now comes the text of the entry. +} +\def\finishentry#1{% + % #1 is the page number. + % + % The following is kludged to not output a line of dots in the index if + % there are no page numbers. The next person who breaks this will be + % cursed by a Unix daemon. + \setbox\boxA = \hbox{#1}% + \ifdim\wd\boxA = 0pt + \ % + \else + % + % If we must, put the page number on a line of its own, and fill out + % this line with blank space. (The \hfil is overwhelmed with the + % fill leaders glue in \indexdotfill if the page number does fit.) + \hfil\penalty50 + \null\nobreak\indexdotfill % Have leaders before the page number. + % + % The `\ ' here is removed by the implicit \unskip that TeX does as + % part of (the primitive) \par. Without it, a spurious underfull + % \hbox ensues. + \ifpdf + \pdfgettoks#1.% + \ \the\toksA + \else + \ #1% + \fi + \fi + \par + \endgroup +} + +% Like plain.tex's \dotfill, except uses up at least 1 em. +\def\indexdotfill{\cleaders + \hbox{$\mathsurround=0pt \mkern1.5mu.\mkern1.5mu$}\hskip 1em plus 1fill} + +\def\primary #1{\line{#1\hfil}} + +\newskip\secondaryindent \secondaryindent=0.5cm +\def\secondary#1#2{{% + \parfillskip=0in + \parskip=0in + \hangindent=1in + \hangafter=1 + \noindent\hskip\secondaryindent\hbox{#1}\indexdotfill + \ifpdf + \pdfgettoks#2.\ \the\toksA % The page number ends the paragraph. + \else + #2 + \fi + \par +}} + +% Define two-column mode, which we use to typeset indexes. +% Adapted from the TeXbook, page 416, which is to say, +% the manmac.tex format used to print the TeXbook itself. +\catcode`\@=11 + +\newbox\partialpage +\newdimen\doublecolumnhsize + +\def\begindoublecolumns{\begingroup % ended by \enddoublecolumns + % Grab any single-column material above us. + \output = {% + % + % Here is a possibility not foreseen in manmac: if we accumulate a + % whole lot of material, we might end up calling this \output + % routine twice in a row (see the doublecol-lose test, which is + % essentially a couple of indexes with @setchapternewpage off). In + % that case we just ship out what is in \partialpage with the normal + % output routine. Generally, \partialpage will be empty when this + % runs and this will be a no-op. See the indexspread.tex test case. + \ifvoid\partialpage \else + \onepageout{\pagecontents\partialpage}% + \fi + % + \global\setbox\partialpage = \vbox{% + % Unvbox the main output page. + \unvbox\PAGE + \kern-\topskip \kern\baselineskip + }% + }% + \eject % run that output routine to set \partialpage + % + % Use the double-column output routine for subsequent pages. + \output = {\doublecolumnout}% + % + % Change the page size parameters. We could do this once outside this + % routine, in each of @smallbook, @afourpaper, and the default 8.5x11 + % format, but then we repeat the same computation. Repeating a couple + % of assignments once per index is clearly meaningless for the + % execution time, so we may as well do it in one place. + % + % First we halve the line length, less a little for the gutter between + % the columns. We compute the gutter based on the line length, so it + % changes automatically with the paper format. The magic constant + % below is chosen so that the gutter has the same value (well, +-<1pt) + % as it did when we hard-coded it. + % + % We put the result in a separate register, \doublecolumhsize, so we + % can restore it in \pagesofar, after \hsize itself has (potentially) + % been clobbered. + % + \doublecolumnhsize = \hsize + \advance\doublecolumnhsize by -.04154\hsize + \divide\doublecolumnhsize by 2 + \hsize = \doublecolumnhsize + % + % Double the \vsize as well. (We don't need a separate register here, + % since nobody clobbers \vsize.) + \vsize = 2\vsize +} + +% The double-column output routine for all double-column pages except +% the last. +% +\def\doublecolumnout{% + \splittopskip=\topskip \splitmaxdepth=\maxdepth + % Get the available space for the double columns -- the normal + % (undoubled) page height minus any material left over from the + % previous page. + \dimen@ = \vsize + \divide\dimen@ by 2 + \advance\dimen@ by -\ht\partialpage + % + % box0 will be the left-hand column, box2 the right. + \setbox0=\vsplit255 to\dimen@ \setbox2=\vsplit255 to\dimen@ + \onepageout\pagesofar + \unvbox255 + \penalty\outputpenalty +} +% +% Re-output the contents of the output page -- any previous material, +% followed by the two boxes we just split, in box0 and box2. +\def\pagesofar{% + \unvbox\partialpage + % + \hsize = \doublecolumnhsize + \wd0=\hsize \wd2=\hsize + \hbox to\pagewidth{\box0\hfil\box2}% +} +% +% All done with double columns. +\def\enddoublecolumns{% + % The following penalty ensures that the page builder is exercised + % _before_ we change the output routine. This is necessary in the + % following situation: + % + % The last section of the index consists only of a single entry. + % Before this section, \pagetotal is less than \pagegoal, so no + % break occurs before the last section starts. However, the last + % section, consisting of \initial and the single \entry, does not + % fit on the page and has to be broken off. Without the following + % penalty the page builder will not be exercised until \eject + % below, and by that time we'll already have changed the output + % routine to the \balancecolumns version, so the next-to-last + % double-column page will be processed with \balancecolumns, which + % is wrong: The two columns will go to the main vertical list, with + % the broken-off section in the recent contributions. As soon as + % the output routine finishes, TeX starts reconsidering the page + % break. The two columns and the broken-off section both fit on the + % page, because the two columns now take up only half of the page + % goal. When TeX sees \eject from below which follows the final + % section, it invokes the new output routine that we've set after + % \balancecolumns below; \onepageout will try to fit the two columns + % and the final section into the vbox of \pageheight (see + % \pagebody), causing an overfull box. + % + % Note that glue won't work here, because glue does not exercise the + % page builder, unlike penalties (see The TeXbook, pp. 280-281). + \penalty0 + % + \output = {% + % Split the last of the double-column material. Leave it on the + % current page, no automatic page break. + \balancecolumns + % + % If we end up splitting too much material for the current page, + % though, there will be another page break right after this \output + % invocation ends. Having called \balancecolumns once, we do not + % want to call it again. Therefore, reset \output to its normal + % definition right away. (We hope \balancecolumns will never be + % called on to balance too much material, but if it is, this makes + % the output somewhat more palatable.) + \global\output = {\onepageout{\pagecontents\PAGE}}% + }% + \eject + \endgroup % started in \begindoublecolumns + % + % \pagegoal was set to the doubled \vsize above, since we restarted + % the current page. We're now back to normal single-column + % typesetting, so reset \pagegoal to the normal \vsize (after the + % \endgroup where \vsize got restored). + \pagegoal = \vsize +} +% +% Called at the end of the double column material. +\def\balancecolumns{% + \setbox0 = \vbox{\unvbox255}% like \box255 but more efficient, see p.120. + \dimen@ = \ht0 + \advance\dimen@ by \topskip + \advance\dimen@ by-\baselineskip + \divide\dimen@ by 2 % target to split to + %debug\message{final 2-column material height=\the\ht0, target=\the\dimen@.}% + \splittopskip = \topskip + % Loop until we get a decent breakpoint. + {% + \vbadness = 10000 + \loop + \global\setbox3 = \copy0 + \global\setbox1 = \vsplit3 to \dimen@ + \ifdim\ht3>\dimen@ + \global\advance\dimen@ by 1pt + \repeat + }% + %debug\message{split to \the\dimen@, column heights: \the\ht1, \the\ht3.}% + \setbox0=\vbox to\dimen@{\unvbox1}% + \setbox2=\vbox to\dimen@{\unvbox3}% + % + \pagesofar +} +\catcode`\@ = \other + + +\message{sectioning,} +% Chapters, sections, etc. + +% Let's start with @part. +\outer\parseargdef\part{\partzzz{#1}} +\def\partzzz#1{% + \chapoddpage + \null + \vskip.3\vsize % move it down on the page a bit + \begingroup + \noindent \titlefonts\rmisbold #1\par % the text + \let\lastnode=\empty % no node to associate with + \writetocentry{part}{#1}{}% but put it in the toc + \headingsoff % no headline or footline on the part page + \chapoddpage + \endgroup +} + +% \unnumberedno is an oxymoron. But we count the unnumbered +% sections so that we can refer to them unambiguously in the pdf +% outlines by their "section number". We avoid collisions with chapter +% numbers by starting them at 10000. (If a document ever has 10000 +% chapters, we're in trouble anyway, I'm sure.) +\newcount\unnumberedno \unnumberedno = 10000 +\newcount\chapno +\newcount\secno \secno=0 +\newcount\subsecno \subsecno=0 +\newcount\subsubsecno \subsubsecno=0 + +% This counter is funny since it counts through charcodes of letters A, B, ... +\newcount\appendixno \appendixno = `\@ +% +% \def\appendixletter{\char\the\appendixno} +% We do the following ugly conditional instead of the above simple +% construct for the sake of pdftex, which needs the actual +% letter in the expansion, not just typeset. +% +\def\appendixletter{% + \ifnum\appendixno=`A A% + \else\ifnum\appendixno=`B B% + \else\ifnum\appendixno=`C C% + \else\ifnum\appendixno=`D D% + \else\ifnum\appendixno=`E E% + \else\ifnum\appendixno=`F F% + \else\ifnum\appendixno=`G G% + \else\ifnum\appendixno=`H H% + \else\ifnum\appendixno=`I I% + \else\ifnum\appendixno=`J J% + \else\ifnum\appendixno=`K K% + \else\ifnum\appendixno=`L L% + \else\ifnum\appendixno=`M M% + \else\ifnum\appendixno=`N N% + \else\ifnum\appendixno=`O O% + \else\ifnum\appendixno=`P P% + \else\ifnum\appendixno=`Q Q% + \else\ifnum\appendixno=`R R% + \else\ifnum\appendixno=`S S% + \else\ifnum\appendixno=`T T% + \else\ifnum\appendixno=`U U% + \else\ifnum\appendixno=`V V% + \else\ifnum\appendixno=`W W% + \else\ifnum\appendixno=`X X% + \else\ifnum\appendixno=`Y Y% + \else\ifnum\appendixno=`Z Z% + % The \the is necessary, despite appearances, because \appendixletter is + % expanded while writing the .toc file. \char\appendixno is not + % expandable, thus it is written literally, thus all appendixes come out + % with the same letter (or @) in the toc without it. + \else\char\the\appendixno + \fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi + \fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi} + +% Each @chapter defines these (using marks) as the number+name, number +% and name of the chapter. Page headings and footings can use +% these. @section does likewise. +\def\thischapter{} +\def\thischapternum{} +\def\thischaptername{} +\def\thissection{} +\def\thissectionnum{} +\def\thissectionname{} + +\newcount\absseclevel % used to calculate proper heading level +\newcount\secbase\secbase=0 % @raisesections/@lowersections modify this count + +% @raisesections: treat @section as chapter, @subsection as section, etc. +\def\raisesections{\global\advance\secbase by -1} +\let\up=\raisesections % original BFox name + +% @lowersections: treat @chapter as section, @section as subsection, etc. +\def\lowersections{\global\advance\secbase by 1} +\let\down=\lowersections % original BFox name + +% we only have subsub. +\chardef\maxseclevel = 3 +% +% A numbered section within an unnumbered changes to unnumbered too. +% To achieve this, remember the "biggest" unnum. sec. we are currently in: +\chardef\unnlevel = \maxseclevel +% +% Trace whether the current chapter is an appendix or not: +% \chapheadtype is "N" or "A", unnumbered chapters are ignored. +\def\chapheadtype{N} + +% Choose a heading macro +% #1 is heading type +% #2 is heading level +% #3 is text for heading +\def\genhead#1#2#3{% + % Compute the abs. sec. level: + \absseclevel=#2 + \advance\absseclevel by \secbase + % Make sure \absseclevel doesn't fall outside the range: + \ifnum \absseclevel < 0 + \absseclevel = 0 + \else + \ifnum \absseclevel > 3 + \absseclevel = 3 + \fi + \fi + % The heading type: + \def\headtype{#1}% + \if \headtype U% + \ifnum \absseclevel < \unnlevel + \chardef\unnlevel = \absseclevel + \fi + \else + % Check for appendix sections: + \ifnum \absseclevel = 0 + \edef\chapheadtype{\headtype}% + \else + \if \headtype A\if \chapheadtype N% + \errmessage{@appendix... within a non-appendix chapter}% + \fi\fi + \fi + % Check for numbered within unnumbered: + \ifnum \absseclevel > \unnlevel + \def\headtype{U}% + \else + \chardef\unnlevel = 3 + \fi + \fi + % Now print the heading: + \if \headtype U% + \ifcase\absseclevel + \unnumberedzzz{#3}% + \or \unnumberedseczzz{#3}% + \or \unnumberedsubseczzz{#3}% + \or \unnumberedsubsubseczzz{#3}% + \fi + \else + \if \headtype A% + \ifcase\absseclevel + \appendixzzz{#3}% + \or \appendixsectionzzz{#3}% + \or \appendixsubseczzz{#3}% + \or \appendixsubsubseczzz{#3}% + \fi + \else + \ifcase\absseclevel + \chapterzzz{#3}% + \or \seczzz{#3}% + \or \numberedsubseczzz{#3}% + \or \numberedsubsubseczzz{#3}% + \fi + \fi + \fi + \suppressfirstparagraphindent +} + +% an interface: +\def\numhead{\genhead N} +\def\apphead{\genhead A} +\def\unnmhead{\genhead U} + +% @chapter, @appendix, @unnumbered. Increment top-level counter, reset +% all lower-level sectioning counters to zero. +% +% Also set \chaplevelprefix, which we prepend to @float sequence numbers +% (e.g., figures), q.v. By default (before any chapter), that is empty. +\let\chaplevelprefix = \empty +% +\outer\parseargdef\chapter{\numhead0{#1}} % normally numhead0 calls chapterzzz +\def\chapterzzz#1{% + % section resetting is \global in case the chapter is in a group, such + % as an @include file. + \global\secno=0 \global\subsecno=0 \global\subsubsecno=0 + \global\advance\chapno by 1 + % + % Used for \float. + \gdef\chaplevelprefix{\the\chapno.}% + \resetallfloatnos + % + % \putwordChapter can contain complex things in translations. + \toks0=\expandafter{\putwordChapter}% + \message{\the\toks0 \space \the\chapno}% + % + % Write the actual heading. + \chapmacro{#1}{Ynumbered}{\the\chapno}% + % + % So @section and the like are numbered underneath this chapter. + \global\let\section = \numberedsec + \global\let\subsection = \numberedsubsec + \global\let\subsubsection = \numberedsubsubsec +} + +\outer\parseargdef\appendix{\apphead0{#1}} % normally calls appendixzzz +% +\def\appendixzzz#1{% + \global\secno=0 \global\subsecno=0 \global\subsubsecno=0 + \global\advance\appendixno by 1 + \gdef\chaplevelprefix{\appendixletter.}% + \resetallfloatnos + % + % \putwordAppendix can contain complex things in translations. + \toks0=\expandafter{\putwordAppendix}% + \message{\the\toks0 \space \appendixletter}% + % + \chapmacro{#1}{Yappendix}{\appendixletter}% + % + \global\let\section = \appendixsec + \global\let\subsection = \appendixsubsec + \global\let\subsubsection = \appendixsubsubsec +} + +% normally unnmhead0 calls unnumberedzzz: +\outer\parseargdef\unnumbered{\unnmhead0{#1}} +\def\unnumberedzzz#1{% + \global\secno=0 \global\subsecno=0 \global\subsubsecno=0 + \global\advance\unnumberedno by 1 + % + % Since an unnumbered has no number, no prefix for figures. + \global\let\chaplevelprefix = \empty + \resetallfloatnos + % + % This used to be simply \message{#1}, but TeX fully expands the + % argument to \message. Therefore, if #1 contained @-commands, TeX + % expanded them. For example, in `@unnumbered The @cite{Book}', TeX + % expanded @cite (which turns out to cause errors because \cite is meant + % to be executed, not expanded). + % + % Anyway, we don't want the fully-expanded definition of @cite to appear + % as a result of the \message, we just want `@cite' itself. We use + % \the to achieve this: TeX expands \the only once, + % simply yielding the contents of . (We also do this for + % the toc entries.) + \toks0 = {#1}% + \message{(\the\toks0)}% + % + \chapmacro{#1}{Ynothing}{\the\unnumberedno}% + % + \global\let\section = \unnumberedsec + \global\let\subsection = \unnumberedsubsec + \global\let\subsubsection = \unnumberedsubsubsec +} + +% @centerchap is like @unnumbered, but the heading is centered. +\outer\parseargdef\centerchap{% + % Well, we could do the following in a group, but that would break + % an assumption that \chapmacro is called at the outermost level. + % Thus we are safer this way: --kasal, 24feb04 + \let\centerparametersmaybe = \centerparameters + \unnmhead0{#1}% + \let\centerparametersmaybe = \relax +} + +% @top is like @unnumbered. +\let\top\unnumbered + +% Sections. +% +\outer\parseargdef\numberedsec{\numhead1{#1}} % normally calls seczzz +\def\seczzz#1{% + \global\subsecno=0 \global\subsubsecno=0 \global\advance\secno by 1 + \sectionheading{#1}{sec}{Ynumbered}{\the\chapno.\the\secno}% +} + +% normally calls appendixsectionzzz: +\outer\parseargdef\appendixsection{\apphead1{#1}} +\def\appendixsectionzzz#1{% + \global\subsecno=0 \global\subsubsecno=0 \global\advance\secno by 1 + \sectionheading{#1}{sec}{Yappendix}{\appendixletter.\the\secno}% +} +\let\appendixsec\appendixsection + +% normally calls unnumberedseczzz: +\outer\parseargdef\unnumberedsec{\unnmhead1{#1}} +\def\unnumberedseczzz#1{% + \global\subsecno=0 \global\subsubsecno=0 \global\advance\secno by 1 + \sectionheading{#1}{sec}{Ynothing}{\the\unnumberedno.\the\secno}% +} + +% Subsections. +% +% normally calls numberedsubseczzz: +\outer\parseargdef\numberedsubsec{\numhead2{#1}} +\def\numberedsubseczzz#1{% + \global\subsubsecno=0 \global\advance\subsecno by 1 + \sectionheading{#1}{subsec}{Ynumbered}{\the\chapno.\the\secno.\the\subsecno}% +} + +% normally calls appendixsubseczzz: +\outer\parseargdef\appendixsubsec{\apphead2{#1}} +\def\appendixsubseczzz#1{% + \global\subsubsecno=0 \global\advance\subsecno by 1 + \sectionheading{#1}{subsec}{Yappendix}% + {\appendixletter.\the\secno.\the\subsecno}% +} + +% normally calls unnumberedsubseczzz: +\outer\parseargdef\unnumberedsubsec{\unnmhead2{#1}} +\def\unnumberedsubseczzz#1{% + \global\subsubsecno=0 \global\advance\subsecno by 1 + \sectionheading{#1}{subsec}{Ynothing}% + {\the\unnumberedno.\the\secno.\the\subsecno}% +} + +% Subsubsections. +% +% normally numberedsubsubseczzz: +\outer\parseargdef\numberedsubsubsec{\numhead3{#1}} +\def\numberedsubsubseczzz#1{% + \global\advance\subsubsecno by 1 + \sectionheading{#1}{subsubsec}{Ynumbered}% + {\the\chapno.\the\secno.\the\subsecno.\the\subsubsecno}% +} + +% normally appendixsubsubseczzz: +\outer\parseargdef\appendixsubsubsec{\apphead3{#1}} +\def\appendixsubsubseczzz#1{% + \global\advance\subsubsecno by 1 + \sectionheading{#1}{subsubsec}{Yappendix}% + {\appendixletter.\the\secno.\the\subsecno.\the\subsubsecno}% +} + +% normally unnumberedsubsubseczzz: +\outer\parseargdef\unnumberedsubsubsec{\unnmhead3{#1}} +\def\unnumberedsubsubseczzz#1{% + \global\advance\subsubsecno by 1 + \sectionheading{#1}{subsubsec}{Ynothing}% + {\the\unnumberedno.\the\secno.\the\subsecno.\the\subsubsecno}% +} + +% These macros control what the section commands do, according +% to what kind of chapter we are in (ordinary, appendix, or unnumbered). +% Define them by default for a numbered chapter. +\let\section = \numberedsec +\let\subsection = \numberedsubsec +\let\subsubsection = \numberedsubsubsec + +% Define @majorheading, @heading and @subheading + +% NOTE on use of \vbox for chapter headings, section headings, and such: +% 1) We use \vbox rather than the earlier \line to permit +% overlong headings to fold. +% 2) \hyphenpenalty is set to 10000 because hyphenation in a +% heading is obnoxious; this forbids it. +% 3) Likewise, headings look best if no \parindent is used, and +% if justification is not attempted. Hence \raggedright. + +\def\majorheading{% + {\advance\chapheadingskip by 10pt \chapbreak }% + \parsearg\chapheadingzzz +} + +\def\chapheading{\chapbreak \parsearg\chapheadingzzz} +\def\chapheadingzzz#1{% + {\chapfonts \vbox{\hyphenpenalty=10000\tolerance=5000 + \parindent=0pt\ptexraggedright + \rmisbold #1\hfill}}% + \bigskip \par\penalty 200\relax + \suppressfirstparagraphindent +} + +% @heading, @subheading, @subsubheading. +\parseargdef\heading{\sectionheading{#1}{sec}{Yomitfromtoc}{} + \suppressfirstparagraphindent} +\parseargdef\subheading{\sectionheading{#1}{subsec}{Yomitfromtoc}{} + \suppressfirstparagraphindent} +\parseargdef\subsubheading{\sectionheading{#1}{subsubsec}{Yomitfromtoc}{} + \suppressfirstparagraphindent} + +% These macros generate a chapter, section, etc. heading only +% (including whitespace, linebreaking, etc. around it), +% given all the information in convenient, parsed form. + +% Args are the skip and penalty (usually negative) +\def\dobreak#1#2{\par\ifdim\lastskip<#1\removelastskip\penalty#2\vskip#1\fi} + +% Parameter controlling skip before chapter headings (if needed) +\newskip\chapheadingskip + +% Define plain chapter starts, and page on/off switching for it. +\def\chapbreak{\dobreak \chapheadingskip {-4000}} +\def\chappager{\par\vfill\supereject} +% Because \domark is called before \chapoddpage, the filler page will +% get the headings for the next chapter, which is wrong. But we don't +% care -- we just disable all headings on the filler page. +\def\chapoddpage{% + \chappager + \ifodd\pageno \else + \begingroup + \headingsoff + \null + \chappager + \endgroup + \fi +} + +\def\setchapternewpage #1 {\csname CHAPPAG#1\endcsname} + +\def\CHAPPAGoff{% +\global\let\contentsalignmacro = \chappager +\global\let\pchapsepmacro=\chapbreak +\global\let\pagealignmacro=\chappager} + +\def\CHAPPAGon{% +\global\let\contentsalignmacro = \chappager +\global\let\pchapsepmacro=\chappager +\global\let\pagealignmacro=\chappager +\global\def\HEADINGSon{\HEADINGSsingle}} + +\def\CHAPPAGodd{% +\global\let\contentsalignmacro = \chapoddpage +\global\let\pchapsepmacro=\chapoddpage +\global\let\pagealignmacro=\chapoddpage +\global\def\HEADINGSon{\HEADINGSdouble}} + +\CHAPPAGon + +% Chapter opening. +% +% #1 is the text, #2 is the section type (Ynumbered, Ynothing, +% Yappendix, Yomitfromtoc), #3 the chapter number. +% +% To test against our argument. +\def\Ynothingkeyword{Ynothing} +\def\Yomitfromtockeyword{Yomitfromtoc} +\def\Yappendixkeyword{Yappendix} +% +\def\chapmacro#1#2#3{% + % Insert the first mark before the heading break (see notes for \domark). + \let\prevchapterdefs=\lastchapterdefs + \let\prevsectiondefs=\lastsectiondefs + \gdef\lastsectiondefs{\gdef\thissectionname{}\gdef\thissectionnum{}% + \gdef\thissection{}}% + % + \def\temptype{#2}% + \ifx\temptype\Ynothingkeyword + \gdef\lastchapterdefs{\gdef\thischaptername{#1}\gdef\thischapternum{}% + \gdef\thischapter{\thischaptername}}% + \else\ifx\temptype\Yomitfromtockeyword + \gdef\lastchapterdefs{\gdef\thischaptername{#1}\gdef\thischapternum{}% + \gdef\thischapter{}}% + \else\ifx\temptype\Yappendixkeyword + \toks0={#1}% + \xdef\lastchapterdefs{% + \gdef\noexpand\thischaptername{\the\toks0}% + \gdef\noexpand\thischapternum{\appendixletter}% + % \noexpand\putwordAppendix avoids expanding indigestible + % commands in some of the translations. + \gdef\noexpand\thischapter{\noexpand\putwordAppendix{} + \noexpand\thischapternum: + \noexpand\thischaptername}% + }% + \else + \toks0={#1}% + \xdef\lastchapterdefs{% + \gdef\noexpand\thischaptername{\the\toks0}% + \gdef\noexpand\thischapternum{\the\chapno}% + % \noexpand\putwordChapter avoids expanding indigestible + % commands in some of the translations. + \gdef\noexpand\thischapter{\noexpand\putwordChapter{} + \noexpand\thischapternum: + \noexpand\thischaptername}% + }% + \fi\fi\fi + % + % Output the mark. Pass it through \safewhatsit, to take care of + % the preceding space. + \safewhatsit\domark + % + % Insert the chapter heading break. + \pchapsepmacro + % + % Now the second mark, after the heading break. No break points + % between here and the heading. + \let\prevchapterdefs=\lastchapterdefs + \let\prevsectiondefs=\lastsectiondefs + \domark + % + {% + \chapfonts \rmisbold + % + % Have to define \lastsection before calling \donoderef, because the + % xref code eventually uses it. On the other hand, it has to be called + % after \pchapsepmacro, or the headline will change too soon. + \gdef\lastsection{#1}% + % + % Only insert the separating space if we have a chapter/appendix + % number, and don't print the unnumbered ``number''. + \ifx\temptype\Ynothingkeyword + \setbox0 = \hbox{}% + \def\toctype{unnchap}% + \else\ifx\temptype\Yomitfromtockeyword + \setbox0 = \hbox{}% contents like unnumbered, but no toc entry + \def\toctype{omit}% + \else\ifx\temptype\Yappendixkeyword + \setbox0 = \hbox{\putwordAppendix{} #3\enspace}% + \def\toctype{app}% + \else + \setbox0 = \hbox{#3\enspace}% + \def\toctype{numchap}% + \fi\fi\fi + % + % Write the toc entry for this chapter. Must come before the + % \donoderef, because we include the current node name in the toc + % entry, and \donoderef resets it to empty. + \writetocentry{\toctype}{#1}{#3}% + % + % For pdftex, we have to write out the node definition (aka, make + % the pdfdest) after any page break, but before the actual text has + % been typeset. If the destination for the pdf outline is after the + % text, then jumping from the outline may wind up with the text not + % being visible, for instance under high magnification. + \donoderef{#2}% + % + % Typeset the actual heading. + \nobreak % Avoid page breaks at the interline glue. + \vbox{\hyphenpenalty=10000 \tolerance=5000 \parindent=0pt \ptexraggedright + \hangindent=\wd0 \centerparametersmaybe + \unhbox0 #1\par}% + }% + \nobreak\bigskip % no page break after a chapter title + \nobreak +} + +% @centerchap -- centered and unnumbered. +\let\centerparametersmaybe = \relax +\def\centerparameters{% + \advance\rightskip by 3\rightskip + \leftskip = \rightskip + \parfillskip = 0pt +} + + +% I don't think this chapter style is supported any more, so I'm not +% updating it with the new noderef stuff. We'll see. --karl, 11aug03. +% +\def\setchapterstyle #1 {\csname CHAPF#1\endcsname} +% +\def\unnchfopen #1{% +\chapoddpage {\chapfonts \vbox{\hyphenpenalty=10000\tolerance=5000 + \parindent=0pt\ptexraggedright + \rmisbold #1\hfill}}\bigskip \par\nobreak +} +\def\chfopen #1#2{\chapoddpage {\chapfonts +\vbox to 3in{\vfil \hbox to\hsize{\hfil #2} \hbox to\hsize{\hfil #1} \vfil}}% +\par\penalty 5000 % +} +\def\centerchfopen #1{% +\chapoddpage {\chapfonts \vbox{\hyphenpenalty=10000\tolerance=5000 + \parindent=0pt + \hfill {\rmisbold #1}\hfill}}\bigskip \par\nobreak +} +\def\CHAPFopen{% + \global\let\chapmacro=\chfopen + \global\let\centerchapmacro=\centerchfopen} + + +% Section titles. These macros combine the section number parts and +% call the generic \sectionheading to do the printing. +% +\newskip\secheadingskip +\def\secheadingbreak{\dobreak \secheadingskip{-1000}} + +% Subsection titles. +\newskip\subsecheadingskip +\def\subsecheadingbreak{\dobreak \subsecheadingskip{-500}} + +% Subsubsection titles. +\def\subsubsecheadingskip{\subsecheadingskip} +\def\subsubsecheadingbreak{\subsecheadingbreak} + + +% Print any size, any type, section title. +% +% #1 is the text, #2 is the section level (sec/subsec/subsubsec), #3 is +% the section type for xrefs (Ynumbered, Ynothing, Yappendix), #4 is the +% section number. +% +\def\seckeyword{sec} +% +\def\sectionheading#1#2#3#4{% + {% + \checkenv{}% should not be in an environment. + % + % Switch to the right set of fonts. + \csname #2fonts\endcsname \rmisbold + % + \def\sectionlevel{#2}% + \def\temptype{#3}% + % + % Insert first mark before the heading break (see notes for \domark). + \let\prevsectiondefs=\lastsectiondefs + \ifx\temptype\Ynothingkeyword + \ifx\sectionlevel\seckeyword + \gdef\lastsectiondefs{\gdef\thissectionname{#1}\gdef\thissectionnum{}% + \gdef\thissection{\thissectionname}}% + \fi + \else\ifx\temptype\Yomitfromtockeyword + % Don't redefine \thissection. + \else\ifx\temptype\Yappendixkeyword + \ifx\sectionlevel\seckeyword + \toks0={#1}% + \xdef\lastsectiondefs{% + \gdef\noexpand\thissectionname{\the\toks0}% + \gdef\noexpand\thissectionnum{#4}% + % \noexpand\putwordSection avoids expanding indigestible + % commands in some of the translations. + \gdef\noexpand\thissection{\noexpand\putwordSection{} + \noexpand\thissectionnum: + \noexpand\thissectionname}% + }% + \fi + \else + \ifx\sectionlevel\seckeyword + \toks0={#1}% + \xdef\lastsectiondefs{% + \gdef\noexpand\thissectionname{\the\toks0}% + \gdef\noexpand\thissectionnum{#4}% + % \noexpand\putwordSection avoids expanding indigestible + % commands in some of the translations. + \gdef\noexpand\thissection{\noexpand\putwordSection{} + \noexpand\thissectionnum: + \noexpand\thissectionname}% + }% + \fi + \fi\fi\fi + % + % Go into vertical mode. Usually we'll already be there, but we + % don't want the following whatsit to end up in a preceding paragraph + % if the document didn't happen to have a blank line. + \par + % + % Output the mark. Pass it through \safewhatsit, to take care of + % the preceding space. + \safewhatsit\domark + % + % Insert space above the heading. + \csname #2headingbreak\endcsname + % + % Now the second mark, after the heading break. No break points + % between here and the heading. + \let\prevsectiondefs=\lastsectiondefs + \domark + % + % Only insert the space after the number if we have a section number. + \ifx\temptype\Ynothingkeyword + \setbox0 = \hbox{}% + \def\toctype{unn}% + \gdef\lastsection{#1}% + \else\ifx\temptype\Yomitfromtockeyword + % for @headings -- no section number, don't include in toc, + % and don't redefine \lastsection. + \setbox0 = \hbox{}% + \def\toctype{omit}% + \let\sectionlevel=\empty + \else\ifx\temptype\Yappendixkeyword + \setbox0 = \hbox{#4\enspace}% + \def\toctype{app}% + \gdef\lastsection{#1}% + \else + \setbox0 = \hbox{#4\enspace}% + \def\toctype{num}% + \gdef\lastsection{#1}% + \fi\fi\fi + % + % Write the toc entry (before \donoderef). See comments in \chapmacro. + \writetocentry{\toctype\sectionlevel}{#1}{#4}% + % + % Write the node reference (= pdf destination for pdftex). + % Again, see comments in \chapmacro. + \donoderef{#3}% + % + % Interline glue will be inserted when the vbox is completed. + % That glue will be a valid breakpoint for the page, since it'll be + % preceded by a whatsit (usually from the \donoderef, or from the + % \writetocentry if there was no node). We don't want to allow that + % break, since then the whatsits could end up on page n while the + % section is on page n+1, thus toc/etc. are wrong. Debian bug 276000. + \nobreak + % + % Output the actual section heading. + \vbox{\hyphenpenalty=10000 \tolerance=5000 \parindent=0pt \ptexraggedright + \hangindent=\wd0 % zero if no section number + \unhbox0 #1}% + }% + % Add extra space after the heading -- half of whatever came above it. + % Don't allow stretch, though. + \kern .5 \csname #2headingskip\endcsname + % + % Do not let the kern be a potential breakpoint, as it would be if it + % was followed by glue. + \nobreak + % + % We'll almost certainly start a paragraph next, so don't let that + % glue accumulate. (Not a breakpoint because it's preceded by a + % discardable item.) However, when a paragraph is not started next + % (\startdefun, \cartouche, \center, etc.), this needs to be wiped out + % or the negative glue will cause weirdly wrong output, typically + % obscuring the section heading with something else. + \vskip-\parskip + % + % This is so the last item on the main vertical list is a known + % \penalty > 10000, so \startdefun, etc., can recognize the situation + % and do the needful. + \penalty 10001 +} + + +\message{toc,} +% Table of contents. +\newwrite\tocfile + +% Write an entry to the toc file, opening it if necessary. +% Called from @chapter, etc. +% +% Example usage: \writetocentry{sec}{Section Name}{\the\chapno.\the\secno} +% We append the current node name (if any) and page number as additional +% arguments for the \{chap,sec,...}entry macros which will eventually +% read this. The node name is used in the pdf outlines as the +% destination to jump to. +% +% We open the .toc file for writing here instead of at @setfilename (or +% any other fixed time) so that @contents can be anywhere in the document. +% But if #1 is `omit', then we don't do anything. This is used for the +% table of contents chapter openings themselves. +% +\newif\iftocfileopened +\def\omitkeyword{omit}% +% +\def\writetocentry#1#2#3{% + \edef\writetoctype{#1}% + \ifx\writetoctype\omitkeyword \else + \iftocfileopened\else + \immediate\openout\tocfile = \jobname.toc + \global\tocfileopenedtrue + \fi + % + \iflinks + {\atdummies + \edef\temp{% + \write\tocfile{@#1entry{#2}{#3}{\lastnode}{\noexpand\folio}}}% + \temp + }% + \fi + \fi + % + % Tell \shipout to create a pdf destination on each page, if we're + % writing pdf. These are used in the table of contents. We can't + % just write one on every page because the title pages are numbered + % 1 and 2 (the page numbers aren't printed), and so are the first + % two pages of the document. Thus, we'd have two destinations named + % `1', and two named `2'. + \ifpdf \global\pdfmakepagedesttrue \fi +} + + +% These characters do not print properly in the Computer Modern roman +% fonts, so we must take special care. This is more or less redundant +% with the Texinfo input format setup at the end of this file. +% +\def\activecatcodes{% + \catcode`\"=\active + \catcode`\$=\active + \catcode`\<=\active + \catcode`\>=\active + \catcode`\\=\active + \catcode`\^=\active + \catcode`\_=\active + \catcode`\|=\active + \catcode`\~=\active +} + + +% Read the toc file, which is essentially Texinfo input. +\def\readtocfile{% + \setupdatafile + \activecatcodes + \input \tocreadfilename +} + +\newskip\contentsrightmargin \contentsrightmargin=1in +\newcount\savepageno +\newcount\lastnegativepageno \lastnegativepageno = -1 + +% Prepare to read what we've written to \tocfile. +% +\def\startcontents#1{% + % If @setchapternewpage on, and @headings double, the contents should + % start on an odd page, unlike chapters. Thus, we maintain + % \contentsalignmacro in parallel with \pagealignmacro. + % From: Torbjorn Granlund + \contentsalignmacro + \immediate\closeout\tocfile + % + % Don't need to put `Contents' or `Short Contents' in the headline. + % It is abundantly clear what they are. + \chapmacro{#1}{Yomitfromtoc}{}% + % + \savepageno = \pageno + \begingroup % Set up to handle contents files properly. + \raggedbottom % Worry more about breakpoints than the bottom. + \advance\hsize by -\contentsrightmargin % Don't use the full line length. + % + % Roman numerals for page numbers. + \ifnum \pageno>0 \global\pageno = \lastnegativepageno \fi +} + +% redefined for the two-volume lispref. We always output on +% \jobname.toc even if this is redefined. +% +\def\tocreadfilename{\jobname.toc} + +% Normal (long) toc. +% +\def\contents{% + \startcontents{\putwordTOC}% + \openin 1 \tocreadfilename\space + \ifeof 1 \else + \readtocfile + \fi + \vfill \eject + \contentsalignmacro % in case @setchapternewpage odd is in effect + \ifeof 1 \else + \pdfmakeoutlines + \fi + \closein 1 + \endgroup + \lastnegativepageno = \pageno + \global\pageno = \savepageno +} + +% And just the chapters. +\def\summarycontents{% + \startcontents{\putwordShortTOC}% + % + \let\partentry = \shortpartentry + \let\numchapentry = \shortchapentry + \let\appentry = \shortchapentry + \let\unnchapentry = \shortunnchapentry + % We want a true roman here for the page numbers. + \secfonts + \let\rm=\shortcontrm \let\bf=\shortcontbf + \let\sl=\shortcontsl \let\tt=\shortconttt + \rm + \hyphenpenalty = 10000 + \advance\baselineskip by 1pt % Open it up a little. + \def\numsecentry##1##2##3##4{} + \let\appsecentry = \numsecentry + \let\unnsecentry = \numsecentry + \let\numsubsecentry = \numsecentry + \let\appsubsecentry = \numsecentry + \let\unnsubsecentry = \numsecentry + \let\numsubsubsecentry = \numsecentry + \let\appsubsubsecentry = \numsecentry + \let\unnsubsubsecentry = \numsecentry + \openin 1 \tocreadfilename\space + \ifeof 1 \else + \readtocfile + \fi + \closein 1 + \vfill \eject + \contentsalignmacro % in case @setchapternewpage odd is in effect + \endgroup + \lastnegativepageno = \pageno + \global\pageno = \savepageno +} +\let\shortcontents = \summarycontents + +% Typeset the label for a chapter or appendix for the short contents. +% The arg is, e.g., `A' for an appendix, or `3' for a chapter. +% +\def\shortchaplabel#1{% + % This space should be enough, since a single number is .5em, and the + % widest letter (M) is 1em, at least in the Computer Modern fonts. + % But use \hss just in case. + % (This space doesn't include the extra space that gets added after + % the label; that gets put in by \shortchapentry above.) + % + % We'd like to right-justify chapter numbers, but that looks strange + % with appendix letters. And right-justifying numbers and + % left-justifying letters looks strange when there is less than 10 + % chapters. Have to read the whole toc once to know how many chapters + % there are before deciding ... + \hbox to 1em{#1\hss}% +} + +% These macros generate individual entries in the table of contents. +% The first argument is the chapter or section name. +% The last argument is the page number. +% The arguments in between are the chapter number, section number, ... + +% Parts, in the main contents. Replace the part number, which doesn't +% exist, with an empty box. Let's hope all the numbers have the same width. +% Also ignore the page number, which is conventionally not printed. +\def\numeralbox{\setbox0=\hbox{8}\hbox to \wd0{\hfil}} +\def\partentry#1#2#3#4{\dochapentry{\numeralbox\labelspace#1}{}} +% +% Parts, in the short toc. +\def\shortpartentry#1#2#3#4{% + \penalty-300 + \vskip.5\baselineskip plus.15\baselineskip minus.1\baselineskip + \shortchapentry{{\bf #1}}{\numeralbox}{}{}% +} + +% Chapters, in the main contents. +\def\numchapentry#1#2#3#4{\dochapentry{#2\labelspace#1}{#4}} +% +% Chapters, in the short toc. +% See comments in \dochapentry re vbox and related settings. +\def\shortchapentry#1#2#3#4{% + \tocentry{\shortchaplabel{#2}\labelspace #1}{\doshortpageno\bgroup#4\egroup}% +} + +% Appendices, in the main contents. +% Need the word Appendix, and a fixed-size box. +% +\def\appendixbox#1{% + % We use M since it's probably the widest letter. + \setbox0 = \hbox{\putwordAppendix{} M}% + \hbox to \wd0{\putwordAppendix{} #1\hss}} +% +\def\appentry#1#2#3#4{\dochapentry{\appendixbox{#2}\labelspace#1}{#4}} + +% Unnumbered chapters. +\def\unnchapentry#1#2#3#4{\dochapentry{#1}{#4}} +\def\shortunnchapentry#1#2#3#4{\tocentry{#1}{\doshortpageno\bgroup#4\egroup}} + +% Sections. +\def\numsecentry#1#2#3#4{\dosecentry{#2\labelspace#1}{#4}} +\let\appsecentry=\numsecentry +\def\unnsecentry#1#2#3#4{\dosecentry{#1}{#4}} + +% Subsections. +\def\numsubsecentry#1#2#3#4{\dosubsecentry{#2\labelspace#1}{#4}} +\let\appsubsecentry=\numsubsecentry +\def\unnsubsecentry#1#2#3#4{\dosubsecentry{#1}{#4}} + +% And subsubsections. +\def\numsubsubsecentry#1#2#3#4{\dosubsubsecentry{#2\labelspace#1}{#4}} +\let\appsubsubsecentry=\numsubsubsecentry +\def\unnsubsubsecentry#1#2#3#4{\dosubsubsecentry{#1}{#4}} + +% This parameter controls the indentation of the various levels. +% Same as \defaultparindent. +\newdimen\tocindent \tocindent = 15pt + +% Now for the actual typesetting. In all these, #1 is the text and #2 is the +% page number. +% +% If the toc has to be broken over pages, we want it to be at chapters +% if at all possible; hence the \penalty. +\def\dochapentry#1#2{% + \penalty-300 \vskip1\baselineskip plus.33\baselineskip minus.25\baselineskip + \begingroup + \chapentryfonts + \tocentry{#1}{\dopageno\bgroup#2\egroup}% + \endgroup + \nobreak\vskip .25\baselineskip plus.1\baselineskip +} + +\def\dosecentry#1#2{\begingroup + \secentryfonts \leftskip=\tocindent + \tocentry{#1}{\dopageno\bgroup#2\egroup}% +\endgroup} + +\def\dosubsecentry#1#2{\begingroup + \subsecentryfonts \leftskip=2\tocindent + \tocentry{#1}{\dopageno\bgroup#2\egroup}% +\endgroup} + +\def\dosubsubsecentry#1#2{\begingroup + \subsubsecentryfonts \leftskip=3\tocindent + \tocentry{#1}{\dopageno\bgroup#2\egroup}% +\endgroup} + +% We use the same \entry macro as for the index entries. +\let\tocentry = \entry + +% Space between chapter (or whatever) number and the title. +\def\labelspace{\hskip1em \relax} + +\def\dopageno#1{{\rm #1}} +\def\doshortpageno#1{{\rm #1}} + +\def\chapentryfonts{\secfonts \rm} +\def\secentryfonts{\textfonts} +\def\subsecentryfonts{\textfonts} +\def\subsubsecentryfonts{\textfonts} + + +\message{environments,} +% @foo ... @end foo. + +% @tex ... @end tex escapes into raw TeX temporarily. +% One exception: @ is still an escape character, so that @end tex works. +% But \@ or @@ will get a plain @ character. + +\envdef\tex{% + \setupmarkupstyle{tex}% + \catcode `\\=0 \catcode `\{=1 \catcode `\}=2 + \catcode `\$=3 \catcode `\&=4 \catcode `\#=6 + \catcode `\^=7 \catcode `\_=8 \catcode `\~=\active \let~=\tie + \catcode `\%=14 + \catcode `\+=\other + \catcode `\"=\other + \catcode `\|=\other + \catcode `\<=\other + \catcode `\>=\other + \catcode`\`=\other + \catcode`\'=\other + \escapechar=`\\ + % + % ' is active in math mode (mathcode"8000). So reset it, and all our + % other math active characters (just in case), to plain's definitions. + \mathactive + % + \let\b=\ptexb + \let\bullet=\ptexbullet + \let\c=\ptexc + \let\,=\ptexcomma + \let\.=\ptexdot + \let\dots=\ptexdots + \let\equiv=\ptexequiv + \let\!=\ptexexclam + \let\i=\ptexi + \let\indent=\ptexindent + \let\noindent=\ptexnoindent + \let\{=\ptexlbrace + \let\+=\tabalign + \let\}=\ptexrbrace + \let\/=\ptexslash + \let\*=\ptexstar + \let\t=\ptext + \expandafter \let\csname top\endcsname=\ptextop % outer + \let\frenchspacing=\plainfrenchspacing + % + \def\endldots{\mathinner{\ldots\ldots\ldots\ldots}}% + \def\enddots{\relax\ifmmode\endldots\else$\mathsurround=0pt \endldots\,$\fi}% + \def\@{@}% +} +% There is no need to define \Etex. + +% Define @lisp ... @end lisp. +% @lisp environment forms a group so it can rebind things, +% including the definition of @end lisp (which normally is erroneous). + +% Amount to narrow the margins by for @lisp. +\newskip\lispnarrowing \lispnarrowing=0.4in + +% This is the definition that ^^M gets inside @lisp, @example, and other +% such environments. \null is better than a space, since it doesn't +% have any width. +\def\lisppar{\null\endgraf} + +% This space is always present above and below environments. +\newskip\envskipamount \envskipamount = 0pt + +% Make spacing and below environment symmetrical. We use \parskip here +% to help in doing that, since in @example-like environments \parskip +% is reset to zero; thus the \afterenvbreak inserts no space -- but the +% start of the next paragraph will insert \parskip. +% +\def\aboveenvbreak{{% + % =10000 instead of <10000 because of a special case in \itemzzz and + % \sectionheading, q.v. + \ifnum \lastpenalty=10000 \else + \advance\envskipamount by \parskip + \endgraf + \ifdim\lastskip<\envskipamount + \removelastskip + % it's not a good place to break if the last penalty was \nobreak + % or better ... + \ifnum\lastpenalty<10000 \penalty-50 \fi + \vskip\envskipamount + \fi + \fi +}} + +\let\afterenvbreak = \aboveenvbreak + +% \nonarrowing is a flag. If "set", @lisp etc don't narrow margins; it will +% also clear it, so that its embedded environments do the narrowing again. +\let\nonarrowing=\relax + +% @cartouche ... @end cartouche: draw rectangle w/rounded corners around +% environment contents. +\font\circle=lcircle10 +\newdimen\circthick +\newdimen\cartouter\newdimen\cartinner +\newskip\normbskip\newskip\normpskip\newskip\normlskip +\circthick=\fontdimen8\circle +% +\def\ctl{{\circle\char'013\hskip -6pt}}% 6pt from pl file: 1/2charwidth +\def\ctr{{\hskip 6pt\circle\char'010}} +\def\cbl{{\circle\char'012\hskip -6pt}} +\def\cbr{{\hskip 6pt\circle\char'011}} +\def\carttop{\hbox to \cartouter{\hskip\lskip + \ctl\leaders\hrule height\circthick\hfil\ctr + \hskip\rskip}} +\def\cartbot{\hbox to \cartouter{\hskip\lskip + \cbl\leaders\hrule height\circthick\hfil\cbr + \hskip\rskip}} +% +\newskip\lskip\newskip\rskip + +\envdef\cartouche{% + \ifhmode\par\fi % can't be in the midst of a paragraph. + \startsavinginserts + \lskip=\leftskip \rskip=\rightskip + \leftskip=0pt\rightskip=0pt % we want these *outside*. + \cartinner=\hsize \advance\cartinner by-\lskip + \advance\cartinner by-\rskip + \cartouter=\hsize + \advance\cartouter by 18.4pt % allow for 3pt kerns on either + % side, and for 6pt waste from + % each corner char, and rule thickness + \normbskip=\baselineskip \normpskip=\parskip \normlskip=\lineskip + % Flag to tell @lisp, etc., not to narrow margin. + \let\nonarrowing = t% + % + % If this cartouche directly follows a sectioning command, we need the + % \parskip glue (backspaced over by default) or the cartouche can + % collide with the section heading. + \ifnum\lastpenalty>10000 \vskip\parskip \penalty\lastpenalty \fi + % + \vbox\bgroup + \baselineskip=0pt\parskip=0pt\lineskip=0pt + \carttop + \hbox\bgroup + \hskip\lskip + \vrule\kern3pt + \vbox\bgroup + \kern3pt + \hsize=\cartinner + \baselineskip=\normbskip + \lineskip=\normlskip + \parskip=\normpskip + \vskip -\parskip + \comment % For explanation, see the end of def\group. +} +\def\Ecartouche{% + \ifhmode\par\fi + \kern3pt + \egroup + \kern3pt\vrule + \hskip\rskip + \egroup + \cartbot + \egroup + \checkinserts +} + + +% This macro is called at the beginning of all the @example variants, +% inside a group. +\newdimen\nonfillparindent +\def\nonfillstart{% + \aboveenvbreak + \hfuzz = 12pt % Don't be fussy + \sepspaces % Make spaces be word-separators rather than space tokens. + \let\par = \lisppar % don't ignore blank lines + \obeylines % each line of input is a line of output + \parskip = 0pt + % Turn off paragraph indentation but redefine \indent to emulate + % the normal \indent. + \nonfillparindent=\parindent + \parindent = 0pt + \let\indent\nonfillindent + % + \emergencystretch = 0pt % don't try to avoid overfull boxes + \ifx\nonarrowing\relax + \advance \leftskip by \lispnarrowing + \exdentamount=\lispnarrowing + \else + \let\nonarrowing = \relax + \fi + \let\exdent=\nofillexdent +} + +\begingroup +\obeyspaces +% We want to swallow spaces (but not other tokens) after the fake +% @indent in our nonfill-environments, where spaces are normally +% active and set to @tie, resulting in them not being ignored after +% @indent. +\gdef\nonfillindent{\futurelet\temp\nonfillindentcheck}% +\gdef\nonfillindentcheck{% +\ifx\temp % +\expandafter\nonfillindentgobble% +\else% +\leavevmode\nonfillindentbox% +\fi% +}% +\endgroup +\def\nonfillindentgobble#1{\nonfillindent} +\def\nonfillindentbox{\hbox to \nonfillparindent{\hss}} + +% If you want all examples etc. small: @set dispenvsize small. +% If you want even small examples the full size: @set dispenvsize nosmall. +% This affects the following displayed environments: +% @example, @display, @format, @lisp +% +\def\smallword{small} +\def\nosmallword{nosmall} +\let\SETdispenvsize\relax +\def\setnormaldispenv{% + \ifx\SETdispenvsize\smallword + % end paragraph for sake of leading, in case document has no blank + % line. This is redundant with what happens in \aboveenvbreak, but + % we need to do it before changing the fonts, and it's inconvenient + % to change the fonts afterward. + \ifnum \lastpenalty=10000 \else \endgraf \fi + \smallexamplefonts \rm + \fi +} +\def\setsmalldispenv{% + \ifx\SETdispenvsize\nosmallword + \else + \ifnum \lastpenalty=10000 \else \endgraf \fi + \smallexamplefonts \rm + \fi +} + +% We often define two environments, @foo and @smallfoo. +% Let's do it in one command. #1 is the env name, #2 the definition. +\def\makedispenvdef#1#2{% + \expandafter\envdef\csname#1\endcsname {\setnormaldispenv #2}% + \expandafter\envdef\csname small#1\endcsname {\setsmalldispenv #2}% + \expandafter\let\csname E#1\endcsname \afterenvbreak + \expandafter\let\csname Esmall#1\endcsname \afterenvbreak +} + +% Define two environment synonyms (#1 and #2) for an environment. +\def\maketwodispenvdef#1#2#3{% + \makedispenvdef{#1}{#3}% + \makedispenvdef{#2}{#3}% +} +% +% @lisp: indented, narrowed, typewriter font; +% @example: same as @lisp. +% +% @smallexample and @smalllisp: use smaller fonts. +% Originally contributed by Pavel@xerox. +% +\maketwodispenvdef{lisp}{example}{% + \nonfillstart + \tt\setupmarkupstyle{example}% + \let\kbdfont = \kbdexamplefont % Allow @kbd to do something special. + \gobble % eat return +} +% @display/@smalldisplay: same as @lisp except keep current font. +% +\makedispenvdef{display}{% + \nonfillstart + \gobble +} + +% @format/@smallformat: same as @display except don't narrow margins. +% +\makedispenvdef{format}{% + \let\nonarrowing = t% + \nonfillstart + \gobble +} + +% @flushleft: same as @format, but doesn't obey \SETdispenvsize. +\envdef\flushleft{% + \let\nonarrowing = t% + \nonfillstart + \gobble +} +\let\Eflushleft = \afterenvbreak + +% @flushright. +% +\envdef\flushright{% + \let\nonarrowing = t% + \nonfillstart + \advance\leftskip by 0pt plus 1fill\relax + \gobble +} +\let\Eflushright = \afterenvbreak + + +% @raggedright does more-or-less normal line breaking but no right +% justification. From plain.tex. +\envdef\raggedright{% + \rightskip0pt plus2em \spaceskip.3333em \xspaceskip.5em\relax +} +\let\Eraggedright\par + +\envdef\raggedleft{% + \parindent=0pt \leftskip0pt plus2em + \spaceskip.3333em \xspaceskip.5em \parfillskip=0pt + \hbadness=10000 % Last line will usually be underfull, so turn off + % badness reporting. +} +\let\Eraggedleft\par + +\envdef\raggedcenter{% + \parindent=0pt \rightskip0pt plus1em \leftskip0pt plus1em + \spaceskip.3333em \xspaceskip.5em \parfillskip=0pt + \hbadness=10000 % Last line will usually be underfull, so turn off + % badness reporting. +} +\let\Eraggedcenter\par + + +% @quotation does normal linebreaking (hence we can't use \nonfillstart) +% and narrows the margins. We keep \parskip nonzero in general, since +% we're doing normal filling. So, when using \aboveenvbreak and +% \afterenvbreak, temporarily make \parskip 0. +% +\makedispenvdef{quotation}{\quotationstart} +% +\def\quotationstart{% + {\parskip=0pt \aboveenvbreak}% because \aboveenvbreak inserts \parskip + \parindent=0pt + % + % @cartouche defines \nonarrowing to inhibit narrowing at next level down. + \ifx\nonarrowing\relax + \advance\leftskip by \lispnarrowing + \advance\rightskip by \lispnarrowing + \exdentamount = \lispnarrowing + \else + \let\nonarrowing = \relax + \fi + \parsearg\quotationlabel +} + +% We have retained a nonzero parskip for the environment, since we're +% doing normal filling. +% +\def\Equotation{% + \par + \ifx\quotationauthor\thisisundefined\else + % indent a bit. + \leftline{\kern 2\leftskip \sl ---\quotationauthor}% + \fi + {\parskip=0pt \afterenvbreak}% +} +\def\Esmallquotation{\Equotation} + +% If we're given an argument, typeset it in bold with a colon after. +\def\quotationlabel#1{% + \def\temp{#1}% + \ifx\temp\empty \else + {\bf #1: }% + \fi +} + + +% LaTeX-like @verbatim...@end verbatim and @verb{...} +% If we want to allow any as delimiter, +% we need the curly braces so that makeinfo sees the @verb command, eg: +% `@verbx...x' would look like the '@verbx' command. --janneke@gnu.org +% +% [Knuth]: Donald Ervin Knuth, 1996. The TeXbook. +% +% [Knuth] p.344; only we need to do the other characters Texinfo sets +% active too. Otherwise, they get lost as the first character on a +% verbatim line. +\def\dospecials{% + \do\ \do\\\do\{\do\}\do\$\do\&% + \do\#\do\^\do\^^K\do\_\do\^^A\do\%\do\~% + \do\<\do\>\do\|\do\@\do+\do\"% + % Don't do the quotes -- if we do, @set txicodequoteundirected and + % @set txicodequotebacktick will not have effect on @verb and + % @verbatim, and ?` and !` ligatures won't get disabled. + %\do\`\do\'% +} +% +% [Knuth] p. 380 +\def\uncatcodespecials{% + \def\do##1{\catcode`##1=\other}\dospecials} +% +% Setup for the @verb command. +% +% Eight spaces for a tab +\begingroup + \catcode`\^^I=\active + \gdef\tabeightspaces{\catcode`\^^I=\active\def^^I{\ \ \ \ \ \ \ \ }} +\endgroup +% +\def\setupverb{% + \tt % easiest (and conventionally used) font for verbatim + \def\par{\leavevmode\endgraf}% + \setupmarkupstyle{verb}% + \tabeightspaces + % Respect line breaks, + % print special symbols as themselves, and + % make each space count + % must do in this order: + \obeylines \uncatcodespecials \sepspaces +} + +% Setup for the @verbatim environment +% +% Real tab expansion. +\newdimen\tabw \setbox0=\hbox{\tt\space} \tabw=8\wd0 % tab amount +% +% We typeset each line of the verbatim in an \hbox, so we can handle +% tabs. The \global is in case the verbatim line starts with an accent, +% or some other command that starts with a begin-group. Otherwise, the +% entire \verbbox would disappear at the corresponding end-group, before +% it is typeset. Meanwhile, we can't have nested verbatim commands +% (can we?), so the \global won't be overwriting itself. +\newbox\verbbox +\def\starttabbox{\global\setbox\verbbox=\hbox\bgroup} +% +\begingroup + \catcode`\^^I=\active + \gdef\tabexpand{% + \catcode`\^^I=\active + \def^^I{\leavevmode\egroup + \dimen\verbbox=\wd\verbbox % the width so far, or since the previous tab + \divide\dimen\verbbox by\tabw + \multiply\dimen\verbbox by\tabw % compute previous multiple of \tabw + \advance\dimen\verbbox by\tabw % advance to next multiple of \tabw + \wd\verbbox=\dimen\verbbox \box\verbbox \starttabbox + }% + } +\endgroup + +% start the verbatim environment. +\def\setupverbatim{% + \let\nonarrowing = t% + \nonfillstart + \tt % easiest (and conventionally used) font for verbatim + % The \leavevmode here is for blank lines. Otherwise, we would + % never \starttabox and the \egroup would end verbatim mode. + \def\par{\leavevmode\egroup\box\verbbox\endgraf}% + \tabexpand + \setupmarkupstyle{verbatim}% + % Respect line breaks, + % print special symbols as themselves, and + % make each space count. + % Must do in this order: + \obeylines \uncatcodespecials \sepspaces + \everypar{\starttabbox}% +} + +% Do the @verb magic: verbatim text is quoted by unique +% delimiter characters. Before first delimiter expect a +% right brace, after last delimiter expect closing brace: +% +% \def\doverb'{'#1'}'{#1} +% +% [Knuth] p. 382; only eat outer {} +\begingroup + \catcode`[=1\catcode`]=2\catcode`\{=\other\catcode`\}=\other + \gdef\doverb{#1[\def\next##1#1}[##1\endgroup]\next] +\endgroup +% +\def\verb{\begingroup\setupverb\doverb} +% +% +% Do the @verbatim magic: define the macro \doverbatim so that +% the (first) argument ends when '@end verbatim' is reached, ie: +% +% \def\doverbatim#1@end verbatim{#1} +% +% For Texinfo it's a lot easier than for LaTeX, +% because texinfo's \verbatim doesn't stop at '\end{verbatim}': +% we need not redefine '\', '{' and '}'. +% +% Inspired by LaTeX's verbatim command set [latex.ltx] +% +\begingroup + \catcode`\ =\active + \obeylines % + % ignore everything up to the first ^^M, that's the newline at the end + % of the @verbatim input line itself. Otherwise we get an extra blank + % line in the output. + \xdef\doverbatim#1^^M#2@end verbatim{#2\noexpand\end\gobble verbatim}% + % We really want {...\end verbatim} in the body of the macro, but + % without the active space; thus we have to use \xdef and \gobble. +\endgroup +% +\envdef\verbatim{% + \setupverbatim\doverbatim +} +\let\Everbatim = \afterenvbreak + + +% @verbatiminclude FILE - insert text of file in verbatim environment. +% +\def\verbatiminclude{\parseargusing\filenamecatcodes\doverbatiminclude} +% +\def\doverbatiminclude#1{% + {% + \makevalueexpandable + \setupverbatim + \indexnofonts % Allow `@@' and other weird things in file names. + \wlog{texinfo.tex: doing @verbatiminclude of #1^^J}% + \input #1 + \afterenvbreak + }% +} + +% @copying ... @end copying. +% Save the text away for @insertcopying later. +% +% We save the uninterpreted tokens, rather than creating a box. +% Saving the text in a box would be much easier, but then all the +% typesetting commands (@smallbook, font changes, etc.) have to be done +% beforehand -- and a) we want @copying to be done first in the source +% file; b) letting users define the frontmatter in as flexible order as +% possible is very desirable. +% +\def\copying{\checkenv{}\begingroup\scanargctxt\docopying} +\def\docopying#1@end copying{\endgroup\def\copyingtext{#1}} +% +\def\insertcopying{% + \begingroup + \parindent = 0pt % paragraph indentation looks wrong on title page + \scanexp\copyingtext + \endgroup +} + + +\message{defuns,} +% @defun etc. + +\newskip\defbodyindent \defbodyindent=.4in +\newskip\defargsindent \defargsindent=50pt +\newskip\deflastargmargin \deflastargmargin=18pt +\newcount\defunpenalty + +% Start the processing of @deffn: +\def\startdefun{% + \ifnum\lastpenalty<10000 + \medbreak + \defunpenalty=10003 % Will keep this @deffn together with the + % following @def command, see below. + \else + % If there are two @def commands in a row, we'll have a \nobreak, + % which is there to keep the function description together with its + % header. But if there's nothing but headers, we need to allow a + % break somewhere. Check specifically for penalty 10002, inserted + % by \printdefunline, instead of 10000, since the sectioning + % commands also insert a nobreak penalty, and we don't want to allow + % a break between a section heading and a defun. + % + % As a further refinement, we avoid "club" headers by signalling + % with penalty of 10003 after the very first @deffn in the + % sequence (see above), and penalty of 10002 after any following + % @def command. + \ifnum\lastpenalty=10002 \penalty2000 \else \defunpenalty=10002 \fi + % + % Similarly, after a section heading, do not allow a break. + % But do insert the glue. + \medskip % preceded by discardable penalty, so not a breakpoint + \fi + % + \parindent=0in + \advance\leftskip by \defbodyindent + \exdentamount=\defbodyindent +} + +\def\dodefunx#1{% + % First, check whether we are in the right environment: + \checkenv#1% + % + % As above, allow line break if we have multiple x headers in a row. + % It's not a great place, though. + \ifnum\lastpenalty=10002 \penalty3000 \else \defunpenalty=10002 \fi + % + % And now, it's time to reuse the body of the original defun: + \expandafter\gobbledefun#1% +} +\def\gobbledefun#1\startdefun{} + +% \printdefunline \deffnheader{text} +% +\def\printdefunline#1#2{% + \begingroup + % call \deffnheader: + #1#2 \endheader + % common ending: + \interlinepenalty = 10000 + \advance\rightskip by 0pt plus 1fil\relax + \endgraf + \nobreak\vskip -\parskip + \penalty\defunpenalty % signal to \startdefun and \dodefunx + % Some of the @defun-type tags do not enable magic parentheses, + % rendering the following check redundant. But we don't optimize. + \checkparencounts + \endgroup +} + +\def\Edefun{\endgraf\medbreak} + +% \makedefun{deffn} creates \deffn, \deffnx and \Edeffn; +% the only thing remaining is to define \deffnheader. +% +\def\makedefun#1{% + \expandafter\let\csname E#1\endcsname = \Edefun + \edef\temp{\noexpand\domakedefun + \makecsname{#1}\makecsname{#1x}\makecsname{#1header}}% + \temp +} + +% \domakedefun \deffn \deffnx \deffnheader +% +% Define \deffn and \deffnx, without parameters. +% \deffnheader has to be defined explicitly. +% +\def\domakedefun#1#2#3{% + \envdef#1{% + \startdefun + \doingtypefnfalse % distinguish typed functions from all else + \parseargusing\activeparens{\printdefunline#3}% + }% + \def#2{\dodefunx#1}% + \def#3% +} + +\newif\ifdoingtypefn % doing typed function? +\newif\ifrettypeownline % typeset return type on its own line? + +% @deftypefnnewline on|off says whether the return type of typed functions +% are printed on their own line. This affects @deftypefn, @deftypefun, +% @deftypeop, and @deftypemethod. +% +\parseargdef\deftypefnnewline{% + \def\temp{#1}% + \ifx\temp\onword + \expandafter\let\csname SETtxideftypefnnl\endcsname + = \empty + \else\ifx\temp\offword + \expandafter\let\csname SETtxideftypefnnl\endcsname + = \relax + \else + \errhelp = \EMsimple + \errmessage{Unknown @txideftypefnnl value `\temp', + must be on|off}% + \fi\fi +} + +% Untyped functions: + +% @deffn category name args +\makedefun{deffn}{\deffngeneral{}} + +% @deffn category class name args +\makedefun{defop}#1 {\defopon{#1\ \putwordon}} + +% \defopon {category on}class name args +\def\defopon#1#2 {\deffngeneral{\putwordon\ \code{#2}}{#1\ \code{#2}} } + +% \deffngeneral {subind}category name args +% +\def\deffngeneral#1#2 #3 #4\endheader{% + % Remember that \dosubind{fn}{foo}{} is equivalent to \doind{fn}{foo}. + \dosubind{fn}{\code{#3}}{#1}% + \defname{#2}{}{#3}\magicamp\defunargs{#4\unskip}% +} + +% Typed functions: + +% @deftypefn category type name args +\makedefun{deftypefn}{\deftypefngeneral{}} + +% @deftypeop category class type name args +\makedefun{deftypeop}#1 {\deftypeopon{#1\ \putwordon}} + +% \deftypeopon {category on}class type name args +\def\deftypeopon#1#2 {\deftypefngeneral{\putwordon\ \code{#2}}{#1\ \code{#2}} } + +% \deftypefngeneral {subind}category type name args +% +\def\deftypefngeneral#1#2 #3 #4 #5\endheader{% + \dosubind{fn}{\code{#4}}{#1}% + \doingtypefntrue + \defname{#2}{#3}{#4}\defunargs{#5\unskip}% +} + +% Typed variables: + +% @deftypevr category type var args +\makedefun{deftypevr}{\deftypecvgeneral{}} + +% @deftypecv category class type var args +\makedefun{deftypecv}#1 {\deftypecvof{#1\ \putwordof}} + +% \deftypecvof {category of}class type var args +\def\deftypecvof#1#2 {\deftypecvgeneral{\putwordof\ \code{#2}}{#1\ \code{#2}} } + +% \deftypecvgeneral {subind}category type var args +% +\def\deftypecvgeneral#1#2 #3 #4 #5\endheader{% + \dosubind{vr}{\code{#4}}{#1}% + \defname{#2}{#3}{#4}\defunargs{#5\unskip}% +} + +% Untyped variables: + +% @defvr category var args +\makedefun{defvr}#1 {\deftypevrheader{#1} {} } + +% @defcv category class var args +\makedefun{defcv}#1 {\defcvof{#1\ \putwordof}} + +% \defcvof {category of}class var args +\def\defcvof#1#2 {\deftypecvof{#1}#2 {} } + +% Types: + +% @deftp category name args +\makedefun{deftp}#1 #2 #3\endheader{% + \doind{tp}{\code{#2}}% + \defname{#1}{}{#2}\defunargs{#3\unskip}% +} + +% Remaining @defun-like shortcuts: +\makedefun{defun}{\deffnheader{\putwordDeffunc} } +\makedefun{defmac}{\deffnheader{\putwordDefmac} } +\makedefun{defspec}{\deffnheader{\putwordDefspec} } +\makedefun{deftypefun}{\deftypefnheader{\putwordDeffunc} } +\makedefun{defvar}{\defvrheader{\putwordDefvar} } +\makedefun{defopt}{\defvrheader{\putwordDefopt} } +\makedefun{deftypevar}{\deftypevrheader{\putwordDefvar} } +\makedefun{defmethod}{\defopon\putwordMethodon} +\makedefun{deftypemethod}{\deftypeopon\putwordMethodon} +\makedefun{defivar}{\defcvof\putwordInstanceVariableof} +\makedefun{deftypeivar}{\deftypecvof\putwordInstanceVariableof} + +% \defname, which formats the name of the @def (not the args). +% #1 is the category, such as "Function". +% #2 is the return type, if any. +% #3 is the function name. +% +% We are followed by (but not passed) the arguments, if any. +% +\def\defname#1#2#3{% + \par + % Get the values of \leftskip and \rightskip as they were outside the @def... + \advance\leftskip by -\defbodyindent + % + % Determine if we are typesetting the return type of a typed function + % on a line by itself. + \rettypeownlinefalse + \ifdoingtypefn % doing a typed function specifically? + % then check user option for putting return type on its own line: + \expandafter\ifx\csname SETtxideftypefnnl\endcsname\relax \else + \rettypeownlinetrue + \fi + \fi + % + % How we'll format the category name. Putting it in brackets helps + % distinguish it from the body text that may end up on the next line + % just below it. + \def\temp{#1}% + \setbox0=\hbox{\kern\deflastargmargin \ifx\temp\empty\else [\rm\temp]\fi} + % + % Figure out line sizes for the paragraph shape. We'll always have at + % least two. + \tempnum = 2 + % + % The first line needs space for \box0; but if \rightskip is nonzero, + % we need only space for the part of \box0 which exceeds it: + \dimen0=\hsize \advance\dimen0 by -\wd0 \advance\dimen0 by \rightskip + % + % If doing a return type on its own line, we'll have another line. + \ifrettypeownline + \advance\tempnum by 1 + \def\maybeshapeline{0in \hsize}% + \else + \def\maybeshapeline{}% + \fi + % + % The continuations: + \dimen2=\hsize \advance\dimen2 by -\defargsindent + % + % The final paragraph shape: + \parshape \tempnum 0in \dimen0 \maybeshapeline \defargsindent \dimen2 + % + % Put the category name at the right margin. + \noindent + \hbox to 0pt{% + \hfil\box0 \kern-\hsize + % \hsize has to be shortened this way: + \kern\leftskip + % Intentionally do not respect \rightskip, since we need the space. + }% + % + % Allow all lines to be underfull without complaint: + \tolerance=10000 \hbadness=10000 + \exdentamount=\defbodyindent + {% + % defun fonts. We use typewriter by default (used to be bold) because: + % . we're printing identifiers, they should be in tt in principle. + % . in languages with many accents, such as Czech or French, it's + % common to leave accents off identifiers. The result looks ok in + % tt, but exceedingly strange in rm. + % . we don't want -- and --- to be treated as ligatures. + % . this still does not fix the ?` and !` ligatures, but so far no + % one has made identifiers using them :). + \df \tt + \def\temp{#2}% text of the return type + \ifx\temp\empty\else + \tclose{\temp}% typeset the return type + \ifrettypeownline + % put return type on its own line; prohibit line break following: + \hfil\vadjust{\nobreak}\break + \else + \space % type on same line, so just followed by a space + \fi + \fi % no return type + #3% output function name + }% + {\rm\enskip}% hskip 0.5 em of \tenrm + % + \boldbrax + % arguments will be output next, if any. +} + +% Print arguments in slanted roman (not ttsl), inconsistently with using +% tt for the name. This is because literal text is sometimes needed in +% the argument list (groff manual), and ttsl and tt are not very +% distinguishable. Prevent hyphenation at `-' chars. +% +\def\defunargs#1{% + % use sl by default (not ttsl), + % tt for the names. + \df \sl \hyphenchar\font=0 + % + % On the other hand, if an argument has two dashes (for instance), we + % want a way to get ttsl. Let's try @var for that. + \def\var##1{{\setupmarkupstyle{var}\ttslanted{##1}}}% + #1% + \sl\hyphenchar\font=45 +} + +% We want ()&[] to print specially on the defun line. +% +\def\activeparens{% + \catcode`\(=\active \catcode`\)=\active + \catcode`\[=\active \catcode`\]=\active + \catcode`\&=\active +} + +% Make control sequences which act like normal parenthesis chars. +\let\lparen = ( \let\rparen = ) + +% Be sure that we always have a definition for `(', etc. For example, +% if the fn name has parens in it, \boldbrax will not be in effect yet, +% so TeX would otherwise complain about undefined control sequence. +{ + \activeparens + \global\let(=\lparen \global\let)=\rparen + \global\let[=\lbrack \global\let]=\rbrack + \global\let& = \& + + \gdef\boldbrax{\let(=\opnr\let)=\clnr\let[=\lbrb\let]=\rbrb} + \gdef\magicamp{\let&=\amprm} +} + +\newcount\parencount + +% If we encounter &foo, then turn on ()-hacking afterwards +\newif\ifampseen +\def\amprm#1 {\ampseentrue{\bf\ }} + +\def\parenfont{% + \ifampseen + % At the first level, print parens in roman, + % otherwise use the default font. + \ifnum \parencount=1 \rm \fi + \else + % The \sf parens (in \boldbrax) actually are a little bolder than + % the contained text. This is especially needed for [ and ] . + \sf + \fi +} +\def\infirstlevel#1{% + \ifampseen + \ifnum\parencount=1 + #1% + \fi + \fi +} +\def\bfafterword#1 {#1 \bf} + +\def\opnr{% + \global\advance\parencount by 1 + {\parenfont(}% + \infirstlevel \bfafterword +} +\def\clnr{% + {\parenfont)}% + \infirstlevel \sl + \global\advance\parencount by -1 +} + +\newcount\brackcount +\def\lbrb{% + \global\advance\brackcount by 1 + {\bf[}% +} +\def\rbrb{% + {\bf]}% + \global\advance\brackcount by -1 +} + +\def\checkparencounts{% + \ifnum\parencount=0 \else \badparencount \fi + \ifnum\brackcount=0 \else \badbrackcount \fi +} +% these should not use \errmessage; the glibc manual, at least, actually +% has such constructs (when documenting function pointers). +\def\badparencount{% + \message{Warning: unbalanced parentheses in @def...}% + \global\parencount=0 +} +\def\badbrackcount{% + \message{Warning: unbalanced square brackets in @def...}% + \global\brackcount=0 +} + + +\message{macros,} +% @macro. + +% To do this right we need a feature of e-TeX, \scantokens, +% which we arrange to emulate with a temporary file in ordinary TeX. +\ifx\eTeXversion\thisisundefined + \newwrite\macscribble + \def\scantokens#1{% + \toks0={#1}% + \immediate\openout\macscribble=\jobname.tmp + \immediate\write\macscribble{\the\toks0}% + \immediate\closeout\macscribble + \input \jobname.tmp + } +\fi + +\def\scanmacro#1{\begingroup + \newlinechar`\^^M + \let\xeatspaces\eatspaces + % + % Undo catcode changes of \startcontents and \doprintindex + % When called from @insertcopying or (short)caption, we need active + % backslash to get it printed correctly. Previously, we had + % \catcode`\\=\other instead. We'll see whether a problem appears + % with macro expansion. --kasal, 19aug04 + \catcode`\@=0 \catcode`\\=\active \escapechar=`\@ + % + % ... and for \example: + \spaceisspace + % + % The \empty here causes a following catcode 5 newline to be eaten as + % part of reading whitespace after a control sequence. It does not + % eat a catcode 13 newline. There's no good way to handle the two + % cases (untried: maybe e-TeX's \everyeof could help, though plain TeX + % would then have different behavior). See the Macro Details node in + % the manual for the workaround we recommend for macros and + % line-oriented commands. + % + \scantokens{#1\empty}% +\endgroup} + +\def\scanexp#1{% + \edef\temp{\noexpand\scanmacro{#1}}% + \temp +} + +\newcount\paramno % Count of parameters +\newtoks\macname % Macro name +\newif\ifrecursive % Is it recursive? + +% List of all defined macros in the form +% \definedummyword\macro1\definedummyword\macro2... +% Currently is also contains all @aliases; the list can be split +% if there is a need. +\def\macrolist{} + +% Add the macro to \macrolist +\def\addtomacrolist#1{\expandafter \addtomacrolistxxx \csname#1\endcsname} +\def\addtomacrolistxxx#1{% + \toks0 = \expandafter{\macrolist\definedummyword#1}% + \xdef\macrolist{\the\toks0}% +} + +% Utility routines. +% This does \let #1 = #2, with \csnames; that is, +% \let \csname#1\endcsname = \csname#2\endcsname +% (except of course we have to play expansion games). +% +\def\cslet#1#2{% + \expandafter\let + \csname#1\expandafter\endcsname + \csname#2\endcsname +} + +% Trim leading and trailing spaces off a string. +% Concepts from aro-bend problem 15 (see CTAN). +{\catcode`\@=11 +\gdef\eatspaces #1{\expandafter\trim@\expandafter{#1 }} +\gdef\trim@ #1{\trim@@ @#1 @ #1 @ @@} +\gdef\trim@@ #1@ #2@ #3@@{\trim@@@\empty #2 @} +\def\unbrace#1{#1} +\unbrace{\gdef\trim@@@ #1 } #2@{#1} +} + +% Trim a single trailing ^^M off a string. +{\catcode`\^^M=\other \catcode`\Q=3% +\gdef\eatcr #1{\eatcra #1Q^^MQ}% +\gdef\eatcra#1^^MQ{\eatcrb#1Q}% +\gdef\eatcrb#1Q#2Q{#1}% +} + +% Macro bodies are absorbed as an argument in a context where +% all characters are catcode 10, 11 or 12, except \ which is active +% (as in normal texinfo). It is necessary to change the definition of \ +% to recognize macro arguments; this is the job of \mbodybackslash. +% +% Non-ASCII encodings make 8-bit characters active, so un-activate +% them to avoid their expansion. Must do this non-globally, to +% confine the change to the current group. +% +% It's necessary to have hard CRs when the macro is executed. This is +% done by making ^^M (\endlinechar) catcode 12 when reading the macro +% body, and then making it the \newlinechar in \scanmacro. +% +\def\scanctxt{% used as subroutine + \catcode`\"=\other + \catcode`\+=\other + \catcode`\<=\other + \catcode`\>=\other + \catcode`\@=\other + \catcode`\^=\other + \catcode`\_=\other + \catcode`\|=\other + \catcode`\~=\other + \ifx\declaredencoding\ascii \else \setnonasciicharscatcodenonglobal\other \fi +} + +\def\scanargctxt{% used for copying and captions, not macros. + \scanctxt + \catcode`\\=\other + \catcode`\^^M=\other +} + +\def\macrobodyctxt{% used for @macro definitions + \scanctxt + \catcode`\{=\other + \catcode`\}=\other + \catcode`\^^M=\other + \usembodybackslash +} + +\def\macroargctxt{% used when scanning invocations + \scanctxt + \catcode`\\=0 +} +% why catcode 0 for \ in the above? To recognize \\ \{ \} as "escapes" +% for the single characters \ { }. Thus, we end up with the "commands" +% that would be written @\ @{ @} in a Texinfo document. +% +% We already have @{ and @}. For @\, we define it here, and only for +% this purpose, to produce a typewriter backslash (so, the @\ that we +% define for @math can't be used with @macro calls): +% +\def\\{\normalbackslash}% +% +% We would like to do this for \, too, since that is what makeinfo does. +% But it is not possible, because Texinfo already has a command @, for a +% cedilla accent. Documents must use @comma{} instead. +% +% \anythingelse will almost certainly be an error of some kind. + + +% \mbodybackslash is the definition of \ in @macro bodies. +% It maps \foo\ => \csname macarg.foo\endcsname => #N +% where N is the macro parameter number. +% We define \csname macarg.\endcsname to be \realbackslash, so +% \\ in macro replacement text gets you a backslash. +% +{\catcode`@=0 @catcode`@\=@active + @gdef@usembodybackslash{@let\=@mbodybackslash} + @gdef@mbodybackslash#1\{@csname macarg.#1@endcsname} +} +\expandafter\def\csname macarg.\endcsname{\realbackslash} + +\def\margbackslash#1{\char`\#1 } + +\def\macro{\recursivefalse\parsearg\macroxxx} +\def\rmacro{\recursivetrue\parsearg\macroxxx} + +\def\macroxxx#1{% + \getargs{#1}% now \macname is the macname and \argl the arglist + \ifx\argl\empty % no arguments + \paramno=0\relax + \else + \expandafter\parsemargdef \argl;% + \if\paramno>256\relax + \ifx\eTeXversion\thisisundefined + \errhelp = \EMsimple + \errmessage{You need eTeX to compile a file with macros with more than 256 arguments} + \fi + \fi + \fi + \if1\csname ismacro.\the\macname\endcsname + \message{Warning: redefining \the\macname}% + \else + \expandafter\ifx\csname \the\macname\endcsname \relax + \else \errmessage{Macro name \the\macname\space already defined}\fi + \global\cslet{macsave.\the\macname}{\the\macname}% + \global\expandafter\let\csname ismacro.\the\macname\endcsname=1% + \addtomacrolist{\the\macname}% + \fi + \begingroup \macrobodyctxt + \ifrecursive \expandafter\parsermacbody + \else \expandafter\parsemacbody + \fi} + +\parseargdef\unmacro{% + \if1\csname ismacro.#1\endcsname + \global\cslet{#1}{macsave.#1}% + \global\expandafter\let \csname ismacro.#1\endcsname=0% + % Remove the macro name from \macrolist: + \begingroup + \expandafter\let\csname#1\endcsname \relax + \let\definedummyword\unmacrodo + \xdef\macrolist{\macrolist}% + \endgroup + \else + \errmessage{Macro #1 not defined}% + \fi +} + +% Called by \do from \dounmacro on each macro. The idea is to omit any +% macro definitions that have been changed to \relax. +% +\def\unmacrodo#1{% + \ifx #1\relax + % remove this + \else + \noexpand\definedummyword \noexpand#1% + \fi +} + +% This makes use of the obscure feature that if the last token of a +% is #, then the preceding argument is delimited by +% an opening brace, and that opening brace is not consumed. +\def\getargs#1{\getargsxxx#1{}} +\def\getargsxxx#1#{\getmacname #1 \relax\getmacargs} +\def\getmacname#1 #2\relax{\macname={#1}} +\def\getmacargs#1{\def\argl{#1}} + +% For macro processing make @ a letter so that we can make Texinfo private macro names. +\edef\texiatcatcode{\the\catcode`\@} +\catcode `@=11\relax + +% Parse the optional {params} list. Set up \paramno and \paramlist +% so \defmacro knows what to do. Define \macarg.BLAH for each BLAH +% in the params list to some hook where the argument si to be expanded. If +% there are less than 10 arguments that hook is to be replaced by ##N where N +% is the position in that list, that is to say the macro arguments are to be +% defined `a la TeX in the macro body. +% +% That gets used by \mbodybackslash (above). +% +% We need to get `macro parameter char #' into several definitions. +% The technique used is stolen from LaTeX: let \hash be something +% unexpandable, insert that wherever you need a #, and then redefine +% it to # just before using the token list produced. +% +% The same technique is used to protect \eatspaces till just before +% the macro is used. +% +% If there are 10 or more arguments, a different technique is used, where the +% hook remains in the body, and when macro is to be expanded the body is +% processed again to replace the arguments. +% +% In that case, the hook is \the\toks N-1, and we simply set \toks N-1 to the +% argument N value and then \edef the body (nothing else will expand because of +% the catcode regime underwhich the body was input). +% +% If you compile with TeX (not eTeX), and you have macros with 10 or more +% arguments, you need that no macro has more than 256 arguments, otherwise an +% error is produced. +\def\parsemargdef#1;{% + \paramno=0\def\paramlist{}% + \let\hash\relax + \let\xeatspaces\relax + \parsemargdefxxx#1,;,% + % In case that there are 10 or more arguments we parse again the arguments + % list to set new definitions for the \macarg.BLAH macros corresponding to + % each BLAH argument. It was anyhow needed to parse already once this list + % in order to count the arguments, and as macros with at most 9 arguments + % are by far more frequent than macro with 10 or more arguments, defining + % twice the \macarg.BLAH macros does not cost too much processing power. + \ifnum\paramno<10\relax\else + \paramno0\relax + \parsemmanyargdef@@#1,;,% 10 or more arguments + \fi +} +\def\parsemargdefxxx#1,{% + \if#1;\let\next=\relax + \else \let\next=\parsemargdefxxx + \advance\paramno by 1 + \expandafter\edef\csname macarg.\eatspaces{#1}\endcsname + {\xeatspaces{\hash\the\paramno}}% + \edef\paramlist{\paramlist\hash\the\paramno,}% + \fi\next} + +\def\parsemmanyargdef@@#1,{% + \if#1;\let\next=\relax + \else + \let\next=\parsemmanyargdef@@ + \edef\tempb{\eatspaces{#1}}% + \expandafter\def\expandafter\tempa + \expandafter{\csname macarg.\tempb\endcsname}% + % Note that we need some extra \noexpand\noexpand, this is because we + % don't want \the to be expanded in the \parsermacbody as it uses an + % \xdef . + \expandafter\edef\tempa + {\noexpand\noexpand\noexpand\the\toks\the\paramno}% + \advance\paramno by 1\relax + \fi\next} + +% These two commands read recursive and nonrecursive macro bodies. +% (They're different since rec and nonrec macros end differently.) +% + +\catcode `\@\texiatcatcode +\long\def\parsemacbody#1@end macro% +{\xdef\temp{\eatcr{#1}}\endgroup\defmacro}% +\long\def\parsermacbody#1@end rmacro% +{\xdef\temp{\eatcr{#1}}\endgroup\defmacro}% +\catcode `\@=11\relax + +\let\endargs@\relax +\let\nil@\relax +\def\nilm@{\nil@}% +\long\def\nillm@{\nil@}% + +% This macro is expanded during the Texinfo macro expansion, not during its +% definition. It gets all the arguments values and assigns them to macros +% macarg.ARGNAME +% +% #1 is the macro name +% #2 is the list of argument names +% #3 is the list of argument values +\def\getargvals@#1#2#3{% + \def\macargdeflist@{}% + \def\saveparamlist@{#2}% Need to keep a copy for parameter expansion. + \def\paramlist{#2,\nil@}% + \def\macroname{#1}% + \begingroup + \macroargctxt + \def\argvaluelist{#3,\nil@}% + \def\@tempa{#3}% + \ifx\@tempa\empty + \setemptyargvalues@ + \else + \getargvals@@ + \fi +} + +% +\def\getargvals@@{% + \ifx\paramlist\nilm@ + % Some sanity check needed here that \argvaluelist is also empty. + \ifx\argvaluelist\nillm@ + \else + \errhelp = \EMsimple + \errmessage{Too many arguments in macro `\macroname'!}% + \fi + \let\next\macargexpandinbody@ + \else + \ifx\argvaluelist\nillm@ + % No more arguments values passed to macro. Set remaining named-arg + % macros to empty. + \let\next\setemptyargvalues@ + \else + % pop current arg name into \@tempb + \def\@tempa##1{\pop@{\@tempb}{\paramlist}##1\endargs@}% + \expandafter\@tempa\expandafter{\paramlist}% + % pop current argument value into \@tempc + \def\@tempa##1{\longpop@{\@tempc}{\argvaluelist}##1\endargs@}% + \expandafter\@tempa\expandafter{\argvaluelist}% + % Here \@tempb is the current arg name and \@tempc is the current arg value. + % First place the new argument macro definition into \@tempd + \expandafter\macname\expandafter{\@tempc}% + \expandafter\let\csname macarg.\@tempb\endcsname\relax + \expandafter\def\expandafter\@tempe\expandafter{% + \csname macarg.\@tempb\endcsname}% + \edef\@tempd{\long\def\@tempe{\the\macname}}% + \push@\@tempd\macargdeflist@ + \let\next\getargvals@@ + \fi + \fi + \next +} + +\def\push@#1#2{% + \expandafter\expandafter\expandafter\def + \expandafter\expandafter\expandafter#2% + \expandafter\expandafter\expandafter{% + \expandafter#1#2}% +} + +% Replace arguments by their values in the macro body, and place the result +% in macro \@tempa +\def\macvalstoargs@{% + % To do this we use the property that token registers that are \the'ed + % within an \edef expand only once. So we are going to place all argument + % values into respective token registers. + % + % First we save the token context, and initialize argument numbering. + \begingroup + \paramno0\relax + % Then, for each argument number #N, we place the corresponding argument + % value into a new token list register \toks#N + \expandafter\putargsintokens@\saveparamlist@,;,% + % Then, we expand the body so that argument are replaced by their + % values. The trick for values not to be expanded themselves is that they + % are within tokens and that tokens expand only once in an \edef . + \edef\@tempc{\csname mac.\macroname .body\endcsname}% + % Now we restore the token stack pointer to free the token list registers + % which we have used, but we make sure that expanded body is saved after + % group. + \expandafter + \endgroup + \expandafter\def\expandafter\@tempa\expandafter{\@tempc}% + } + +\def\macargexpandinbody@{% + %% Define the named-macro outside of this group and then close this group. + \expandafter + \endgroup + \macargdeflist@ + % First the replace in body the macro arguments by their values, the result + % is in \@tempa . + \macvalstoargs@ + % Then we point at the \norecurse or \gobble (for recursive) macro value + % with \@tempb . + \expandafter\let\expandafter\@tempb\csname mac.\macroname .recurse\endcsname + % Depending on whether it is recursive or not, we need some tailing + % \egroup . + \ifx\@tempb\gobble + \let\@tempc\relax + \else + \let\@tempc\egroup + \fi + % And now we do the real job: + \edef\@tempd{\noexpand\@tempb{\macroname}\noexpand\scanmacro{\@tempa}\@tempc}% + \@tempd +} + +\def\putargsintokens@#1,{% + \if#1;\let\next\relax + \else + \let\next\putargsintokens@ + % First we allocate the new token list register, and give it a temporary + % alias \@tempb . + \toksdef\@tempb\the\paramno + % Then we place the argument value into that token list register. + \expandafter\let\expandafter\@tempa\csname macarg.#1\endcsname + \expandafter\@tempb\expandafter{\@tempa}% + \advance\paramno by 1\relax + \fi + \next +} + +% Save the token stack pointer into macro #1 +\def\texisavetoksstackpoint#1{\edef#1{\the\@cclvi}} +% Restore the token stack pointer from number in macro #1 +\def\texirestoretoksstackpoint#1{\expandafter\mathchardef\expandafter\@cclvi#1\relax} +% newtoks that can be used non \outer . +\def\texinonouternewtoks{\alloc@ 5\toks \toksdef \@cclvi} + +% Tailing missing arguments are set to empty +\def\setemptyargvalues@{% + \ifx\paramlist\nilm@ + \let\next\macargexpandinbody@ + \else + \expandafter\setemptyargvaluesparser@\paramlist\endargs@ + \let\next\setemptyargvalues@ + \fi + \next +} + +\def\setemptyargvaluesparser@#1,#2\endargs@{% + \expandafter\def\expandafter\@tempa\expandafter{% + \expandafter\def\csname macarg.#1\endcsname{}}% + \push@\@tempa\macargdeflist@ + \def\paramlist{#2}% +} + +% #1 is the element target macro +% #2 is the list macro +% #3,#4\endargs@ is the list value +\def\pop@#1#2#3,#4\endargs@{% + \def#1{#3}% + \def#2{#4}% +} +\long\def\longpop@#1#2#3,#4\endargs@{% + \long\def#1{#3}% + \long\def#2{#4}% +} + +% This defines a Texinfo @macro. There are eight cases: recursive and +% nonrecursive macros of zero, one, up to nine, and many arguments. +% Much magic with \expandafter here. +% \xdef is used so that macro definitions will survive the file +% they're defined in; @include reads the file inside a group. +% +\def\defmacro{% + \let\hash=##% convert placeholders to macro parameter chars + \ifrecursive + \ifcase\paramno + % 0 + \expandafter\xdef\csname\the\macname\endcsname{% + \noexpand\scanmacro{\temp}}% + \or % 1 + \expandafter\xdef\csname\the\macname\endcsname{% + \bgroup\noexpand\macroargctxt + \noexpand\braceorline + \expandafter\noexpand\csname\the\macname xxx\endcsname}% + \expandafter\xdef\csname\the\macname xxx\endcsname##1{% + \egroup\noexpand\scanmacro{\temp}}% + \else + \ifnum\paramno<10\relax % at most 9 + \expandafter\xdef\csname\the\macname\endcsname{% + \bgroup\noexpand\macroargctxt + \noexpand\csname\the\macname xx\endcsname}% + \expandafter\xdef\csname\the\macname xx\endcsname##1{% + \expandafter\noexpand\csname\the\macname xxx\endcsname ##1,}% + \expandafter\expandafter + \expandafter\xdef + \expandafter\expandafter + \csname\the\macname xxx\endcsname + \paramlist{\egroup\noexpand\scanmacro{\temp}}% + \else % 10 or more + \expandafter\xdef\csname\the\macname\endcsname{% + \noexpand\getargvals@{\the\macname}{\argl}% + }% + \global\expandafter\let\csname mac.\the\macname .body\endcsname\temp + \global\expandafter\let\csname mac.\the\macname .recurse\endcsname\gobble + \fi + \fi + \else + \ifcase\paramno + % 0 + \expandafter\xdef\csname\the\macname\endcsname{% + \noexpand\norecurse{\the\macname}% + \noexpand\scanmacro{\temp}\egroup}% + \or % 1 + \expandafter\xdef\csname\the\macname\endcsname{% + \bgroup\noexpand\macroargctxt + \noexpand\braceorline + \expandafter\noexpand\csname\the\macname xxx\endcsname}% + \expandafter\xdef\csname\the\macname xxx\endcsname##1{% + \egroup + \noexpand\norecurse{\the\macname}% + \noexpand\scanmacro{\temp}\egroup}% + \else % at most 9 + \ifnum\paramno<10\relax + \expandafter\xdef\csname\the\macname\endcsname{% + \bgroup\noexpand\macroargctxt + \expandafter\noexpand\csname\the\macname xx\endcsname}% + \expandafter\xdef\csname\the\macname xx\endcsname##1{% + \expandafter\noexpand\csname\the\macname xxx\endcsname ##1,}% + \expandafter\expandafter + \expandafter\xdef + \expandafter\expandafter + \csname\the\macname xxx\endcsname + \paramlist{% + \egroup + \noexpand\norecurse{\the\macname}% + \noexpand\scanmacro{\temp}\egroup}% + \else % 10 or more: + \expandafter\xdef\csname\the\macname\endcsname{% + \noexpand\getargvals@{\the\macname}{\argl}% + }% + \global\expandafter\let\csname mac.\the\macname .body\endcsname\temp + \global\expandafter\let\csname mac.\the\macname .recurse\endcsname\norecurse + \fi + \fi + \fi} + +\catcode `\@\texiatcatcode\relax + +\def\norecurse#1{\bgroup\cslet{#1}{macsave.#1}} + +% \braceorline decides whether the next nonwhitespace character is a +% {. If so it reads up to the closing }, if not, it reads the whole +% line. Whatever was read is then fed to the next control sequence +% as an argument (by \parsebrace or \parsearg). +% +\def\braceorline#1{\let\macnamexxx=#1\futurelet\nchar\braceorlinexxx} +\def\braceorlinexxx{% + \ifx\nchar\bgroup\else + \expandafter\parsearg + \fi \macnamexxx} + + +% @alias. +% We need some trickery to remove the optional spaces around the equal +% sign. Make them active and then expand them all to nothing. +% +\def\alias{\parseargusing\obeyspaces\aliasxxx} +\def\aliasxxx #1{\aliasyyy#1\relax} +\def\aliasyyy #1=#2\relax{% + {% + \expandafter\let\obeyedspace=\empty + \addtomacrolist{#1}% + \xdef\next{\global\let\makecsname{#1}=\makecsname{#2}}% + }% + \next +} + + +\message{cross references,} + +\newwrite\auxfile +\newif\ifhavexrefs % True if xref values are known. +\newif\ifwarnedxrefs % True if we warned once that they aren't known. + +% @inforef is relatively simple. +\def\inforef #1{\inforefzzz #1,,,,**} +\def\inforefzzz #1,#2,#3,#4**{% + \putwordSee{} \putwordInfo{} \putwordfile{} \file{\ignorespaces #3{}}, + node \samp{\ignorespaces#1{}}} + +% @node's only job in TeX is to define \lastnode, which is used in +% cross-references. The @node line might or might not have commas, and +% might or might not have spaces before the first comma, like: +% @node foo , bar , ... +% We don't want such trailing spaces in the node name. +% +\parseargdef\node{\checkenv{}\donode #1 ,\finishnodeparse} +% +% also remove a trailing comma, in case of something like this: +% @node Help-Cross, , , Cross-refs +\def\donode#1 ,#2\finishnodeparse{\dodonode #1,\finishnodeparse} +\def\dodonode#1,#2\finishnodeparse{\gdef\lastnode{#1}} + +\let\nwnode=\node +\let\lastnode=\empty + +% Write a cross-reference definition for the current node. #1 is the +% type (Ynumbered, Yappendix, Ynothing). +% +\def\donoderef#1{% + \ifx\lastnode\empty\else + \setref{\lastnode}{#1}% + \global\let\lastnode=\empty + \fi +} + +% @anchor{NAME} -- define xref target at arbitrary point. +% +\newcount\savesfregister +% +\def\savesf{\relax \ifhmode \savesfregister=\spacefactor \fi} +\def\restoresf{\relax \ifhmode \spacefactor=\savesfregister \fi} +\def\anchor#1{\savesf \setref{#1}{Ynothing}\restoresf \ignorespaces} + +% \setref{NAME}{SNT} defines a cross-reference point NAME (a node or an +% anchor), which consists of three parts: +% 1) NAME-title - the current sectioning name taken from \lastsection, +% or the anchor name. +% 2) NAME-snt - section number and type, passed as the SNT arg, or +% empty for anchors. +% 3) NAME-pg - the page number. +% +% This is called from \donoderef, \anchor, and \dofloat. In the case of +% floats, there is an additional part, which is not written here: +% 4) NAME-lof - the text as it should appear in a @listoffloats. +% +\def\setref#1#2{% + \pdfmkdest{#1}% + \iflinks + {% + \atdummies % preserve commands, but don't expand them + \edef\writexrdef##1##2{% + \write\auxfile{@xrdef{#1-% #1 of \setref, expanded by the \edef + ##1}{##2}}% these are parameters of \writexrdef + }% + \toks0 = \expandafter{\lastsection}% + \immediate \writexrdef{title}{\the\toks0 }% + \immediate \writexrdef{snt}{\csname #2\endcsname}% \Ynumbered etc. + \safewhatsit{\writexrdef{pg}{\folio}}% will be written later, at \shipout + }% + \fi +} + +% @xrefautosectiontitle on|off says whether @section(ing) names are used +% automatically in xrefs, if the third arg is not explicitly specified. +% This was provided as a "secret" @set xref-automatic-section-title +% variable, now it's official. +% +\parseargdef\xrefautomaticsectiontitle{% + \def\temp{#1}% + \ifx\temp\onword + \expandafter\let\csname SETxref-automatic-section-title\endcsname + = \empty + \else\ifx\temp\offword + \expandafter\let\csname SETxref-automatic-section-title\endcsname + = \relax + \else + \errhelp = \EMsimple + \errmessage{Unknown @xrefautomaticsectiontitle value `\temp', + must be on|off}% + \fi\fi +} + + +% @xref, @pxref, and @ref generate cross-references. For \xrefX, #1 is +% the node name, #2 the name of the Info cross-reference, #3 the printed +% node name, #4 the name of the Info file, #5 the name of the printed +% manual. All but the node name can be omitted. +% +\def\pxref#1{\putwordsee{} \xrefX[#1,,,,,,,]} +\def\xref#1{\putwordSee{} \xrefX[#1,,,,,,,]} +\def\ref#1{\xrefX[#1,,,,,,,]} +% +\newbox\topbox +\newbox\printedrefnamebox +\newbox\printedmanualbox +% +\def\xrefX[#1,#2,#3,#4,#5,#6]{\begingroup + \unsepspaces + % + \def\printedrefname{\ignorespaces #3}% + \setbox\printedrefnamebox = \hbox{\printedrefname\unskip}% + % + \def\printedmanual{\ignorespaces #5}% + \setbox\printedmanualbox = \hbox{\printedmanual\unskip}% + % + % If the printed reference name (arg #3) was not explicitly given in + % the @xref, figure out what we want to use. + \ifdim \wd\printedrefnamebox = 0pt + % No printed node name was explicitly given. + \expandafter\ifx\csname SETxref-automatic-section-title\endcsname \relax + % Not auto section-title: use node name inside the square brackets. + \def\printedrefname{\ignorespaces #1}% + \else + % Auto section-title: use chapter/section title inside + % the square brackets if we have it. + \ifdim \wd\printedmanualbox > 0pt + % It is in another manual, so we don't have it; use node name. + \def\printedrefname{\ignorespaces #1}% + \else + \ifhavexrefs + % We (should) know the real title if we have the xref values. + \def\printedrefname{\refx{#1-title}{}}% + \else + % Otherwise just copy the Info node name. + \def\printedrefname{\ignorespaces #1}% + \fi% + \fi + \fi + \fi + % + % Make link in pdf output. + \ifpdf + {\indexnofonts + \turnoffactive + \makevalueexpandable + % This expands tokens, so do it after making catcode changes, so _ + % etc. don't get their TeX definitions. + \getfilename{#4}% + % + \edef\pdfxrefdest{#1}% + \txiescapepdf\pdfxrefdest + % + \leavevmode + \startlink attr{/Border [0 0 0]}% + \ifnum\filenamelength>0 + goto file{\the\filename.pdf} name{\pdfxrefdest}% + \else + goto name{\pdfmkpgn{\pdfxrefdest}}% + \fi + }% + \setcolor{\linkcolor}% + \fi + % + % Float references are printed completely differently: "Figure 1.2" + % instead of "[somenode], p.3". We distinguish them by the + % LABEL-title being set to a magic string. + {% + % Have to otherify everything special to allow the \csname to + % include an _ in the xref name, etc. + \indexnofonts + \turnoffactive + \expandafter\global\expandafter\let\expandafter\Xthisreftitle + \csname XR#1-title\endcsname + }% + \iffloat\Xthisreftitle + % If the user specified the print name (third arg) to the ref, + % print it instead of our usual "Figure 1.2". + \ifdim\wd\printedrefnamebox = 0pt + \refx{#1-snt}{}% + \else + \printedrefname + \fi + % + % if the user also gave the printed manual name (fifth arg), append + % "in MANUALNAME". + \ifdim \wd\printedmanualbox > 0pt + \space \putwordin{} \cite{\printedmanual}% + \fi + \else + % node/anchor (non-float) references. + % + % If we use \unhbox to print the node names, TeX does not insert + % empty discretionaries after hyphens, which means that it will not + % find a line break at a hyphen in a node names. Since some manuals + % are best written with fairly long node names, containing hyphens, + % this is a loss. Therefore, we give the text of the node name + % again, so it is as if TeX is seeing it for the first time. + % + % Cross-manual reference. Only include the "Section ``foo'' in" if + % the foo is neither missing or Top. Thus, @xref{,,,foo,The Foo Manual} + % outputs simply "see The Foo Manual". + \ifdim \wd\printedmanualbox > 0pt + % What is the 7sp about? The idea is that we also want to omit + % the Section part if we would be printing "Top", since they are + % clearly trying to refer to the whole manual. But, this being + % TeX, we can't easily compare strings while ignoring the possible + % spaces before and after in the input. By adding the arbitrary + % 7sp, we make it much less likely that a real node name would + % happen to have the same width as "Top" (e.g., in a monospaced font). + % I hope it will never happen in practice. + % + % For the same basic reason, we retypeset the "Top" at every + % reference, since the current font is indeterminate. + % + \setbox\topbox = \hbox{Top\kern7sp}% + \setbox2 = \hbox{\ignorespaces \printedrefname \unskip \kern7sp}% + \ifdim \wd2 > 7sp + \ifdim \wd2 = \wd\topbox \else + \putwordSection{} ``\printedrefname'' \putwordin{}\space + \fi + \fi + \cite{\printedmanual}% + \else + % Reference in this manual. + % + % _ (for example) has to be the character _ for the purposes of the + % control sequence corresponding to the node, but it has to expand + % into the usual \leavevmode...\vrule stuff for purposes of + % printing. So we \turnoffactive for the \refx-snt, back on for the + % printing, back off for the \refx-pg. + {\turnoffactive + % Only output a following space if the -snt ref is nonempty; for + % @unnumbered and @anchor, it won't be. + \setbox2 = \hbox{\ignorespaces \refx{#1-snt}{}}% + \ifdim \wd2 > 0pt \refx{#1-snt}\space\fi + }% + % output the `[mynode]' via the macro below so it can be overridden. + \xrefprintnodename\printedrefname + % + % But we always want a comma and a space: + ,\space + % + % output the `page 3'. + \turnoffactive \putwordpage\tie\refx{#1-pg}{}% + \fi + \fi + \endlink +\endgroup} + +% This macro is called from \xrefX for the `[nodename]' part of xref +% output. It's a separate macro only so it can be changed more easily, +% since square brackets don't work well in some documents. Particularly +% one that Bob is working on :). +% +\def\xrefprintnodename#1{[#1]} + +% Things referred to by \setref. +% +\def\Ynothing{} +\def\Yomitfromtoc{} +\def\Ynumbered{% + \ifnum\secno=0 + \putwordChapter@tie \the\chapno + \else \ifnum\subsecno=0 + \putwordSection@tie \the\chapno.\the\secno + \else \ifnum\subsubsecno=0 + \putwordSection@tie \the\chapno.\the\secno.\the\subsecno + \else + \putwordSection@tie \the\chapno.\the\secno.\the\subsecno.\the\subsubsecno + \fi\fi\fi +} +\def\Yappendix{% + \ifnum\secno=0 + \putwordAppendix@tie @char\the\appendixno{}% + \else \ifnum\subsecno=0 + \putwordSection@tie @char\the\appendixno.\the\secno + \else \ifnum\subsubsecno=0 + \putwordSection@tie @char\the\appendixno.\the\secno.\the\subsecno + \else + \putwordSection@tie + @char\the\appendixno.\the\secno.\the\subsecno.\the\subsubsecno + \fi\fi\fi +} + +% Define \refx{NAME}{SUFFIX} to reference a cross-reference string named NAME. +% If its value is nonempty, SUFFIX is output afterward. +% +\def\refx#1#2{% + {% + \indexnofonts + \otherbackslash + \expandafter\global\expandafter\let\expandafter\thisrefX + \csname XR#1\endcsname + }% + \ifx\thisrefX\relax + % If not defined, say something at least. + \angleleft un\-de\-fined\angleright + \iflinks + \ifhavexrefs + {\toks0 = {#1}% avoid expansion of possibly-complex value + \message{\linenumber Undefined cross reference `\the\toks0'.}}% + \else + \ifwarnedxrefs\else + \global\warnedxrefstrue + \message{Cross reference values unknown; you must run TeX again.}% + \fi + \fi + \fi + \else + % It's defined, so just use it. + \thisrefX + \fi + #2% Output the suffix in any case. +} + +% This is the macro invoked by entries in the aux file. Usually it's +% just a \def (we prepend XR to the control sequence name to avoid +% collisions). But if this is a float type, we have more work to do. +% +\def\xrdef#1#2{% + {% The node name might contain 8-bit characters, which in our current + % implementation are changed to commands like @'e. Don't let these + % mess up the control sequence name. + \indexnofonts + \turnoffactive + \xdef\safexrefname{#1}% + }% + % + \expandafter\gdef\csname XR\safexrefname\endcsname{#2}% remember this xref + % + % Was that xref control sequence that we just defined for a float? + \expandafter\iffloat\csname XR\safexrefname\endcsname + % it was a float, and we have the (safe) float type in \iffloattype. + \expandafter\let\expandafter\floatlist + \csname floatlist\iffloattype\endcsname + % + % Is this the first time we've seen this float type? + \expandafter\ifx\floatlist\relax + \toks0 = {\do}% yes, so just \do + \else + % had it before, so preserve previous elements in list. + \toks0 = \expandafter{\floatlist\do}% + \fi + % + % Remember this xref in the control sequence \floatlistFLOATTYPE, + % for later use in \listoffloats. + \expandafter\xdef\csname floatlist\iffloattype\endcsname{\the\toks0 + {\safexrefname}}% + \fi +} + +% Read the last existing aux file, if any. No error if none exists. +% +\def\tryauxfile{% + \openin 1 \jobname.aux + \ifeof 1 \else + \readdatafile{aux}% + \global\havexrefstrue + \fi + \closein 1 +} + +\def\setupdatafile{% + \catcode`\^^@=\other + \catcode`\^^A=\other + \catcode`\^^B=\other + \catcode`\^^C=\other + \catcode`\^^D=\other + \catcode`\^^E=\other + \catcode`\^^F=\other + \catcode`\^^G=\other + \catcode`\^^H=\other + \catcode`\^^K=\other + \catcode`\^^L=\other + \catcode`\^^N=\other + \catcode`\^^P=\other + \catcode`\^^Q=\other + \catcode`\^^R=\other + \catcode`\^^S=\other + \catcode`\^^T=\other + \catcode`\^^U=\other + \catcode`\^^V=\other + \catcode`\^^W=\other + \catcode`\^^X=\other + \catcode`\^^Z=\other + \catcode`\^^[=\other + \catcode`\^^\=\other + \catcode`\^^]=\other + \catcode`\^^^=\other + \catcode`\^^_=\other + % It was suggested to set the catcode of ^ to 7, which would allow ^^e4 etc. + % in xref tags, i.e., node names. But since ^^e4 notation isn't + % supported in the main text, it doesn't seem desirable. Furthermore, + % that is not enough: for node names that actually contain a ^ + % character, we would end up writing a line like this: 'xrdef {'hat + % b-title}{'hat b} and \xrdef does a \csname...\endcsname on the first + % argument, and \hat is not an expandable control sequence. It could + % all be worked out, but why? Either we support ^^ or we don't. + % + % The other change necessary for this was to define \auxhat: + % \def\auxhat{\def^{'hat }}% extra space so ok if followed by letter + % and then to call \auxhat in \setq. + % + \catcode`\^=\other + % + % Special characters. Should be turned off anyway, but... + \catcode`\~=\other + \catcode`\[=\other + \catcode`\]=\other + \catcode`\"=\other + \catcode`\_=\other + \catcode`\|=\other + \catcode`\<=\other + \catcode`\>=\other + \catcode`\$=\other + \catcode`\#=\other + \catcode`\&=\other + \catcode`\%=\other + \catcode`+=\other % avoid \+ for paranoia even though we've turned it off + % + % This is to support \ in node names and titles, since the \ + % characters end up in a \csname. It's easier than + % leaving it active and making its active definition an actual \ + % character. What I don't understand is why it works in the *value* + % of the xrdef. Seems like it should be a catcode12 \, and that + % should not typeset properly. But it works, so I'm moving on for + % now. --karl, 15jan04. + \catcode`\\=\other + % + % Make the characters 128-255 be printing characters. + {% + \count1=128 + \def\loop{% + \catcode\count1=\other + \advance\count1 by 1 + \ifnum \count1<256 \loop \fi + }% + }% + % + % @ is our escape character in .aux files, and we need braces. + \catcode`\{=1 + \catcode`\}=2 + \catcode`\@=0 +} + +\def\readdatafile#1{% +\begingroup + \setupdatafile + \input\jobname.#1 +\endgroup} + + +\message{insertions,} +% including footnotes. + +\newcount \footnoteno + +% The trailing space in the following definition for supereject is +% vital for proper filling; pages come out unaligned when you do a +% pagealignmacro call if that space before the closing brace is +% removed. (Generally, numeric constants should always be followed by a +% space to prevent strange expansion errors.) +\def\supereject{\par\penalty -20000\footnoteno =0 } + +% @footnotestyle is meaningful for Info output only. +\let\footnotestyle=\comment + +{\catcode `\@=11 +% +% Auto-number footnotes. Otherwise like plain. +\gdef\footnote{% + \let\indent=\ptexindent + \let\noindent=\ptexnoindent + \global\advance\footnoteno by \@ne + \edef\thisfootno{$^{\the\footnoteno}$}% + % + % In case the footnote comes at the end of a sentence, preserve the + % extra spacing after we do the footnote number. + \let\@sf\empty + \ifhmode\edef\@sf{\spacefactor\the\spacefactor}\ptexslash\fi + % + % Remove inadvertent blank space before typesetting the footnote number. + \unskip + \thisfootno\@sf + \dofootnote +}% + +% Don't bother with the trickery in plain.tex to not require the +% footnote text as a parameter. Our footnotes don't need to be so general. +% +% Oh yes, they do; otherwise, @ifset (and anything else that uses +% \parseargline) fails inside footnotes because the tokens are fixed when +% the footnote is read. --karl, 16nov96. +% +\gdef\dofootnote{% + \insert\footins\bgroup + % We want to typeset this text as a normal paragraph, even if the + % footnote reference occurs in (for example) a display environment. + % So reset some parameters. + \hsize=\pagewidth + \interlinepenalty\interfootnotelinepenalty + \splittopskip\ht\strutbox % top baseline for broken footnotes + \splitmaxdepth\dp\strutbox + \floatingpenalty\@MM + \leftskip\z@skip + \rightskip\z@skip + \spaceskip\z@skip + \xspaceskip\z@skip + \parindent\defaultparindent + % + \smallfonts \rm + % + % Because we use hanging indentation in footnotes, a @noindent appears + % to exdent this text, so make it be a no-op. makeinfo does not use + % hanging indentation so @noindent can still be needed within footnote + % text after an @example or the like (not that this is good style). + \let\noindent = \relax + % + % Hang the footnote text off the number. Use \everypar in case the + % footnote extends for more than one paragraph. + \everypar = {\hang}% + \textindent{\thisfootno}% + % + % Don't crash into the line above the footnote text. Since this + % expands into a box, it must come within the paragraph, lest it + % provide a place where TeX can split the footnote. + \footstrut + % + % Invoke rest of plain TeX footnote routine. + \futurelet\next\fo@t +} +}%end \catcode `\@=11 + +% In case a @footnote appears in a vbox, save the footnote text and create +% the real \insert just after the vbox finished. Otherwise, the insertion +% would be lost. +% Similarly, if a @footnote appears inside an alignment, save the footnote +% text to a box and make the \insert when a row of the table is finished. +% And the same can be done for other insert classes. --kasal, 16nov03. + +% Replace the \insert primitive by a cheating macro. +% Deeper inside, just make sure that the saved insertions are not spilled +% out prematurely. +% +\def\startsavinginserts{% + \ifx \insert\ptexinsert + \let\insert\saveinsert + \else + \let\checkinserts\relax + \fi +} + +% This \insert replacement works for both \insert\footins{foo} and +% \insert\footins\bgroup foo\egroup, but it doesn't work for \insert27{foo}. +% +\def\saveinsert#1{% + \edef\next{\noexpand\savetobox \makeSAVEname#1}% + \afterassignment\next + % swallow the left brace + \let\temp = +} +\def\makeSAVEname#1{\makecsname{SAVE\expandafter\gobble\string#1}} +\def\savetobox#1{\global\setbox#1 = \vbox\bgroup \unvbox#1} + +\def\checksaveins#1{\ifvoid#1\else \placesaveins#1\fi} + +\def\placesaveins#1{% + \ptexinsert \csname\expandafter\gobblesave\string#1\endcsname + {\box#1}% +} + +% eat @SAVE -- beware, all of them have catcode \other: +{ + \def\dospecials{\do S\do A\do V\do E} \uncatcodespecials % ;-) + \gdef\gobblesave @SAVE{} +} + +% initialization: +\def\newsaveins #1{% + \edef\next{\noexpand\newsaveinsX \makeSAVEname#1}% + \next +} +\def\newsaveinsX #1{% + \csname newbox\endcsname #1% + \expandafter\def\expandafter\checkinserts\expandafter{\checkinserts + \checksaveins #1}% +} + +% initialize: +\let\checkinserts\empty +\newsaveins\footins +\newsaveins\margin + + +% @image. We use the macros from epsf.tex to support this. +% If epsf.tex is not installed and @image is used, we complain. +% +% Check for and read epsf.tex up front. If we read it only at @image +% time, we might be inside a group, and then its definitions would get +% undone and the next image would fail. +\openin 1 = epsf.tex +\ifeof 1 \else + % Do not bother showing banner with epsf.tex v2.7k (available in + % doc/epsf.tex and on ctan). + \def\epsfannounce{\toks0 = }% + \input epsf.tex +\fi +\closein 1 +% +% We will only complain once about lack of epsf.tex. +\newif\ifwarnednoepsf +\newhelp\noepsfhelp{epsf.tex must be installed for images to + work. It is also included in the Texinfo distribution, or you can get + it from ftp://tug.org/tex/epsf.tex.} +% +\def\image#1{% + \ifx\epsfbox\thisisundefined + \ifwarnednoepsf \else + \errhelp = \noepsfhelp + \errmessage{epsf.tex not found, images will be ignored}% + \global\warnednoepsftrue + \fi + \else + \imagexxx #1,,,,,\finish + \fi +} +% +% Arguments to @image: +% #1 is (mandatory) image filename; we tack on .eps extension. +% #2 is (optional) width, #3 is (optional) height. +% #4 is (ignored optional) html alt text. +% #5 is (ignored optional) extension. +% #6 is just the usual extra ignored arg for parsing stuff. +\newif\ifimagevmode +\def\imagexxx#1,#2,#3,#4,#5,#6\finish{\begingroup + \catcode`\^^M = 5 % in case we're inside an example + \normalturnoffactive % allow _ et al. in names + % If the image is by itself, center it. + \ifvmode + \imagevmodetrue + \else \ifx\centersub\centerV + % for @center @image, we need a vbox so we can have our vertical space + \imagevmodetrue + \vbox\bgroup % vbox has better behavior than vtop herev + \fi\fi + % + \ifimagevmode + \nobreak\medskip + % Usually we'll have text after the image which will insert + % \parskip glue, so insert it here too to equalize the space + % above and below. + \nobreak\vskip\parskip + \nobreak + \fi + % + % Leave vertical mode so that indentation from an enclosing + % environment such as @quotation is respected. + % However, if we're at the top level, we don't want the + % normal paragraph indentation. + % On the other hand, if we are in the case of @center @image, we don't + % want to start a paragraph, which will create a hsize-width box and + % eradicate the centering. + \ifx\centersub\centerV\else \noindent \fi + % + % Output the image. + \ifpdf + \dopdfimage{#1}{#2}{#3}% + \else + % \epsfbox itself resets \epsf?size at each figure. + \setbox0 = \hbox{\ignorespaces #2}\ifdim\wd0 > 0pt \epsfxsize=#2\relax \fi + \setbox0 = \hbox{\ignorespaces #3}\ifdim\wd0 > 0pt \epsfysize=#3\relax \fi + \epsfbox{#1.eps}% + \fi + % + \ifimagevmode + \medskip % space after a standalone image + \fi + \ifx\centersub\centerV \egroup \fi +\endgroup} + + +% @float FLOATTYPE,LABEL,LOC ... @end float for displayed figures, tables, +% etc. We don't actually implement floating yet, we always include the +% float "here". But it seemed the best name for the future. +% +\envparseargdef\float{\eatcommaspace\eatcommaspace\dofloat#1, , ,\finish} + +% There may be a space before second and/or third parameter; delete it. +\def\eatcommaspace#1, {#1,} + +% #1 is the optional FLOATTYPE, the text label for this float, typically +% "Figure", "Table", "Example", etc. Can't contain commas. If omitted, +% this float will not be numbered and cannot be referred to. +% +% #2 is the optional xref label. Also must be present for the float to +% be referable. +% +% #3 is the optional positioning argument; for now, it is ignored. It +% will somehow specify the positions allowed to float to (here, top, bottom). +% +% We keep a separate counter for each FLOATTYPE, which we reset at each +% chapter-level command. +\let\resetallfloatnos=\empty +% +\def\dofloat#1,#2,#3,#4\finish{% + \let\thiscaption=\empty + \let\thisshortcaption=\empty + % + % don't lose footnotes inside @float. + % + % BEWARE: when the floats start float, we have to issue warning whenever an + % insert appears inside a float which could possibly float. --kasal, 26may04 + % + \startsavinginserts + % + % We can't be used inside a paragraph. + \par + % + \vtop\bgroup + \def\floattype{#1}% + \def\floatlabel{#2}% + \def\floatloc{#3}% we do nothing with this yet. + % + \ifx\floattype\empty + \let\safefloattype=\empty + \else + {% + % the floattype might have accents or other special characters, + % but we need to use it in a control sequence name. + \indexnofonts + \turnoffactive + \xdef\safefloattype{\floattype}% + }% + \fi + % + % If label is given but no type, we handle that as the empty type. + \ifx\floatlabel\empty \else + % We want each FLOATTYPE to be numbered separately (Figure 1, + % Table 1, Figure 2, ...). (And if no label, no number.) + % + \expandafter\getfloatno\csname\safefloattype floatno\endcsname + \global\advance\floatno by 1 + % + {% + % This magic value for \lastsection is output by \setref as the + % XREFLABEL-title value. \xrefX uses it to distinguish float + % labels (which have a completely different output format) from + % node and anchor labels. And \xrdef uses it to construct the + % lists of floats. + % + \edef\lastsection{\floatmagic=\safefloattype}% + \setref{\floatlabel}{Yfloat}% + }% + \fi + % + % start with \parskip glue, I guess. + \vskip\parskip + % + % Don't suppress indentation if a float happens to start a section. + \restorefirstparagraphindent +} + +% we have these possibilities: +% @float Foo,lbl & @caption{Cap}: Foo 1.1: Cap +% @float Foo,lbl & no caption: Foo 1.1 +% @float Foo & @caption{Cap}: Foo: Cap +% @float Foo & no caption: Foo +% @float ,lbl & Caption{Cap}: 1.1: Cap +% @float ,lbl & no caption: 1.1 +% @float & @caption{Cap}: Cap +% @float & no caption: +% +\def\Efloat{% + \let\floatident = \empty + % + % In all cases, if we have a float type, it comes first. + \ifx\floattype\empty \else \def\floatident{\floattype}\fi + % + % If we have an xref label, the number comes next. + \ifx\floatlabel\empty \else + \ifx\floattype\empty \else % if also had float type, need tie first. + \appendtomacro\floatident{\tie}% + \fi + % the number. + \appendtomacro\floatident{\chaplevelprefix\the\floatno}% + \fi + % + % Start the printed caption with what we've constructed in + % \floatident, but keep it separate; we need \floatident again. + \let\captionline = \floatident + % + \ifx\thiscaption\empty \else + \ifx\floatident\empty \else + \appendtomacro\captionline{: }% had ident, so need a colon between + \fi + % + % caption text. + \appendtomacro\captionline{\scanexp\thiscaption}% + \fi + % + % If we have anything to print, print it, with space before. + % Eventually this needs to become an \insert. + \ifx\captionline\empty \else + \vskip.5\parskip + \captionline + % + % Space below caption. + \vskip\parskip + \fi + % + % If have an xref label, write the list of floats info. Do this + % after the caption, to avoid chance of it being a breakpoint. + \ifx\floatlabel\empty \else + % Write the text that goes in the lof to the aux file as + % \floatlabel-lof. Besides \floatident, we include the short + % caption if specified, else the full caption if specified, else nothing. + {% + \atdummies + % + % since we read the caption text in the macro world, where ^^M + % is turned into a normal character, we have to scan it back, so + % we don't write the literal three characters "^^M" into the aux file. + \scanexp{% + \xdef\noexpand\gtemp{% + \ifx\thisshortcaption\empty + \thiscaption + \else + \thisshortcaption + \fi + }% + }% + \immediate\write\auxfile{@xrdef{\floatlabel-lof}{\floatident + \ifx\gtemp\empty \else : \gtemp \fi}}% + }% + \fi + \egroup % end of \vtop + % + % place the captured inserts + % + % BEWARE: when the floats start floating, we have to issue warning + % whenever an insert appears inside a float which could possibly + % float. --kasal, 26may04 + % + \checkinserts +} + +% Append the tokens #2 to the definition of macro #1, not expanding either. +% +\def\appendtomacro#1#2{% + \expandafter\def\expandafter#1\expandafter{#1#2}% +} + +% @caption, @shortcaption +% +\def\caption{\docaption\thiscaption} +\def\shortcaption{\docaption\thisshortcaption} +\def\docaption{\checkenv\float \bgroup\scanargctxt\defcaption} +\def\defcaption#1#2{\egroup \def#1{#2}} + +% The parameter is the control sequence identifying the counter we are +% going to use. Create it if it doesn't exist and assign it to \floatno. +\def\getfloatno#1{% + \ifx#1\relax + % Haven't seen this figure type before. + \csname newcount\endcsname #1% + % + % Remember to reset this floatno at the next chap. + \expandafter\gdef\expandafter\resetallfloatnos + \expandafter{\resetallfloatnos #1=0 }% + \fi + \let\floatno#1% +} + +% \setref calls this to get the XREFLABEL-snt value. We want an @xref +% to the FLOATLABEL to expand to "Figure 3.1". We call \setref when we +% first read the @float command. +% +\def\Yfloat{\floattype@tie \chaplevelprefix\the\floatno}% + +% Magic string used for the XREFLABEL-title value, so \xrefX can +% distinguish floats from other xref types. +\def\floatmagic{!!float!!} + +% #1 is the control sequence we are passed; we expand into a conditional +% which is true if #1 represents a float ref. That is, the magic +% \lastsection value which we \setref above. +% +\def\iffloat#1{\expandafter\doiffloat#1==\finish} +% +% #1 is (maybe) the \floatmagic string. If so, #2 will be the +% (safe) float type for this float. We set \iffloattype to #2. +% +\def\doiffloat#1=#2=#3\finish{% + \def\temp{#1}% + \def\iffloattype{#2}% + \ifx\temp\floatmagic +} + +% @listoffloats FLOATTYPE - print a list of floats like a table of contents. +% +\parseargdef\listoffloats{% + \def\floattype{#1}% floattype + {% + % the floattype might have accents or other special characters, + % but we need to use it in a control sequence name. + \indexnofonts + \turnoffactive + \xdef\safefloattype{\floattype}% + }% + % + % \xrdef saves the floats as a \do-list in \floatlistSAFEFLOATTYPE. + \expandafter\ifx\csname floatlist\safefloattype\endcsname \relax + \ifhavexrefs + % if the user said @listoffloats foo but never @float foo. + \message{\linenumber No `\safefloattype' floats to list.}% + \fi + \else + \begingroup + \leftskip=\tocindent % indent these entries like a toc + \let\do=\listoffloatsdo + \csname floatlist\safefloattype\endcsname + \endgroup + \fi +} + +% This is called on each entry in a list of floats. We're passed the +% xref label, in the form LABEL-title, which is how we save it in the +% aux file. We strip off the -title and look up \XRLABEL-lof, which +% has the text we're supposed to typeset here. +% +% Figures without xref labels will not be included in the list (since +% they won't appear in the aux file). +% +\def\listoffloatsdo#1{\listoffloatsdoentry#1\finish} +\def\listoffloatsdoentry#1-title\finish{{% + % Can't fully expand XR#1-lof because it can contain anything. Just + % pass the control sequence. On the other hand, XR#1-pg is just the + % page number, and we want to fully expand that so we can get a link + % in pdf output. + \toksA = \expandafter{\csname XR#1-lof\endcsname}% + % + % use the same \entry macro we use to generate the TOC and index. + \edef\writeentry{\noexpand\entry{\the\toksA}{\csname XR#1-pg\endcsname}}% + \writeentry +}} + + +\message{localization,} + +% For single-language documents, @documentlanguage is usually given very +% early, just after @documentencoding. Single argument is the language +% (de) or locale (de_DE) abbreviation. +% +{ + \catcode`\_ = \active + \globaldefs=1 +\parseargdef\documentlanguage{\begingroup + \let_=\normalunderscore % normal _ character for filenames + \tex % read txi-??.tex file in plain TeX. + % Read the file by the name they passed if it exists. + \openin 1 txi-#1.tex + \ifeof 1 + \documentlanguagetrywithoutunderscore{#1_\finish}% + \else + \globaldefs = 1 % everything in the txi-LL files needs to persist + \input txi-#1.tex + \fi + \closein 1 + \endgroup % end raw TeX +\endgroup} +% +% If they passed de_DE, and txi-de_DE.tex doesn't exist, +% try txi-de.tex. +% +\gdef\documentlanguagetrywithoutunderscore#1_#2\finish{% + \openin 1 txi-#1.tex + \ifeof 1 + \errhelp = \nolanghelp + \errmessage{Cannot read language file txi-#1.tex}% + \else + \globaldefs = 1 % everything in the txi-LL files needs to persist + \input txi-#1.tex + \fi + \closein 1 +} +}% end of special _ catcode +% +\newhelp\nolanghelp{The given language definition file cannot be found or +is empty. Maybe you need to install it? Putting it in the current +directory should work if nowhere else does.} + +% This macro is called from txi-??.tex files; the first argument is the +% \language name to set (without the "\lang@" prefix), the second and +% third args are \{left,right}hyphenmin. +% +% The language names to pass are determined when the format is built. +% See the etex.log file created at that time, e.g., +% /usr/local/texlive/2008/texmf-var/web2c/pdftex/etex.log. +% +% With TeX Live 2008, etex now includes hyphenation patterns for all +% available languages. This means we can support hyphenation in +% Texinfo, at least to some extent. (This still doesn't solve the +% accented characters problem.) +% +\catcode`@=11 +\def\txisetlanguage#1#2#3{% + % do not set the language if the name is undefined in the current TeX. + \expandafter\ifx\csname lang@#1\endcsname \relax + \message{no patterns for #1}% + \else + \global\language = \csname lang@#1\endcsname + \fi + % but there is no harm in adjusting the hyphenmin values regardless. + \global\lefthyphenmin = #2\relax + \global\righthyphenmin = #3\relax +} + +% Helpers for encodings. +% Set the catcode of characters 128 through 255 to the specified number. +% +\def\setnonasciicharscatcode#1{% + \count255=128 + \loop\ifnum\count255<256 + \global\catcode\count255=#1\relax + \advance\count255 by 1 + \repeat +} + +\def\setnonasciicharscatcodenonglobal#1{% + \count255=128 + \loop\ifnum\count255<256 + \catcode\count255=#1\relax + \advance\count255 by 1 + \repeat +} + +% @documentencoding sets the definition of non-ASCII characters +% according to the specified encoding. +% +\parseargdef\documentencoding{% + % Encoding being declared for the document. + \def\declaredencoding{\csname #1.enc\endcsname}% + % + % Supported encodings: names converted to tokens in order to be able + % to compare them with \ifx. + \def\ascii{\csname US-ASCII.enc\endcsname}% + \def\latnine{\csname ISO-8859-15.enc\endcsname}% + \def\latone{\csname ISO-8859-1.enc\endcsname}% + \def\lattwo{\csname ISO-8859-2.enc\endcsname}% + \def\utfeight{\csname UTF-8.enc\endcsname}% + % + \ifx \declaredencoding \ascii + \asciichardefs + % + \else \ifx \declaredencoding \lattwo + \setnonasciicharscatcode\active + \lattwochardefs + % + \else \ifx \declaredencoding \latone + \setnonasciicharscatcode\active + \latonechardefs + % + \else \ifx \declaredencoding \latnine + \setnonasciicharscatcode\active + \latninechardefs + % + \else \ifx \declaredencoding \utfeight + \setnonasciicharscatcode\active + \utfeightchardefs + % + \else + \message{Unknown document encoding #1, ignoring.}% + % + \fi % utfeight + \fi % latnine + \fi % latone + \fi % lattwo + \fi % ascii +} + +% A message to be logged when using a character that isn't available +% the default font encoding (OT1). +% +\def\missingcharmsg#1{\message{Character missing in OT1 encoding: #1.}} + +% Take account of \c (plain) vs. \, (Texinfo) difference. +\def\cedilla#1{\ifx\c\ptexc\c{#1}\else\,{#1}\fi} + +% First, make active non-ASCII characters in order for them to be +% correctly categorized when TeX reads the replacement text of +% macros containing the character definitions. +\setnonasciicharscatcode\active +% +% Latin1 (ISO-8859-1) character definitions. +\def\latonechardefs{% + \gdef^^a0{\tie} + \gdef^^a1{\exclamdown} + \gdef^^a2{\missingcharmsg{CENT SIGN}} + \gdef^^a3{{\pounds}} + \gdef^^a4{\missingcharmsg{CURRENCY SIGN}} + \gdef^^a5{\missingcharmsg{YEN SIGN}} + \gdef^^a6{\missingcharmsg{BROKEN BAR}} + \gdef^^a7{\S} + \gdef^^a8{\"{}} + \gdef^^a9{\copyright} + \gdef^^aa{\ordf} + \gdef^^ab{\guillemetleft} + \gdef^^ac{$\lnot$} + \gdef^^ad{\-} + \gdef^^ae{\registeredsymbol} + \gdef^^af{\={}} + % + \gdef^^b0{\textdegree} + \gdef^^b1{$\pm$} + \gdef^^b2{$^2$} + \gdef^^b3{$^3$} + \gdef^^b4{\'{}} + \gdef^^b5{$\mu$} + \gdef^^b6{\P} + % + \gdef^^b7{$^.$} + \gdef^^b8{\cedilla\ } + \gdef^^b9{$^1$} + \gdef^^ba{\ordm} + % + \gdef^^bb{\guillemetright} + \gdef^^bc{$1\over4$} + \gdef^^bd{$1\over2$} + \gdef^^be{$3\over4$} + \gdef^^bf{\questiondown} + % + \gdef^^c0{\`A} + \gdef^^c1{\'A} + \gdef^^c2{\^A} + \gdef^^c3{\~A} + \gdef^^c4{\"A} + \gdef^^c5{\ringaccent A} + \gdef^^c6{\AE} + \gdef^^c7{\cedilla C} + \gdef^^c8{\`E} + \gdef^^c9{\'E} + \gdef^^ca{\^E} + \gdef^^cb{\"E} + \gdef^^cc{\`I} + \gdef^^cd{\'I} + \gdef^^ce{\^I} + \gdef^^cf{\"I} + % + \gdef^^d0{\DH} + \gdef^^d1{\~N} + \gdef^^d2{\`O} + \gdef^^d3{\'O} + \gdef^^d4{\^O} + \gdef^^d5{\~O} + \gdef^^d6{\"O} + \gdef^^d7{$\times$} + \gdef^^d8{\O} + \gdef^^d9{\`U} + \gdef^^da{\'U} + \gdef^^db{\^U} + \gdef^^dc{\"U} + \gdef^^dd{\'Y} + \gdef^^de{\TH} + \gdef^^df{\ss} + % + \gdef^^e0{\`a} + \gdef^^e1{\'a} + \gdef^^e2{\^a} + \gdef^^e3{\~a} + \gdef^^e4{\"a} + \gdef^^e5{\ringaccent a} + \gdef^^e6{\ae} + \gdef^^e7{\cedilla c} + \gdef^^e8{\`e} + \gdef^^e9{\'e} + \gdef^^ea{\^e} + \gdef^^eb{\"e} + \gdef^^ec{\`{\dotless i}} + \gdef^^ed{\'{\dotless i}} + \gdef^^ee{\^{\dotless i}} + \gdef^^ef{\"{\dotless i}} + % + \gdef^^f0{\dh} + \gdef^^f1{\~n} + \gdef^^f2{\`o} + \gdef^^f3{\'o} + \gdef^^f4{\^o} + \gdef^^f5{\~o} + \gdef^^f6{\"o} + \gdef^^f7{$\div$} + \gdef^^f8{\o} + \gdef^^f9{\`u} + \gdef^^fa{\'u} + \gdef^^fb{\^u} + \gdef^^fc{\"u} + \gdef^^fd{\'y} + \gdef^^fe{\th} + \gdef^^ff{\"y} +} + +% Latin9 (ISO-8859-15) encoding character definitions. +\def\latninechardefs{% + % Encoding is almost identical to Latin1. + \latonechardefs + % + \gdef^^a4{\euro} + \gdef^^a6{\v S} + \gdef^^a8{\v s} + \gdef^^b4{\v Z} + \gdef^^b8{\v z} + \gdef^^bc{\OE} + \gdef^^bd{\oe} + \gdef^^be{\"Y} +} + +% Latin2 (ISO-8859-2) character definitions. +\def\lattwochardefs{% + \gdef^^a0{\tie} + \gdef^^a1{\ogonek{A}} + \gdef^^a2{\u{}} + \gdef^^a3{\L} + \gdef^^a4{\missingcharmsg{CURRENCY SIGN}} + \gdef^^a5{\v L} + \gdef^^a6{\'S} + \gdef^^a7{\S} + \gdef^^a8{\"{}} + \gdef^^a9{\v S} + \gdef^^aa{\cedilla S} + \gdef^^ab{\v T} + \gdef^^ac{\'Z} + \gdef^^ad{\-} + \gdef^^ae{\v Z} + \gdef^^af{\dotaccent Z} + % + \gdef^^b0{\textdegree} + \gdef^^b1{\ogonek{a}} + \gdef^^b2{\ogonek{ }} + \gdef^^b3{\l} + \gdef^^b4{\'{}} + \gdef^^b5{\v l} + \gdef^^b6{\'s} + \gdef^^b7{\v{}} + \gdef^^b8{\cedilla\ } + \gdef^^b9{\v s} + \gdef^^ba{\cedilla s} + \gdef^^bb{\v t} + \gdef^^bc{\'z} + \gdef^^bd{\H{}} + \gdef^^be{\v z} + \gdef^^bf{\dotaccent z} + % + \gdef^^c0{\'R} + \gdef^^c1{\'A} + \gdef^^c2{\^A} + \gdef^^c3{\u A} + \gdef^^c4{\"A} + \gdef^^c5{\'L} + \gdef^^c6{\'C} + \gdef^^c7{\cedilla C} + \gdef^^c8{\v C} + \gdef^^c9{\'E} + \gdef^^ca{\ogonek{E}} + \gdef^^cb{\"E} + \gdef^^cc{\v E} + \gdef^^cd{\'I} + \gdef^^ce{\^I} + \gdef^^cf{\v D} + % + \gdef^^d0{\DH} + \gdef^^d1{\'N} + \gdef^^d2{\v N} + \gdef^^d3{\'O} + \gdef^^d4{\^O} + \gdef^^d5{\H O} + \gdef^^d6{\"O} + \gdef^^d7{$\times$} + \gdef^^d8{\v R} + \gdef^^d9{\ringaccent U} + \gdef^^da{\'U} + \gdef^^db{\H U} + \gdef^^dc{\"U} + \gdef^^dd{\'Y} + \gdef^^de{\cedilla T} + \gdef^^df{\ss} + % + \gdef^^e0{\'r} + \gdef^^e1{\'a} + \gdef^^e2{\^a} + \gdef^^e3{\u a} + \gdef^^e4{\"a} + \gdef^^e5{\'l} + \gdef^^e6{\'c} + \gdef^^e7{\cedilla c} + \gdef^^e8{\v c} + \gdef^^e9{\'e} + \gdef^^ea{\ogonek{e}} + \gdef^^eb{\"e} + \gdef^^ec{\v e} + \gdef^^ed{\'{\dotless{i}}} + \gdef^^ee{\^{\dotless{i}}} + \gdef^^ef{\v d} + % + \gdef^^f0{\dh} + \gdef^^f1{\'n} + \gdef^^f2{\v n} + \gdef^^f3{\'o} + \gdef^^f4{\^o} + \gdef^^f5{\H o} + \gdef^^f6{\"o} + \gdef^^f7{$\div$} + \gdef^^f8{\v r} + \gdef^^f9{\ringaccent u} + \gdef^^fa{\'u} + \gdef^^fb{\H u} + \gdef^^fc{\"u} + \gdef^^fd{\'y} + \gdef^^fe{\cedilla t} + \gdef^^ff{\dotaccent{}} +} + +% UTF-8 character definitions. +% +% This code to support UTF-8 is based on LaTeX's utf8.def, with some +% changes for Texinfo conventions. It is included here under the GPL by +% permission from Frank Mittelbach and the LaTeX team. +% +\newcount\countUTFx +\newcount\countUTFy +\newcount\countUTFz + +\gdef\UTFviiiTwoOctets#1#2{\expandafter + \UTFviiiDefined\csname u8:#1\string #2\endcsname} +% +\gdef\UTFviiiThreeOctets#1#2#3{\expandafter + \UTFviiiDefined\csname u8:#1\string #2\string #3\endcsname} +% +\gdef\UTFviiiFourOctets#1#2#3#4{\expandafter + \UTFviiiDefined\csname u8:#1\string #2\string #3\string #4\endcsname} + +\gdef\UTFviiiDefined#1{% + \ifx #1\relax + \message{\linenumber Unicode char \string #1 not defined for Texinfo}% + \else + \expandafter #1% + \fi +} + +\begingroup + \catcode`\~13 + \catcode`\"12 + + \def\UTFviiiLoop{% + \global\catcode\countUTFx\active + \uccode`\~\countUTFx + \uppercase\expandafter{\UTFviiiTmp}% + \advance\countUTFx by 1 + \ifnum\countUTFx < \countUTFy + \expandafter\UTFviiiLoop + \fi} + + \countUTFx = "C2 + \countUTFy = "E0 + \def\UTFviiiTmp{% + \xdef~{\noexpand\UTFviiiTwoOctets\string~}} + \UTFviiiLoop + + \countUTFx = "E0 + \countUTFy = "F0 + \def\UTFviiiTmp{% + \xdef~{\noexpand\UTFviiiThreeOctets\string~}} + \UTFviiiLoop + + \countUTFx = "F0 + \countUTFy = "F4 + \def\UTFviiiTmp{% + \xdef~{\noexpand\UTFviiiFourOctets\string~}} + \UTFviiiLoop +\endgroup + +\begingroup + \catcode`\"=12 + \catcode`\<=12 + \catcode`\.=12 + \catcode`\,=12 + \catcode`\;=12 + \catcode`\!=12 + \catcode`\~=13 + + \gdef\DeclareUnicodeCharacter#1#2{% + \countUTFz = "#1\relax + %\wlog{\space\space defining Unicode char U+#1 (decimal \the\countUTFz)}% + \begingroup + \parseXMLCharref + \def\UTFviiiTwoOctets##1##2{% + \csname u8:##1\string ##2\endcsname}% + \def\UTFviiiThreeOctets##1##2##3{% + \csname u8:##1\string ##2\string ##3\endcsname}% + \def\UTFviiiFourOctets##1##2##3##4{% + \csname u8:##1\string ##2\string ##3\string ##4\endcsname}% + \expandafter\expandafter\expandafter\expandafter + \expandafter\expandafter\expandafter + \gdef\UTFviiiTmp{#2}% + \endgroup} + + \gdef\parseXMLCharref{% + \ifnum\countUTFz < "A0\relax + \errhelp = \EMsimple + \errmessage{Cannot define Unicode char value < 00A0}% + \else\ifnum\countUTFz < "800\relax + \parseUTFviiiA,% + \parseUTFviiiB C\UTFviiiTwoOctets.,% + \else\ifnum\countUTFz < "10000\relax + \parseUTFviiiA;% + \parseUTFviiiA,% + \parseUTFviiiB E\UTFviiiThreeOctets.{,;}% + \else + \parseUTFviiiA;% + \parseUTFviiiA,% + \parseUTFviiiA!% + \parseUTFviiiB F\UTFviiiFourOctets.{!,;}% + \fi\fi\fi + } + + \gdef\parseUTFviiiA#1{% + \countUTFx = \countUTFz + \divide\countUTFz by 64 + \countUTFy = \countUTFz + \multiply\countUTFz by 64 + \advance\countUTFx by -\countUTFz + \advance\countUTFx by 128 + \uccode `#1\countUTFx + \countUTFz = \countUTFy} + + \gdef\parseUTFviiiB#1#2#3#4{% + \advance\countUTFz by "#10\relax + \uccode `#3\countUTFz + \uppercase{\gdef\UTFviiiTmp{#2#3#4}}} +\endgroup + +\def\utfeightchardefs{% + \DeclareUnicodeCharacter{00A0}{\tie} + \DeclareUnicodeCharacter{00A1}{\exclamdown} + \DeclareUnicodeCharacter{00A3}{\pounds} + \DeclareUnicodeCharacter{00A8}{\"{ }} + \DeclareUnicodeCharacter{00A9}{\copyright} + \DeclareUnicodeCharacter{00AA}{\ordf} + \DeclareUnicodeCharacter{00AB}{\guillemetleft} + \DeclareUnicodeCharacter{00AD}{\-} + \DeclareUnicodeCharacter{00AE}{\registeredsymbol} + \DeclareUnicodeCharacter{00AF}{\={ }} + + \DeclareUnicodeCharacter{00B0}{\ringaccent{ }} + \DeclareUnicodeCharacter{00B4}{\'{ }} + \DeclareUnicodeCharacter{00B8}{\cedilla{ }} + \DeclareUnicodeCharacter{00BA}{\ordm} + \DeclareUnicodeCharacter{00BB}{\guillemetright} + \DeclareUnicodeCharacter{00BF}{\questiondown} + + \DeclareUnicodeCharacter{00C0}{\`A} + \DeclareUnicodeCharacter{00C1}{\'A} + \DeclareUnicodeCharacter{00C2}{\^A} + \DeclareUnicodeCharacter{00C3}{\~A} + \DeclareUnicodeCharacter{00C4}{\"A} + \DeclareUnicodeCharacter{00C5}{\AA} + \DeclareUnicodeCharacter{00C6}{\AE} + \DeclareUnicodeCharacter{00C7}{\cedilla{C}} + \DeclareUnicodeCharacter{00C8}{\`E} + \DeclareUnicodeCharacter{00C9}{\'E} + \DeclareUnicodeCharacter{00CA}{\^E} + \DeclareUnicodeCharacter{00CB}{\"E} + \DeclareUnicodeCharacter{00CC}{\`I} + \DeclareUnicodeCharacter{00CD}{\'I} + \DeclareUnicodeCharacter{00CE}{\^I} + \DeclareUnicodeCharacter{00CF}{\"I} + + \DeclareUnicodeCharacter{00D0}{\DH} + \DeclareUnicodeCharacter{00D1}{\~N} + \DeclareUnicodeCharacter{00D2}{\`O} + \DeclareUnicodeCharacter{00D3}{\'O} + \DeclareUnicodeCharacter{00D4}{\^O} + \DeclareUnicodeCharacter{00D5}{\~O} + \DeclareUnicodeCharacter{00D6}{\"O} + \DeclareUnicodeCharacter{00D8}{\O} + \DeclareUnicodeCharacter{00D9}{\`U} + \DeclareUnicodeCharacter{00DA}{\'U} + \DeclareUnicodeCharacter{00DB}{\^U} + \DeclareUnicodeCharacter{00DC}{\"U} + \DeclareUnicodeCharacter{00DD}{\'Y} + \DeclareUnicodeCharacter{00DE}{\TH} + \DeclareUnicodeCharacter{00DF}{\ss} + + \DeclareUnicodeCharacter{00E0}{\`a} + \DeclareUnicodeCharacter{00E1}{\'a} + \DeclareUnicodeCharacter{00E2}{\^a} + \DeclareUnicodeCharacter{00E3}{\~a} + \DeclareUnicodeCharacter{00E4}{\"a} + \DeclareUnicodeCharacter{00E5}{\aa} + \DeclareUnicodeCharacter{00E6}{\ae} + \DeclareUnicodeCharacter{00E7}{\cedilla{c}} + \DeclareUnicodeCharacter{00E8}{\`e} + \DeclareUnicodeCharacter{00E9}{\'e} + \DeclareUnicodeCharacter{00EA}{\^e} + \DeclareUnicodeCharacter{00EB}{\"e} + \DeclareUnicodeCharacter{00EC}{\`{\dotless{i}}} + \DeclareUnicodeCharacter{00ED}{\'{\dotless{i}}} + \DeclareUnicodeCharacter{00EE}{\^{\dotless{i}}} + \DeclareUnicodeCharacter{00EF}{\"{\dotless{i}}} + + \DeclareUnicodeCharacter{00F0}{\dh} + \DeclareUnicodeCharacter{00F1}{\~n} + \DeclareUnicodeCharacter{00F2}{\`o} + \DeclareUnicodeCharacter{00F3}{\'o} + \DeclareUnicodeCharacter{00F4}{\^o} + \DeclareUnicodeCharacter{00F5}{\~o} + \DeclareUnicodeCharacter{00F6}{\"o} + \DeclareUnicodeCharacter{00F8}{\o} + \DeclareUnicodeCharacter{00F9}{\`u} + \DeclareUnicodeCharacter{00FA}{\'u} + \DeclareUnicodeCharacter{00FB}{\^u} + \DeclareUnicodeCharacter{00FC}{\"u} + \DeclareUnicodeCharacter{00FD}{\'y} + \DeclareUnicodeCharacter{00FE}{\th} + \DeclareUnicodeCharacter{00FF}{\"y} + + \DeclareUnicodeCharacter{0100}{\=A} + \DeclareUnicodeCharacter{0101}{\=a} + \DeclareUnicodeCharacter{0102}{\u{A}} + \DeclareUnicodeCharacter{0103}{\u{a}} + \DeclareUnicodeCharacter{0104}{\ogonek{A}} + \DeclareUnicodeCharacter{0105}{\ogonek{a}} + \DeclareUnicodeCharacter{0106}{\'C} + \DeclareUnicodeCharacter{0107}{\'c} + \DeclareUnicodeCharacter{0108}{\^C} + \DeclareUnicodeCharacter{0109}{\^c} + \DeclareUnicodeCharacter{0118}{\ogonek{E}} + \DeclareUnicodeCharacter{0119}{\ogonek{e}} + \DeclareUnicodeCharacter{010A}{\dotaccent{C}} + \DeclareUnicodeCharacter{010B}{\dotaccent{c}} + \DeclareUnicodeCharacter{010C}{\v{C}} + \DeclareUnicodeCharacter{010D}{\v{c}} + \DeclareUnicodeCharacter{010E}{\v{D}} + + \DeclareUnicodeCharacter{0112}{\=E} + \DeclareUnicodeCharacter{0113}{\=e} + \DeclareUnicodeCharacter{0114}{\u{E}} + \DeclareUnicodeCharacter{0115}{\u{e}} + \DeclareUnicodeCharacter{0116}{\dotaccent{E}} + \DeclareUnicodeCharacter{0117}{\dotaccent{e}} + \DeclareUnicodeCharacter{011A}{\v{E}} + \DeclareUnicodeCharacter{011B}{\v{e}} + \DeclareUnicodeCharacter{011C}{\^G} + \DeclareUnicodeCharacter{011D}{\^g} + \DeclareUnicodeCharacter{011E}{\u{G}} + \DeclareUnicodeCharacter{011F}{\u{g}} + + \DeclareUnicodeCharacter{0120}{\dotaccent{G}} + \DeclareUnicodeCharacter{0121}{\dotaccent{g}} + \DeclareUnicodeCharacter{0124}{\^H} + \DeclareUnicodeCharacter{0125}{\^h} + \DeclareUnicodeCharacter{0128}{\~I} + \DeclareUnicodeCharacter{0129}{\~{\dotless{i}}} + \DeclareUnicodeCharacter{012A}{\=I} + \DeclareUnicodeCharacter{012B}{\={\dotless{i}}} + \DeclareUnicodeCharacter{012C}{\u{I}} + \DeclareUnicodeCharacter{012D}{\u{\dotless{i}}} + + \DeclareUnicodeCharacter{0130}{\dotaccent{I}} + \DeclareUnicodeCharacter{0131}{\dotless{i}} + \DeclareUnicodeCharacter{0132}{IJ} + \DeclareUnicodeCharacter{0133}{ij} + \DeclareUnicodeCharacter{0134}{\^J} + \DeclareUnicodeCharacter{0135}{\^{\dotless{j}}} + \DeclareUnicodeCharacter{0139}{\'L} + \DeclareUnicodeCharacter{013A}{\'l} + + \DeclareUnicodeCharacter{0141}{\L} + \DeclareUnicodeCharacter{0142}{\l} + \DeclareUnicodeCharacter{0143}{\'N} + \DeclareUnicodeCharacter{0144}{\'n} + \DeclareUnicodeCharacter{0147}{\v{N}} + \DeclareUnicodeCharacter{0148}{\v{n}} + \DeclareUnicodeCharacter{014C}{\=O} + \DeclareUnicodeCharacter{014D}{\=o} + \DeclareUnicodeCharacter{014E}{\u{O}} + \DeclareUnicodeCharacter{014F}{\u{o}} + + \DeclareUnicodeCharacter{0150}{\H{O}} + \DeclareUnicodeCharacter{0151}{\H{o}} + \DeclareUnicodeCharacter{0152}{\OE} + \DeclareUnicodeCharacter{0153}{\oe} + \DeclareUnicodeCharacter{0154}{\'R} + \DeclareUnicodeCharacter{0155}{\'r} + \DeclareUnicodeCharacter{0158}{\v{R}} + \DeclareUnicodeCharacter{0159}{\v{r}} + \DeclareUnicodeCharacter{015A}{\'S} + \DeclareUnicodeCharacter{015B}{\'s} + \DeclareUnicodeCharacter{015C}{\^S} + \DeclareUnicodeCharacter{015D}{\^s} + \DeclareUnicodeCharacter{015E}{\cedilla{S}} + \DeclareUnicodeCharacter{015F}{\cedilla{s}} + + \DeclareUnicodeCharacter{0160}{\v{S}} + \DeclareUnicodeCharacter{0161}{\v{s}} + \DeclareUnicodeCharacter{0162}{\cedilla{t}} + \DeclareUnicodeCharacter{0163}{\cedilla{T}} + \DeclareUnicodeCharacter{0164}{\v{T}} + + \DeclareUnicodeCharacter{0168}{\~U} + \DeclareUnicodeCharacter{0169}{\~u} + \DeclareUnicodeCharacter{016A}{\=U} + \DeclareUnicodeCharacter{016B}{\=u} + \DeclareUnicodeCharacter{016C}{\u{U}} + \DeclareUnicodeCharacter{016D}{\u{u}} + \DeclareUnicodeCharacter{016E}{\ringaccent{U}} + \DeclareUnicodeCharacter{016F}{\ringaccent{u}} + + \DeclareUnicodeCharacter{0170}{\H{U}} + \DeclareUnicodeCharacter{0171}{\H{u}} + \DeclareUnicodeCharacter{0174}{\^W} + \DeclareUnicodeCharacter{0175}{\^w} + \DeclareUnicodeCharacter{0176}{\^Y} + \DeclareUnicodeCharacter{0177}{\^y} + \DeclareUnicodeCharacter{0178}{\"Y} + \DeclareUnicodeCharacter{0179}{\'Z} + \DeclareUnicodeCharacter{017A}{\'z} + \DeclareUnicodeCharacter{017B}{\dotaccent{Z}} + \DeclareUnicodeCharacter{017C}{\dotaccent{z}} + \DeclareUnicodeCharacter{017D}{\v{Z}} + \DeclareUnicodeCharacter{017E}{\v{z}} + + \DeclareUnicodeCharacter{01C4}{D\v{Z}} + \DeclareUnicodeCharacter{01C5}{D\v{z}} + \DeclareUnicodeCharacter{01C6}{d\v{z}} + \DeclareUnicodeCharacter{01C7}{LJ} + \DeclareUnicodeCharacter{01C8}{Lj} + \DeclareUnicodeCharacter{01C9}{lj} + \DeclareUnicodeCharacter{01CA}{NJ} + \DeclareUnicodeCharacter{01CB}{Nj} + \DeclareUnicodeCharacter{01CC}{nj} + \DeclareUnicodeCharacter{01CD}{\v{A}} + \DeclareUnicodeCharacter{01CE}{\v{a}} + \DeclareUnicodeCharacter{01CF}{\v{I}} + + \DeclareUnicodeCharacter{01D0}{\v{\dotless{i}}} + \DeclareUnicodeCharacter{01D1}{\v{O}} + \DeclareUnicodeCharacter{01D2}{\v{o}} + \DeclareUnicodeCharacter{01D3}{\v{U}} + \DeclareUnicodeCharacter{01D4}{\v{u}} + + \DeclareUnicodeCharacter{01E2}{\={\AE}} + \DeclareUnicodeCharacter{01E3}{\={\ae}} + \DeclareUnicodeCharacter{01E6}{\v{G}} + \DeclareUnicodeCharacter{01E7}{\v{g}} + \DeclareUnicodeCharacter{01E8}{\v{K}} + \DeclareUnicodeCharacter{01E9}{\v{k}} + + \DeclareUnicodeCharacter{01F0}{\v{\dotless{j}}} + \DeclareUnicodeCharacter{01F1}{DZ} + \DeclareUnicodeCharacter{01F2}{Dz} + \DeclareUnicodeCharacter{01F3}{dz} + \DeclareUnicodeCharacter{01F4}{\'G} + \DeclareUnicodeCharacter{01F5}{\'g} + \DeclareUnicodeCharacter{01F8}{\`N} + \DeclareUnicodeCharacter{01F9}{\`n} + \DeclareUnicodeCharacter{01FC}{\'{\AE}} + \DeclareUnicodeCharacter{01FD}{\'{\ae}} + \DeclareUnicodeCharacter{01FE}{\'{\O}} + \DeclareUnicodeCharacter{01FF}{\'{\o}} + + \DeclareUnicodeCharacter{021E}{\v{H}} + \DeclareUnicodeCharacter{021F}{\v{h}} + + \DeclareUnicodeCharacter{0226}{\dotaccent{A}} + \DeclareUnicodeCharacter{0227}{\dotaccent{a}} + \DeclareUnicodeCharacter{0228}{\cedilla{E}} + \DeclareUnicodeCharacter{0229}{\cedilla{e}} + \DeclareUnicodeCharacter{022E}{\dotaccent{O}} + \DeclareUnicodeCharacter{022F}{\dotaccent{o}} + + \DeclareUnicodeCharacter{0232}{\=Y} + \DeclareUnicodeCharacter{0233}{\=y} + \DeclareUnicodeCharacter{0237}{\dotless{j}} + + \DeclareUnicodeCharacter{02DB}{\ogonek{ }} + + \DeclareUnicodeCharacter{1E02}{\dotaccent{B}} + \DeclareUnicodeCharacter{1E03}{\dotaccent{b}} + \DeclareUnicodeCharacter{1E04}{\udotaccent{B}} + \DeclareUnicodeCharacter{1E05}{\udotaccent{b}} + \DeclareUnicodeCharacter{1E06}{\ubaraccent{B}} + \DeclareUnicodeCharacter{1E07}{\ubaraccent{b}} + \DeclareUnicodeCharacter{1E0A}{\dotaccent{D}} + \DeclareUnicodeCharacter{1E0B}{\dotaccent{d}} + \DeclareUnicodeCharacter{1E0C}{\udotaccent{D}} + \DeclareUnicodeCharacter{1E0D}{\udotaccent{d}} + \DeclareUnicodeCharacter{1E0E}{\ubaraccent{D}} + \DeclareUnicodeCharacter{1E0F}{\ubaraccent{d}} + + \DeclareUnicodeCharacter{1E1E}{\dotaccent{F}} + \DeclareUnicodeCharacter{1E1F}{\dotaccent{f}} + + \DeclareUnicodeCharacter{1E20}{\=G} + \DeclareUnicodeCharacter{1E21}{\=g} + \DeclareUnicodeCharacter{1E22}{\dotaccent{H}} + \DeclareUnicodeCharacter{1E23}{\dotaccent{h}} + \DeclareUnicodeCharacter{1E24}{\udotaccent{H}} + \DeclareUnicodeCharacter{1E25}{\udotaccent{h}} + \DeclareUnicodeCharacter{1E26}{\"H} + \DeclareUnicodeCharacter{1E27}{\"h} + + \DeclareUnicodeCharacter{1E30}{\'K} + \DeclareUnicodeCharacter{1E31}{\'k} + \DeclareUnicodeCharacter{1E32}{\udotaccent{K}} + \DeclareUnicodeCharacter{1E33}{\udotaccent{k}} + \DeclareUnicodeCharacter{1E34}{\ubaraccent{K}} + \DeclareUnicodeCharacter{1E35}{\ubaraccent{k}} + \DeclareUnicodeCharacter{1E36}{\udotaccent{L}} + \DeclareUnicodeCharacter{1E37}{\udotaccent{l}} + \DeclareUnicodeCharacter{1E3A}{\ubaraccent{L}} + \DeclareUnicodeCharacter{1E3B}{\ubaraccent{l}} + \DeclareUnicodeCharacter{1E3E}{\'M} + \DeclareUnicodeCharacter{1E3F}{\'m} + + \DeclareUnicodeCharacter{1E40}{\dotaccent{M}} + \DeclareUnicodeCharacter{1E41}{\dotaccent{m}} + \DeclareUnicodeCharacter{1E42}{\udotaccent{M}} + \DeclareUnicodeCharacter{1E43}{\udotaccent{m}} + \DeclareUnicodeCharacter{1E44}{\dotaccent{N}} + \DeclareUnicodeCharacter{1E45}{\dotaccent{n}} + \DeclareUnicodeCharacter{1E46}{\udotaccent{N}} + \DeclareUnicodeCharacter{1E47}{\udotaccent{n}} + \DeclareUnicodeCharacter{1E48}{\ubaraccent{N}} + \DeclareUnicodeCharacter{1E49}{\ubaraccent{n}} + + \DeclareUnicodeCharacter{1E54}{\'P} + \DeclareUnicodeCharacter{1E55}{\'p} + \DeclareUnicodeCharacter{1E56}{\dotaccent{P}} + \DeclareUnicodeCharacter{1E57}{\dotaccent{p}} + \DeclareUnicodeCharacter{1E58}{\dotaccent{R}} + \DeclareUnicodeCharacter{1E59}{\dotaccent{r}} + \DeclareUnicodeCharacter{1E5A}{\udotaccent{R}} + \DeclareUnicodeCharacter{1E5B}{\udotaccent{r}} + \DeclareUnicodeCharacter{1E5E}{\ubaraccent{R}} + \DeclareUnicodeCharacter{1E5F}{\ubaraccent{r}} + + \DeclareUnicodeCharacter{1E60}{\dotaccent{S}} + \DeclareUnicodeCharacter{1E61}{\dotaccent{s}} + \DeclareUnicodeCharacter{1E62}{\udotaccent{S}} + \DeclareUnicodeCharacter{1E63}{\udotaccent{s}} + \DeclareUnicodeCharacter{1E6A}{\dotaccent{T}} + \DeclareUnicodeCharacter{1E6B}{\dotaccent{t}} + \DeclareUnicodeCharacter{1E6C}{\udotaccent{T}} + \DeclareUnicodeCharacter{1E6D}{\udotaccent{t}} + \DeclareUnicodeCharacter{1E6E}{\ubaraccent{T}} + \DeclareUnicodeCharacter{1E6F}{\ubaraccent{t}} + + \DeclareUnicodeCharacter{1E7C}{\~V} + \DeclareUnicodeCharacter{1E7D}{\~v} + \DeclareUnicodeCharacter{1E7E}{\udotaccent{V}} + \DeclareUnicodeCharacter{1E7F}{\udotaccent{v}} + + \DeclareUnicodeCharacter{1E80}{\`W} + \DeclareUnicodeCharacter{1E81}{\`w} + \DeclareUnicodeCharacter{1E82}{\'W} + \DeclareUnicodeCharacter{1E83}{\'w} + \DeclareUnicodeCharacter{1E84}{\"W} + \DeclareUnicodeCharacter{1E85}{\"w} + \DeclareUnicodeCharacter{1E86}{\dotaccent{W}} + \DeclareUnicodeCharacter{1E87}{\dotaccent{w}} + \DeclareUnicodeCharacter{1E88}{\udotaccent{W}} + \DeclareUnicodeCharacter{1E89}{\udotaccent{w}} + \DeclareUnicodeCharacter{1E8A}{\dotaccent{X}} + \DeclareUnicodeCharacter{1E8B}{\dotaccent{x}} + \DeclareUnicodeCharacter{1E8C}{\"X} + \DeclareUnicodeCharacter{1E8D}{\"x} + \DeclareUnicodeCharacter{1E8E}{\dotaccent{Y}} + \DeclareUnicodeCharacter{1E8F}{\dotaccent{y}} + + \DeclareUnicodeCharacter{1E90}{\^Z} + \DeclareUnicodeCharacter{1E91}{\^z} + \DeclareUnicodeCharacter{1E92}{\udotaccent{Z}} + \DeclareUnicodeCharacter{1E93}{\udotaccent{z}} + \DeclareUnicodeCharacter{1E94}{\ubaraccent{Z}} + \DeclareUnicodeCharacter{1E95}{\ubaraccent{z}} + \DeclareUnicodeCharacter{1E96}{\ubaraccent{h}} + \DeclareUnicodeCharacter{1E97}{\"t} + \DeclareUnicodeCharacter{1E98}{\ringaccent{w}} + \DeclareUnicodeCharacter{1E99}{\ringaccent{y}} + + \DeclareUnicodeCharacter{1EA0}{\udotaccent{A}} + \DeclareUnicodeCharacter{1EA1}{\udotaccent{a}} + + \DeclareUnicodeCharacter{1EB8}{\udotaccent{E}} + \DeclareUnicodeCharacter{1EB9}{\udotaccent{e}} + \DeclareUnicodeCharacter{1EBC}{\~E} + \DeclareUnicodeCharacter{1EBD}{\~e} + + \DeclareUnicodeCharacter{1ECA}{\udotaccent{I}} + \DeclareUnicodeCharacter{1ECB}{\udotaccent{i}} + \DeclareUnicodeCharacter{1ECC}{\udotaccent{O}} + \DeclareUnicodeCharacter{1ECD}{\udotaccent{o}} + + \DeclareUnicodeCharacter{1EE4}{\udotaccent{U}} + \DeclareUnicodeCharacter{1EE5}{\udotaccent{u}} + + \DeclareUnicodeCharacter{1EF2}{\`Y} + \DeclareUnicodeCharacter{1EF3}{\`y} + \DeclareUnicodeCharacter{1EF4}{\udotaccent{Y}} + + \DeclareUnicodeCharacter{1EF8}{\~Y} + \DeclareUnicodeCharacter{1EF9}{\~y} + + \DeclareUnicodeCharacter{2013}{--} + \DeclareUnicodeCharacter{2014}{---} + \DeclareUnicodeCharacter{2018}{\quoteleft} + \DeclareUnicodeCharacter{2019}{\quoteright} + \DeclareUnicodeCharacter{201A}{\quotesinglbase} + \DeclareUnicodeCharacter{201C}{\quotedblleft} + \DeclareUnicodeCharacter{201D}{\quotedblright} + \DeclareUnicodeCharacter{201E}{\quotedblbase} + \DeclareUnicodeCharacter{2022}{\bullet} + \DeclareUnicodeCharacter{2026}{\dots} + \DeclareUnicodeCharacter{2039}{\guilsinglleft} + \DeclareUnicodeCharacter{203A}{\guilsinglright} + \DeclareUnicodeCharacter{20AC}{\euro} + + \DeclareUnicodeCharacter{2192}{\expansion} + \DeclareUnicodeCharacter{21D2}{\result} + + \DeclareUnicodeCharacter{2212}{\minus} + \DeclareUnicodeCharacter{2217}{\point} + \DeclareUnicodeCharacter{2261}{\equiv} +}% end of \utfeightchardefs + + +% US-ASCII character definitions. +\def\asciichardefs{% nothing need be done + \relax +} + +% Make non-ASCII characters printable again for compatibility with +% existing Texinfo documents that may use them, even without declaring a +% document encoding. +% +\setnonasciicharscatcode \other + + +\message{formatting,} + +\newdimen\defaultparindent \defaultparindent = 15pt + +\chapheadingskip = 15pt plus 4pt minus 2pt +\secheadingskip = 12pt plus 3pt minus 2pt +\subsecheadingskip = 9pt plus 2pt minus 2pt + +% Prevent underfull vbox error messages. +\vbadness = 10000 + +% Don't be very finicky about underfull hboxes, either. +\hbadness = 6666 + +% Following George Bush, get rid of widows and orphans. +\widowpenalty=10000 +\clubpenalty=10000 + +% Use TeX 3.0's \emergencystretch to help line breaking, but if we're +% using an old version of TeX, don't do anything. We want the amount of +% stretch added to depend on the line length, hence the dependence on +% \hsize. We call this whenever the paper size is set. +% +\def\setemergencystretch{% + \ifx\emergencystretch\thisisundefined + % Allow us to assign to \emergencystretch anyway. + \def\emergencystretch{\dimen0}% + \else + \emergencystretch = .15\hsize + \fi +} + +% Parameters in order: 1) textheight; 2) textwidth; +% 3) voffset; 4) hoffset; 5) binding offset; 6) topskip; +% 7) physical page height; 8) physical page width. +% +% We also call \setleading{\textleading}, so the caller should define +% \textleading. The caller should also set \parskip. +% +\def\internalpagesizes#1#2#3#4#5#6#7#8{% + \voffset = #3\relax + \topskip = #6\relax + \splittopskip = \topskip + % + \vsize = #1\relax + \advance\vsize by \topskip + \outervsize = \vsize + \advance\outervsize by 2\topandbottommargin + \pageheight = \vsize + % + \hsize = #2\relax + \outerhsize = \hsize + \advance\outerhsize by 0.5in + \pagewidth = \hsize + % + \normaloffset = #4\relax + \bindingoffset = #5\relax + % + \ifpdf + \pdfpageheight #7\relax + \pdfpagewidth #8\relax + % if we don't reset these, they will remain at "1 true in" of + % whatever layout pdftex was dumped with. + \pdfhorigin = 1 true in + \pdfvorigin = 1 true in + \fi + % + \setleading{\textleading} + % + \parindent = \defaultparindent + \setemergencystretch +} + +% @letterpaper (the default). +\def\letterpaper{{\globaldefs = 1 + \parskip = 3pt plus 2pt minus 1pt + \textleading = 13.2pt + % + % If page is nothing but text, make it come out even. + \internalpagesizes{607.2pt}{6in}% that's 46 lines + {\voffset}{.25in}% + {\bindingoffset}{36pt}% + {11in}{8.5in}% +}} + +% Use @smallbook to reset parameters for 7x9.25 trim size. +\def\smallbook{{\globaldefs = 1 + \parskip = 2pt plus 1pt + \textleading = 12pt + % + \internalpagesizes{7.5in}{5in}% + {-.2in}{0in}% + {\bindingoffset}{16pt}% + {9.25in}{7in}% + % + \lispnarrowing = 0.3in + \tolerance = 700 + \hfuzz = 1pt + \contentsrightmargin = 0pt + \defbodyindent = .5cm +}} + +% Use @smallerbook to reset parameters for 6x9 trim size. +% (Just testing, parameters still in flux.) +\def\smallerbook{{\globaldefs = 1 + \parskip = 1.5pt plus 1pt + \textleading = 12pt + % + \internalpagesizes{7.4in}{4.8in}% + {-.2in}{-.4in}% + {0pt}{14pt}% + {9in}{6in}% + % + \lispnarrowing = 0.25in + \tolerance = 700 + \hfuzz = 1pt + \contentsrightmargin = 0pt + \defbodyindent = .4cm +}} + +% Use @afourpaper to print on European A4 paper. +\def\afourpaper{{\globaldefs = 1 + \parskip = 3pt plus 2pt minus 1pt + \textleading = 13.2pt + % + % Double-side printing via postscript on Laserjet 4050 + % prints double-sided nicely when \bindingoffset=10mm and \hoffset=-6mm. + % To change the settings for a different printer or situation, adjust + % \normaloffset until the front-side and back-side texts align. Then + % do the same for \bindingoffset. You can set these for testing in + % your texinfo source file like this: + % @tex + % \global\normaloffset = -6mm + % \global\bindingoffset = 10mm + % @end tex + \internalpagesizes{673.2pt}{160mm}% that's 51 lines + {\voffset}{\hoffset}% + {\bindingoffset}{44pt}% + {297mm}{210mm}% + % + \tolerance = 700 + \hfuzz = 1pt + \contentsrightmargin = 0pt + \defbodyindent = 5mm +}} + +% Use @afivepaper to print on European A5 paper. +% From romildo@urano.iceb.ufop.br, 2 July 2000. +% He also recommends making @example and @lisp be small. +\def\afivepaper{{\globaldefs = 1 + \parskip = 2pt plus 1pt minus 0.1pt + \textleading = 12.5pt + % + \internalpagesizes{160mm}{120mm}% + {\voffset}{\hoffset}% + {\bindingoffset}{8pt}% + {210mm}{148mm}% + % + \lispnarrowing = 0.2in + \tolerance = 800 + \hfuzz = 1.2pt + \contentsrightmargin = 0pt + \defbodyindent = 2mm + \tableindent = 12mm +}} + +% A specific text layout, 24x15cm overall, intended for A4 paper. +\def\afourlatex{{\globaldefs = 1 + \afourpaper + \internalpagesizes{237mm}{150mm}% + {\voffset}{4.6mm}% + {\bindingoffset}{7mm}% + {297mm}{210mm}% + % + % Must explicitly reset to 0 because we call \afourpaper. + \globaldefs = 0 +}} + +% Use @afourwide to print on A4 paper in landscape format. +\def\afourwide{{\globaldefs = 1 + \afourpaper + \internalpagesizes{241mm}{165mm}% + {\voffset}{-2.95mm}% + {\bindingoffset}{7mm}% + {297mm}{210mm}% + \globaldefs = 0 +}} + +% @pagesizes TEXTHEIGHT[,TEXTWIDTH] +% Perhaps we should allow setting the margins, \topskip, \parskip, +% and/or leading, also. Or perhaps we should compute them somehow. +% +\parseargdef\pagesizes{\pagesizesyyy #1,,\finish} +\def\pagesizesyyy#1,#2,#3\finish{{% + \setbox0 = \hbox{\ignorespaces #2}\ifdim\wd0 > 0pt \hsize=#2\relax \fi + \globaldefs = 1 + % + \parskip = 3pt plus 2pt minus 1pt + \setleading{\textleading}% + % + \dimen0 = #1\relax + \advance\dimen0 by \voffset + % + \dimen2 = \hsize + \advance\dimen2 by \normaloffset + % + \internalpagesizes{#1}{\hsize}% + {\voffset}{\normaloffset}% + {\bindingoffset}{44pt}% + {\dimen0}{\dimen2}% +}} + +% Set default to letter. +% +\letterpaper + + +\message{and turning on texinfo input format.} + +\def^^L{\par} % remove \outer, so ^L can appear in an @comment + +% DEL is a comment character, in case @c does not suffice. +\catcode`\^^? = 14 + +% Define macros to output various characters with catcode for normal text. +\catcode`\"=\other \def\normaldoublequote{"} +\catcode`\$=\other \def\normaldollar{$}%$ font-lock fix +\catcode`\+=\other \def\normalplus{+} +\catcode`\<=\other \def\normalless{<} +\catcode`\>=\other \def\normalgreater{>} +\catcode`\^=\other \def\normalcaret{^} +\catcode`\_=\other \def\normalunderscore{_} +\catcode`\|=\other \def\normalverticalbar{|} +\catcode`\~=\other \def\normaltilde{~} + +% This macro is used to make a character print one way in \tt +% (where it can probably be output as-is), and another way in other fonts, +% where something hairier probably needs to be done. +% +% #1 is what to print if we are indeed using \tt; #2 is what to print +% otherwise. Since all the Computer Modern typewriter fonts have zero +% interword stretch (and shrink), and it is reasonable to expect all +% typewriter fonts to have this, we can check that font parameter. +% +\def\ifusingtt#1#2{\ifdim \fontdimen3\font=0pt #1\else #2\fi} + +% Same as above, but check for italic font. Actually this also catches +% non-italic slanted fonts since it is impossible to distinguish them from +% italic fonts. But since this is only used by $ and it uses \sl anyway +% this is not a problem. +\def\ifusingit#1#2{\ifdim \fontdimen1\font>0pt #1\else #2\fi} + +% Turn off all special characters except @ +% (and those which the user can use as if they were ordinary). +% Most of these we simply print from the \tt font, but for some, we can +% use math or other variants that look better in normal text. + +\catcode`\"=\active +\def\activedoublequote{{\tt\char34}} +\let"=\activedoublequote +\catcode`\~=\active +\def~{{\tt\char126}} +\chardef\hat=`\^ +\catcode`\^=\active +\def^{{\tt \hat}} + +\catcode`\_=\active +\def_{\ifusingtt\normalunderscore\_} +\let\realunder=_ +% Subroutine for the previous macro. +\def\_{\leavevmode \kern.07em \vbox{\hrule width.3em height.1ex}\kern .07em } + +\catcode`\|=\active +\def|{{\tt\char124}} +\chardef \less=`\< +\catcode`\<=\active +\def<{{\tt \less}} +\chardef \gtr=`\> +\catcode`\>=\active +\def>{{\tt \gtr}} +\catcode`\+=\active +\def+{{\tt \char 43}} +\catcode`\$=\active +\def${\ifusingit{{\sl\$}}\normaldollar}%$ font-lock fix + +% If a .fmt file is being used, characters that might appear in a file +% name cannot be active until we have parsed the command line. +% So turn them off again, and have \everyjob (or @setfilename) turn them on. +% \otherifyactive is called near the end of this file. +\def\otherifyactive{\catcode`+=\other \catcode`\_=\other} + +% Used sometimes to turn off (effectively) the active characters even after +% parsing them. +\def\turnoffactive{% + \normalturnoffactive + \otherbackslash +} + +\catcode`\@=0 + +% \backslashcurfont outputs one backslash character in current font, +% as in \char`\\. +\global\chardef\backslashcurfont=`\\ +\global\let\rawbackslashxx=\backslashcurfont % let existing .??s files work + +% \realbackslash is an actual character `\' with catcode other, and +% \doublebackslash is two of them (for the pdf outlines). +{\catcode`\\=\other @gdef@realbackslash{\} @gdef@doublebackslash{\\}} + +% In texinfo, backslash is an active character; it prints the backslash +% in fixed width font. +\catcode`\\=\active % @ for escape char from now on. + +% The story here is that in math mode, the \char of \backslashcurfont +% ends up printing the roman \ from the math symbol font (because \char +% in math mode uses the \mathcode, and plain.tex sets +% \mathcode`\\="026E). It seems better for @backslashchar{} to always +% print a typewriter backslash, hence we use an explicit \mathchar, +% which is the decimal equivalent of "715c (class 7, e.g., use \fam; +% ignored family value; char position "5C). We can't use " for the +% usual hex value because it has already been made active. +@def@normalbackslash{{@tt @ifmmode @mathchar29020 @else @backslashcurfont @fi}} +@let@backslashchar = @normalbackslash % @backslashchar{} is for user documents. + +% On startup, @fixbackslash assigns: +% @let \ = @normalbackslash +% \rawbackslash defines an active \ to do \backslashcurfont. +% \otherbackslash defines an active \ to be a literal `\' character with +% catcode other. We switch back and forth between these. +@gdef@rawbackslash{@let\=@backslashcurfont} +@gdef@otherbackslash{@let\=@realbackslash} + +% Same as @turnoffactive except outputs \ as {\tt\char`\\} instead of +% the literal character `\'. +% +@def@normalturnoffactive{% + @let"=@normaldoublequote + @let$=@normaldollar %$ font-lock fix + @let+=@normalplus + @let<=@normalless + @let>=@normalgreater + @let\=@normalbackslash + @let^=@normalcaret + @let_=@normalunderscore + @let|=@normalverticalbar + @let~=@normaltilde + @markupsetuplqdefault + @markupsetuprqdefault + @unsepspaces +} + +% Make _ and + \other characters, temporarily. +% This is canceled by @fixbackslash. +@otherifyactive + +% If a .fmt file is being used, we don't want the `\input texinfo' to show up. +% That is what \eatinput is for; after that, the `\' should revert to printing +% a backslash. +% +@gdef@eatinput input texinfo{@fixbackslash} +@global@let\ = @eatinput + +% On the other hand, perhaps the file did not have a `\input texinfo'. Then +% the first `\' in the file would cause an error. This macro tries to fix +% that, assuming it is called before the first `\' could plausibly occur. +% Also turn back on active characters that might appear in the input +% file name, in case not using a pre-dumped format. +% +@gdef@fixbackslash{% + @ifx\@eatinput @let\ = @normalbackslash @fi + @catcode`+=@active + @catcode`@_=@active +} + +% Say @foo, not \foo, in error messages. +@escapechar = `@@ + +% These (along with & and #) are made active for url-breaking, so need +% active definitions as the normal characters. +@def@normaldot{.} +@def@normalquest{?} +@def@normalslash{/} + +% These look ok in all fonts, so just make them not special. +% @hashchar{} gets its own user-level command, because of #line. +@catcode`@& = @other @def@normalamp{&} +@catcode`@# = @other @def@normalhash{#} +@catcode`@% = @other @def@normalpercent{%} + +@let @hashchar = @normalhash + +@c Finally, make ` and ' active, so that txicodequoteundirected and +@c txicodequotebacktick work right in, e.g., @w{@code{`foo'}}. If we +@c don't make ` and ' active, @code will not get them as active chars. +@c Do this last of all since we use ` in the previous @catcode assignments. +@catcode`@'=@active +@catcode`@`=@active +@markupsetuplqdefault +@markupsetuprqdefault + +@c Local variables: +@c eval: (add-hook 'write-file-hooks 'time-stamp) +@c page-delimiter: "^\\\\message" +@c time-stamp-start: "def\\\\texinfoversion{" +@c time-stamp-format: "%:y-%02m-%02d.%02H" +@c time-stamp-end: "}" +@c End: + +@c vim:sw=2: + +@ignore + arch-tag: e1b36e32-c96e-4135-a41a-0b2efa2ea115 +@end ignore From 63b662d4c8240971bd005e8a1e34c2ca89db7f78 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sat, 9 Mar 2013 12:01:19 +0100 Subject: [PATCH 002/623] Created documentation subsection --- Makefile.am | 4 +- configure.ac | 1 + doc/Makefile.am | 13 + doc/fdl-1.3.texi | 506 +++ doc/lgpl.texi | 561 +++ doc/libhttpserver.3 | 35 + doc/texinfo.tex | 9977 +++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 11095 insertions(+), 2 deletions(-) create mode 100644 doc/Makefile.am create mode 100644 doc/fdl-1.3.texi create mode 100644 doc/lgpl.texi create mode 100644 doc/libhttpserver.3 create mode 100644 doc/texinfo.tex diff --git a/Makefile.am b/Makefile.am index 05215d4c..98e12a66 100644 --- a/Makefile.am +++ b/Makefile.am @@ -24,8 +24,8 @@ LIBTOOL_DEPS = @LIBTOOL_DEPS@ AUTOMAKE_OPTIONS = foreign 1.4 ACLOCAL_AMFLAGS = -I m4 -SUBDIRS = src test -DIST_SUBDIRS = src test +SUBDIRS = src test doc +DIST_SUBDIRS = src test doc EXTRA_DIST = libhttpserver.pc.in debian/changelog.in debian/control.in debian/copyright.in debian/rules.in debian/libhttpserver-dev.install.in debian/libhttpserver.install.in redhat/libhttpserver.SPEC.in $(DX_CONFIG) MOSTLYCLEANFILES = $(DX_CLEANFILES) redhat/SOURCES/* diff --git a/configure.ac b/configure.ac index fbf8eec2..e9508870 100644 --- a/configure.ac +++ b/configure.ac @@ -147,6 +147,7 @@ AC_SUBST(EXT_LIBS) AC_OUTPUT( libhttpserver.pc Makefile + doc/Makefile src/Makefile test/Makefile debian/changelog diff --git a/doc/Makefile.am b/doc/Makefile.am new file mode 100644 index 00000000..69a8b038 --- /dev/null +++ b/doc/Makefile.am @@ -0,0 +1,13 @@ +man_MANS = libhttpserver.3 +EXTRA_DIST = $(man_MANS) + +DISTCLEANFILES = \ + libhttpserver.cps \ + libhttpserver.dvi \ + libhttpserver-tutorial.cps \ + libhttpserver-tutorial.dvi +info_TEXINFOS = \ + libhttpserver.texi +httpserver_TEXINFOS = \ + fdl-1.3.texi \ + lgpl.texi diff --git a/doc/fdl-1.3.texi b/doc/fdl-1.3.texi new file mode 100644 index 00000000..8805f1a4 --- /dev/null +++ b/doc/fdl-1.3.texi @@ -0,0 +1,506 @@ +@c The GNU Free Documentation License. +@center Version 1.3, 3 November 2008 + +@c This file is intended to be included within another document, +@c hence no sectioning command or @node. + +@display +Copyright @copyright{} 2000, 2001, 2002, 2007, 2008 Free Software Foundation, Inc. +@uref{http://fsf.org/} + +Everyone is permitted to copy and distribute verbatim copies +of this license document, but changing it is not allowed. +@end display + +@enumerate 0 +@item +PREAMBLE + +The purpose of this License is to make a manual, textbook, or other +functional and useful document @dfn{free} in the sense of freedom: to +assure everyone the effective freedom to copy and redistribute it, +with or without modifying it, either commercially or noncommercially. +Secondarily, this License preserves for the author and publisher a way +to get credit for their work, while not being considered responsible +for modifications made by others. + +This License is a kind of ``copyleft'', which means that derivative +works of the document must themselves be free in the same sense. It +complements the GNU General Public License, which is a copyleft +license designed for free software. + +We have designed this License in order to use it for manuals for free +software, because free software needs free documentation: a free +program should come with manuals providing the same freedoms that the +software does. But this License is not limited to software manuals; +it can be used for any textual work, regardless of subject matter or +whether it is published as a printed book. We recommend this License +principally for works whose purpose is instruction or reference. + +@item +APPLICABILITY AND DEFINITIONS + +This License applies to any manual or other work, in any medium, that +contains a notice placed by the copyright holder saying it can be +distributed under the terms of this License. Such a notice grants a +world-wide, royalty-free license, unlimited in duration, to use that +work under the conditions stated herein. The ``Document'', below, +refers to any such manual or work. Any member of the public is a +licensee, and is addressed as ``you''. You accept the license if you +copy, modify or distribute the work in a way requiring permission +under copyright law. + +A ``Modified Version'' of the Document means any work containing the +Document or a portion of it, either copied verbatim, or with +modifications and/or translated into another language. + +A ``Secondary Section'' is a named appendix or a front-matter section +of the Document that deals exclusively with the relationship of the +publishers or authors of the Document to the Document's overall +subject (or to related matters) and contains nothing that could fall +directly within that overall subject. (Thus, if the Document is in +part a textbook of mathematics, a Secondary Section may not explain +any mathematics.) The relationship could be a matter of historical +connection with the subject or with related matters, or of legal, +commercial, philosophical, ethical or political position regarding +them. + +The ``Invariant Sections'' are certain Secondary Sections whose titles +are designated, as being those of Invariant Sections, in the notice +that says that the Document is released under this License. If a +section does not fit the above definition of Secondary then it is not +allowed to be designated as Invariant. The Document may contain zero +Invariant Sections. If the Document does not identify any Invariant +Sections then there are none. + +The ``Cover Texts'' are certain short passages of text that are listed, +as Front-Cover Texts or Back-Cover Texts, in the notice that says that +the Document is released under this License. A Front-Cover Text may +be at most 5 words, and a Back-Cover Text may be at most 25 words. + +A ``Transparent'' copy of the Document means a machine-readable copy, +represented in a format whose specification is available to the +general public, that is suitable for revising the document +straightforwardly with generic text editors or (for images composed of +pixels) generic paint programs or (for drawings) some widely available +drawing editor, and that is suitable for input to text formatters or +for automatic translation to a variety of formats suitable for input +to text formatters. A copy made in an otherwise Transparent file +format whose markup, or absence of markup, has been arranged to thwart +or discourage subsequent modification by readers is not Transparent. +An image format is not Transparent if used for any substantial amount +of text. A copy that is not ``Transparent'' is called ``Opaque''. + +Examples of suitable formats for Transparent copies include plain +@sc{ascii} without markup, Texinfo input format, La@TeX{} input +format, @acronym{SGML} or @acronym{XML} using a publicly available +@acronym{DTD}, and standard-conforming simple @acronym{HTML}, +PostScript or @acronym{PDF} designed for human modification. Examples +of transparent image formats include @acronym{PNG}, @acronym{XCF} and +@acronym{JPG}. Opaque formats include proprietary formats that can be +read and edited only by proprietary word processors, @acronym{SGML} or +@acronym{XML} for which the @acronym{DTD} and/or processing tools are +not generally available, and the machine-generated @acronym{HTML}, +PostScript or @acronym{PDF} produced by some word processors for +output purposes only. + +The ``Title Page'' means, for a printed book, the title page itself, +plus such following pages as are needed to hold, legibly, the material +this License requires to appear in the title page. For works in +formats which do not have any title page as such, ``Title Page'' means +the text near the most prominent appearance of the work's title, +preceding the beginning of the body of the text. + +The ``publisher'' means any person or entity that distributes copies +of the Document to the public. + +A section ``Entitled XYZ'' means a named subunit of the Document whose +title either is precisely XYZ or contains XYZ in parentheses following +text that translates XYZ in another language. (Here XYZ stands for a +specific section name mentioned below, such as ``Acknowledgements'', +``Dedications'', ``Endorsements'', or ``History''.) To ``Preserve the Title'' +of such a section when you modify the Document means that it remains a +section ``Entitled XYZ'' according to this definition. + +The Document may include Warranty Disclaimers next to the notice which +states that this License applies to the Document. These Warranty +Disclaimers are considered to be included by reference in this +License, but only as regards disclaiming warranties: any other +implication that these Warranty Disclaimers may have is void and has +no effect on the meaning of this License. + +@item +VERBATIM COPYING + +You may copy and distribute the Document in any medium, either +commercially or noncommercially, provided that this License, the +copyright notices, and the license notice saying this License applies +to the Document are reproduced in all copies, and that you add no other +conditions whatsoever to those of this License. You may not use +technical measures to obstruct or control the reading or further +copying of the copies you make or distribute. However, you may accept +compensation in exchange for copies. If you distribute a large enough +number of copies you must also follow the conditions in section 3. + +You may also lend copies, under the same conditions stated above, and +you may publicly display copies. + +@item +COPYING IN QUANTITY + +If you publish printed copies (or copies in media that commonly have +printed covers) of the Document, numbering more than 100, and the +Document's license notice requires Cover Texts, you must enclose the +copies in covers that carry, clearly and legibly, all these Cover +Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on +the back cover. Both covers must also clearly and legibly identify +you as the publisher of these copies. The front cover must present +the full title with all words of the title equally prominent and +visible. You may add other material on the covers in addition. +Copying with changes limited to the covers, as long as they preserve +the title of the Document and satisfy these conditions, can be treated +as verbatim copying in other respects. + +If the required texts for either cover are too voluminous to fit +legibly, you should put the first ones listed (as many as fit +reasonably) on the actual cover, and continue the rest onto adjacent +pages. + +If you publish or distribute Opaque copies of the Document numbering +more than 100, you must either include a machine-readable Transparent +copy along with each Opaque copy, or state in or with each Opaque copy +a computer-network location from which the general network-using +public has access to download using public-standard network protocols +a complete Transparent copy of the Document, free of added material. +If you use the latter option, you must take reasonably prudent steps, +when you begin distribution of Opaque copies in quantity, to ensure +that this Transparent copy will remain thus accessible at the stated +location until at least one year after the last time you distribute an +Opaque copy (directly or through your agents or retailers) of that +edition to the public. + +It is requested, but not required, that you contact the authors of the +Document well before redistributing any large number of copies, to give +them a chance to provide you with an updated version of the Document. + +@item +MODIFICATIONS + +You may copy and distribute a Modified Version of the Document under +the conditions of sections 2 and 3 above, provided that you release +the Modified Version under precisely this License, with the Modified +Version filling the role of the Document, thus licensing distribution +and modification of the Modified Version to whoever possesses a copy +of it. In addition, you must do these things in the Modified Version: + +@enumerate A +@item +Use in the Title Page (and on the covers, if any) a title distinct +from that of the Document, and from those of previous versions +(which should, if there were any, be listed in the History section +of the Document). You may use the same title as a previous version +if the original publisher of that version gives permission. + +@item +List on the Title Page, as authors, one or more persons or entities +responsible for authorship of the modifications in the Modified +Version, together with at least five of the principal authors of the +Document (all of its principal authors, if it has fewer than five), +unless they release you from this requirement. + +@item +State on the Title page the name of the publisher of the +Modified Version, as the publisher. + +@item +Preserve all the copyright notices of the Document. + +@item +Add an appropriate copyright notice for your modifications +adjacent to the other copyright notices. + +@item +Include, immediately after the copyright notices, a license notice +giving the public permission to use the Modified Version under the +terms of this License, in the form shown in the Addendum below. + +@item +Preserve in that license notice the full lists of Invariant Sections +and required Cover Texts given in the Document's license notice. + +@item +Include an unaltered copy of this License. + +@item +Preserve the section Entitled ``History'', Preserve its Title, and add +to it an item stating at least the title, year, new authors, and +publisher of the Modified Version as given on the Title Page. If +there is no section Entitled ``History'' in the Document, create one +stating the title, year, authors, and publisher of the Document as +given on its Title Page, then add an item describing the Modified +Version as stated in the previous sentence. + +@item +Preserve the network location, if any, given in the Document for +public access to a Transparent copy of the Document, and likewise +the network locations given in the Document for previous versions +it was based on. These may be placed in the ``History'' section. +You may omit a network location for a work that was published at +least four years before the Document itself, or if the original +publisher of the version it refers to gives permission. + +@item +For any section Entitled ``Acknowledgements'' or ``Dedications'', Preserve +the Title of the section, and preserve in the section all the +substance and tone of each of the contributor acknowledgements and/or +dedications given therein. + +@item +Preserve all the Invariant Sections of the Document, +unaltered in their text and in their titles. Section numbers +or the equivalent are not considered part of the section titles. + +@item +Delete any section Entitled ``Endorsements''. Such a section +may not be included in the Modified Version. + +@item +Do not retitle any existing section to be Entitled ``Endorsements'' or +to conflict in title with any Invariant Section. + +@item +Preserve any Warranty Disclaimers. +@end enumerate + +If the Modified Version includes new front-matter sections or +appendices that qualify as Secondary Sections and contain no material +copied from the Document, you may at your option designate some or all +of these sections as invariant. To do this, add their titles to the +list of Invariant Sections in the Modified Version's license notice. +These titles must be distinct from any other section titles. + +You may add a section Entitled ``Endorsements'', provided it contains +nothing but endorsements of your Modified Version by various +parties---for example, statements of peer review or that the text has +been approved by an organization as the authoritative definition of a +standard. + +You may add a passage of up to five words as a Front-Cover Text, and a +passage of up to 25 words as a Back-Cover Text, to the end of the list +of Cover Texts in the Modified Version. Only one passage of +Front-Cover Text and one of Back-Cover Text may be added by (or +through arrangements made by) any one entity. If the Document already +includes a cover text for the same cover, previously added by you or +by arrangement made by the same entity you are acting on behalf of, +you may not add another; but you may replace the old one, on explicit +permission from the previous publisher that added the old one. + +The author(s) and publisher(s) of the Document do not by this License +give permission to use their names for publicity for or to assert or +imply endorsement of any Modified Version. + +@item +COMBINING DOCUMENTS + +You may combine the Document with other documents released under this +License, under the terms defined in section 4 above for modified +versions, provided that you include in the combination all of the +Invariant Sections of all of the original documents, unmodified, and +list them all as Invariant Sections of your combined work in its +license notice, and that you preserve all their Warranty Disclaimers. + +The combined work need only contain one copy of this License, and +multiple identical Invariant Sections may be replaced with a single +copy. If there are multiple Invariant Sections with the same name but +different contents, make the title of each such section unique by +adding at the end of it, in parentheses, the name of the original +author or publisher of that section if known, or else a unique number. +Make the same adjustment to the section titles in the list of +Invariant Sections in the license notice of the combined work. + +In the combination, you must combine any sections Entitled ``History'' +in the various original documents, forming one section Entitled +``History''; likewise combine any sections Entitled ``Acknowledgements'', +and any sections Entitled ``Dedications''. You must delete all +sections Entitled ``Endorsements.'' + +@item +COLLECTIONS OF DOCUMENTS + +You may make a collection consisting of the Document and other documents +released under this License, and replace the individual copies of this +License in the various documents with a single copy that is included in +the collection, provided that you follow the rules of this License for +verbatim copying of each of the documents in all other respects. + +You may extract a single document from such a collection, and distribute +it individually under this License, provided you insert a copy of this +License into the extracted document, and follow this License in all +other respects regarding verbatim copying of that document. + +@item +AGGREGATION WITH INDEPENDENT WORKS + +A compilation of the Document or its derivatives with other separate +and independent documents or works, in or on a volume of a storage or +distribution medium, is called an ``aggregate'' if the copyright +resulting from the compilation is not used to limit the legal rights +of the compilation's users beyond what the individual works permit. +When the Document is included in an aggregate, this License does not +apply to the other works in the aggregate which are not themselves +derivative works of the Document. + +If the Cover Text requirement of section 3 is applicable to these +copies of the Document, then if the Document is less than one half of +the entire aggregate, the Document's Cover Texts may be placed on +covers that bracket the Document within the aggregate, or the +electronic equivalent of covers if the Document is in electronic form. +Otherwise they must appear on printed covers that bracket the whole +aggregate. + +@item +TRANSLATION + +Translation is considered a kind of modification, so you may +distribute translations of the Document under the terms of section 4. +Replacing Invariant Sections with translations requires special +permission from their copyright holders, but you may include +translations of some or all Invariant Sections in addition to the +original versions of these Invariant Sections. You may include a +translation of this License, and all the license notices in the +Document, and any Warranty Disclaimers, provided that you also include +the original English version of this License and the original versions +of those notices and disclaimers. In case of a disagreement between +the translation and the original version of this License or a notice +or disclaimer, the original version will prevail. + +If a section in the Document is Entitled ``Acknowledgements'', +``Dedications'', or ``History'', the requirement (section 4) to Preserve +its Title (section 1) will typically require changing the actual +title. + +@item +TERMINATION + +You may not copy, modify, sublicense, or distribute the Document +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense, or distribute it is void, and +will automatically terminate your rights under this License. + +However, if you cease all violation of this License, then your license +from a particular copyright holder is reinstated (a) provisionally, +unless and until the copyright holder explicitly and finally +terminates your license, and (b) permanently, if the copyright holder +fails to notify you of the violation by some reasonable means prior to +60 days after the cessation. + +Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + +Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, receipt of a copy of some or all of the same material does +not give you any rights to use it. + +@item +FUTURE REVISIONS OF THIS LICENSE + +The Free Software Foundation may publish new, revised versions +of the GNU Free Documentation 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. See +@uref{http://www.gnu.org/copyleft/}. + +Each version of the License is given a distinguishing version number. +If the Document specifies that a particular numbered version of this +License ``or any later version'' applies to it, you have the option of +following the terms and conditions either of that specified version or +of any later version that has been published (not as a draft) by the +Free Software Foundation. If the Document does not specify a version +number of this License, you may choose any version ever published (not +as a draft) by the Free Software Foundation. If the Document +specifies that a proxy can decide which future versions of this +License can be used, that proxy's public statement of acceptance of a +version permanently authorizes you to choose that version for the +Document. + +@item +RELICENSING + +``Massive Multiauthor Collaboration Site'' (or ``MMC Site'') means any +World Wide Web server that publishes copyrightable works and also +provides prominent facilities for anybody to edit those works. A +public wiki that anybody can edit is an example of such a server. A +``Massive Multiauthor Collaboration'' (or ``MMC'') contained in the +site means any set of copyrightable works thus published on the MMC +site. + +``CC-BY-SA'' means the Creative Commons Attribution-Share Alike 3.0 +license published by Creative Commons Corporation, a not-for-profit +corporation with a principal place of business in San Francisco, +California, as well as future copyleft versions of that license +published by that same organization. + +``Incorporate'' means to publish or republish a Document, in whole or +in part, as part of another Document. + +An MMC is ``eligible for relicensing'' if it is licensed under this +License, and if all works that were first published under this License +somewhere other than this MMC, and subsequently incorporated in whole +or in part into the MMC, (1) had no cover texts or invariant sections, +and (2) were thus incorporated prior to November 1, 2008. + +The operator of an MMC Site may republish an MMC contained in the site +under CC-BY-SA on the same site at any time before August 1, 2009, +provided the MMC is eligible for relicensing. + +@end enumerate + +@page +@heading ADDENDUM: How to use this License for your documents + +To use this License in a document you have written, include a copy of +the License in the document and put the following copyright and +license notices just after the title page: + +@smallexample +@group + Copyright (C) @var{year} @var{your name}. + Permission is granted to copy, distribute and/or modify this document + under the terms of the GNU Free Documentation License, Version 1.3 + or any later version published by the Free Software Foundation; + with no Invariant Sections, no Front-Cover Texts, and no Back-Cover + Texts. A copy of the license is included in the section entitled ``GNU + Free Documentation License''. +@end group +@end smallexample + +If you have Invariant Sections, Front-Cover Texts and Back-Cover Texts, +replace the ``with@dots{}Texts.'' line with this: + +@smallexample +@group + with the Invariant Sections being @var{list their titles}, with + the Front-Cover Texts being @var{list}, and with the Back-Cover Texts + being @var{list}. +@end group +@end smallexample + +If you have Invariant Sections without Cover Texts, or some other +combination of the three, merge those two alternatives to suit the +situation. + +If your document contains nontrivial examples of program code, we +recommend releasing these examples in parallel under your choice of +free software license, such as the GNU General Public License, +to permit their use in free software. + +@c Local Variables: +@c ispell-local-pdict: "ispell-dict" +@c End: + diff --git a/doc/lgpl.texi b/doc/lgpl.texi new file mode 100644 index 00000000..260c0ce0 --- /dev/null +++ b/doc/lgpl.texi @@ -0,0 +1,561 @@ +@c The GNU Lesser General Public License. +@center Version 2.1, February 1999 + +@c This file is intended to be included within another document, +@c hence no sectioning command or @node. + +@display +Copyright @copyright{} 1991, 1999 Free Software Foundation, Inc. +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + +Everyone is permitted to copy and distribute verbatim copies +of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts +as the successor of the GNU Library Public License, version 2, hence the +version number 2.1.] +@end display + +@subheading Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software---to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software---typically libraries---of the Free +Software Foundation and other authors who decide to use it. You can use +it too, but we suggest you first think carefully about whether this +license or the ordinary General Public License is the better strategy to +use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +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 and use pieces of it +in new free programs; and that you are informed that you can do these +things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the @dfn{Lesser} General Public License because it +does @emph{Less} to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +``work based on the library'' and a ``work that uses the library''. The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + +@subheading TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + +@enumerate 0 +@item +This License Agreement applies to any software library or other program +which contains a notice placed by the copyright holder or other +authorized party saying it may be distributed under the terms of this +Lesser General Public License (also called ``this License''). Each +licensee is addressed as ``you''. + + A ``library'' means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The ``Library'', below, refers to any such software library or work +which has been distributed under these terms. A ``work based on the +Library'' means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term ``modification''.) + + ``Source code'' for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + +@item +You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + +@item +You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + +@enumerate a +@item +The modified work must itself be a software library. + +@item +You must cause the files modified to carry prominent notices +stating that you changed the files and the date of any change. + +@item +You must cause the whole of the work to be licensed at no +charge to all third parties under the terms of this License. + +@item +If a facility in the modified Library refers to a function or a +table of data to be supplied by an application program that uses +the facility, other than as an argument passed when the facility +is invoked, then you must make a good faith effort to ensure that, +in the event an application does not supply such function or +table, the facility still operates, and performs whatever part of +its purpose remains meaningful. + +(For example, a function in a library to compute square roots has +a purpose that is entirely well-defined independent of the +application. Therefore, Subsection 2d requires that any +application-supplied function or table used by this function must +be optional: if the application does not supply it, the square +root function must still compute square roots.) +@end enumerate + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + +@item +You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + +@item +You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + +@item +A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a ``work that uses the Library''. Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a ``work that uses the Library'' with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a ``work that uses the +library''. The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a ``work that uses the Library'' uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + +@item +As an exception to the Sections above, you may also combine or +link a ``work that uses the Library'' with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + +@enumerate a +@item +Accompany the work with the complete corresponding +machine-readable source code for the Library including whatever +changes were used in the work (which must be distributed under +Sections 1 and 2 above); and, if the work is an executable linked +with the Library, with the complete machine-readable ``work that +uses the Library'', as object code and/or source code, so that the +user can modify the Library and then relink to produce a modified +executable containing the modified Library. (It is understood +that the user who changes the contents of definitions files in the +Library will not necessarily be able to recompile the application +to use the modified definitions.) + +@item +Use a suitable shared library mechanism for linking with the Library. A +suitable mechanism is one that (1) uses at run time a copy of the +library already present on the user's computer system, rather than +copying library functions into the executable, and (2) will operate +properly with a modified version of the library, if the user installs +one, as long as the modified version is interface-compatible with the +version that the work was made with. + +@item +Accompany the work with a written offer, valid for at +least three years, to give the same user the materials +specified in Subsection 6a, above, for a charge no more +than the cost of performing this distribution. + +@item +If distribution of the work is made by offering access to copy +from a designated place, offer equivalent access to copy the above +specified materials from the same place. + +@item +Verify that the user has already received a copy of these +materials or that you have already sent this user a copy. +@end enumerate + + For an executable, the required form of the ``work that uses the +Library'' must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies the +executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + +@item +You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + +@enumerate a +@item +Accompany the combined library with a copy of the same work +based on the Library, uncombined with any other library +facilities. This must be distributed under the terms of the +Sections above. + +@item +Give prominent notice with the combined library of the fact +that part of it is a work based on the Library, and explaining +where to find the accompanying uncombined form of the same work. +@end enumerate + +@item +You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + +@item +You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + +@item +Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + +@item +If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + +@item +If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + +@item +The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +``any later version'', you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + +@item +If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + +@iftex +@heading NO WARRANTY +@end iftex +@ifinfo +@center NO WARRANTY + +@end ifinfo + +@item +BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY ``AS IS'' WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + +@item +IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. +@end enumerate + +@iftex +@heading END OF TERMS AND CONDITIONS +@end iftex +@ifinfo +@center END OF TERMS AND CONDITIONS + +@end ifinfo + +@page +@subheading How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +``copyright'' line and a pointer to where the full notice is found. + +@smallexample +@var{one line to give the library's name and an idea of what it does.} +Copyright (C) @var{year} @var{name of author} + +This library is free software; you can redistribute it and/or modify it +under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 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 +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, +USA. +@end smallexample + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a ``copyright disclaimer'' for the library, if +necessary. Here is a sample; alter the names: + +@smallexample +Yoyodyne, Inc., hereby disclaims all copyright interest in the library +`Frob' (a library for tweaking knobs) written by James Random Hacker. + +@var{signature of Ty Coon}, 1 April 1990 +Ty Coon, President of Vice +@end smallexample + +That's all there is to it! diff --git a/doc/libhttpserver.3 b/doc/libhttpserver.3 new file mode 100644 index 00000000..1133cd51 --- /dev/null +++ b/doc/libhttpserver.3 @@ -0,0 +1,35 @@ +.TH LIBHTTPSERVER "3" "02 Mar 2013 "libhttpserver" +.SH "NAME" +libhttpserver \- C++ library for creating an embedded Rest HTTP server (and more) +.SH "SYNOPSIS" + + \fB#include + +.SH "DESCRIPTION" +.P +libhttpserver is an api made with the intent to allow to easily realize Rest based webservers. +.P +The details of the API are described in a detailed documentation and in doxygen generated code reference. +.P +.SH "SEE ALSO" +\fBcurl\fP(1), \fBlibcurl\fP(3), \fBlibmicrohttpd\fP(3) + +.SH "LEGAL NOTICE" +libhttpserver is released under the LGPL Version 2.1 or higher. For details on the license please read the appendix in the manual. + +.SH "FILES" +.TP +httpserver.hpp +libhttpserver include file +.TP +libhttpserver.so +libhttpserver library + +.SH "REPORTING BUGS" +Report bugs by using github issue tracker . + +.SH "AUTHORS" +GNU libhttpserver is designed and realized by Sebastiano Merlino . + +.SH "AVAILABILITY" +You can obtain the latest version from https://github.com/etr/libhttpserver . diff --git a/doc/texinfo.tex b/doc/texinfo.tex new file mode 100644 index 00000000..85b68e79 --- /dev/null +++ b/doc/texinfo.tex @@ -0,0 +1,9977 @@ +% texinfo.tex -- TeX macros to handle Texinfo files. +% +% Load plain if necessary, i.e., if running under initex. +\expandafter\ifx\csname fmtname\endcsname\relax\input plain\fi +% +\def\texinfoversion{2012-03-11.15} +% +% Copyright 1985, 1986, 1988, 1990, 1991, 1992, 1993, 1994, 1995, +% 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, +% 2007, 2008, 2009, 2010, 2011, 2012 Free Software Foundation, Inc. +% +% This texinfo.tex file 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 3 of the +% License, or (at your option) any later version. +% +% This texinfo.tex file 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, see . +% +% As a special exception, when this file is read by TeX when processing +% a Texinfo source document, you may use the result without +% restriction. (This has been our intent since Texinfo was invented.) +% +% Please try the latest version of texinfo.tex before submitting bug +% reports; you can get the latest version from: +% http://www.gnu.org/software/texinfo/ (the Texinfo home page), or +% ftp://tug.org/tex/texinfo.tex +% (and all CTAN mirrors, see http://www.ctan.org). +% The texinfo.tex in any given distribution could well be out +% of date, so if that's what you're using, please check. +% +% Send bug reports to bug-texinfo@gnu.org. Please include including a +% complete document in each bug report with which we can reproduce the +% problem. Patches are, of course, greatly appreciated. +% +% To process a Texinfo manual with TeX, it's most reliable to use the +% texi2dvi shell script that comes with the distribution. For a simple +% manual foo.texi, however, you can get away with this: +% tex foo.texi +% texindex foo.?? +% tex foo.texi +% tex foo.texi +% dvips foo.dvi -o # or whatever; this makes foo.ps. +% The extra TeX runs get the cross-reference information correct. +% Sometimes one run after texindex suffices, and sometimes you need more +% than two; texi2dvi does it as many times as necessary. +% +% It is possible to adapt texinfo.tex for other languages, to some +% extent. You can get the existing language-specific files from the +% full Texinfo distribution. +% +% The GNU Texinfo home page is http://www.gnu.org/software/texinfo. + + +\message{Loading texinfo [version \texinfoversion]:} + +% If in a .fmt file, print the version number +% and turn on active characters that we couldn't do earlier because +% they might have appeared in the input file name. +\everyjob{\message{[Texinfo version \texinfoversion]}% + \catcode`+=\active \catcode`\_=\active} + +\chardef\other=12 + +% We never want plain's \outer definition of \+ in Texinfo. +% For @tex, we can use \tabalign. +\let\+ = \relax + +% Save some plain tex macros whose names we will redefine. +\let\ptexb=\b +\let\ptexbullet=\bullet +\let\ptexc=\c +\let\ptexcomma=\, +\let\ptexdot=\. +\let\ptexdots=\dots +\let\ptexend=\end +\let\ptexequiv=\equiv +\let\ptexexclam=\! +\let\ptexfootnote=\footnote +\let\ptexgtr=> +\let\ptexhat=^ +\let\ptexi=\i +\let\ptexindent=\indent +\let\ptexinsert=\insert +\let\ptexlbrace=\{ +\let\ptexless=< +\let\ptexnewwrite\newwrite +\let\ptexnoindent=\noindent +\let\ptexplus=+ +\let\ptexraggedright=\raggedright +\let\ptexrbrace=\} +\let\ptexslash=\/ +\let\ptexstar=\* +\let\ptext=\t +\let\ptextop=\top +{\catcode`\'=\active \global\let\ptexquoteright'}% active in plain's math mode + +% If this character appears in an error message or help string, it +% starts a new line in the output. +\newlinechar = `^^J + +% Use TeX 3.0's \inputlineno to get the line number, for better error +% messages, but if we're using an old version of TeX, don't do anything. +% +\ifx\inputlineno\thisisundefined + \let\linenumber = \empty % Pre-3.0. +\else + \def\linenumber{l.\the\inputlineno:\space} +\fi + +% Set up fixed words for English if not already set. +\ifx\putwordAppendix\undefined \gdef\putwordAppendix{Appendix}\fi +\ifx\putwordChapter\undefined \gdef\putwordChapter{Chapter}\fi +\ifx\putworderror\undefined \gdef\putworderror{error}\fi +\ifx\putwordfile\undefined \gdef\putwordfile{file}\fi +\ifx\putwordin\undefined \gdef\putwordin{in}\fi +\ifx\putwordIndexIsEmpty\undefined \gdef\putwordIndexIsEmpty{(Index is empty)}\fi +\ifx\putwordIndexNonexistent\undefined \gdef\putwordIndexNonexistent{(Index is nonexistent)}\fi +\ifx\putwordInfo\undefined \gdef\putwordInfo{Info}\fi +\ifx\putwordInstanceVariableof\undefined \gdef\putwordInstanceVariableof{Instance Variable of}\fi +\ifx\putwordMethodon\undefined \gdef\putwordMethodon{Method on}\fi +\ifx\putwordNoTitle\undefined \gdef\putwordNoTitle{No Title}\fi +\ifx\putwordof\undefined \gdef\putwordof{of}\fi +\ifx\putwordon\undefined \gdef\putwordon{on}\fi +\ifx\putwordpage\undefined \gdef\putwordpage{page}\fi +\ifx\putwordsection\undefined \gdef\putwordsection{section}\fi +\ifx\putwordSection\undefined \gdef\putwordSection{Section}\fi +\ifx\putwordsee\undefined \gdef\putwordsee{see}\fi +\ifx\putwordSee\undefined \gdef\putwordSee{See}\fi +\ifx\putwordShortTOC\undefined \gdef\putwordShortTOC{Short Contents}\fi +\ifx\putwordTOC\undefined \gdef\putwordTOC{Table of Contents}\fi +% +\ifx\putwordMJan\undefined \gdef\putwordMJan{January}\fi +\ifx\putwordMFeb\undefined \gdef\putwordMFeb{February}\fi +\ifx\putwordMMar\undefined \gdef\putwordMMar{March}\fi +\ifx\putwordMApr\undefined \gdef\putwordMApr{April}\fi +\ifx\putwordMMay\undefined \gdef\putwordMMay{May}\fi +\ifx\putwordMJun\undefined \gdef\putwordMJun{June}\fi +\ifx\putwordMJul\undefined \gdef\putwordMJul{July}\fi +\ifx\putwordMAug\undefined \gdef\putwordMAug{August}\fi +\ifx\putwordMSep\undefined \gdef\putwordMSep{September}\fi +\ifx\putwordMOct\undefined \gdef\putwordMOct{October}\fi +\ifx\putwordMNov\undefined \gdef\putwordMNov{November}\fi +\ifx\putwordMDec\undefined \gdef\putwordMDec{December}\fi +% +\ifx\putwordDefmac\undefined \gdef\putwordDefmac{Macro}\fi +\ifx\putwordDefspec\undefined \gdef\putwordDefspec{Special Form}\fi +\ifx\putwordDefvar\undefined \gdef\putwordDefvar{Variable}\fi +\ifx\putwordDefopt\undefined \gdef\putwordDefopt{User Option}\fi +\ifx\putwordDeffunc\undefined \gdef\putwordDeffunc{Function}\fi + +% Since the category of space is not known, we have to be careful. +\chardef\spacecat = 10 +\def\spaceisspace{\catcode`\ =\spacecat} + +% sometimes characters are active, so we need control sequences. +\chardef\ampChar = `\& +\chardef\colonChar = `\: +\chardef\commaChar = `\, +\chardef\dashChar = `\- +\chardef\dotChar = `\. +\chardef\exclamChar= `\! +\chardef\hashChar = `\# +\chardef\lquoteChar= `\` +\chardef\questChar = `\? +\chardef\rquoteChar= `\' +\chardef\semiChar = `\; +\chardef\slashChar = `\/ +\chardef\underChar = `\_ + +% Ignore a token. +% +\def\gobble#1{} + +% The following is used inside several \edef's. +\def\makecsname#1{\expandafter\noexpand\csname#1\endcsname} + +% Hyphenation fixes. +\hyphenation{ + Flor-i-da Ghost-script Ghost-view Mac-OS Post-Script + ap-pen-dix bit-map bit-maps + data-base data-bases eshell fall-ing half-way long-est man-u-script + man-u-scripts mini-buf-fer mini-buf-fers over-view par-a-digm + par-a-digms rath-er rec-tan-gu-lar ro-bot-ics se-vere-ly set-up spa-ces + spell-ing spell-ings + stand-alone strong-est time-stamp time-stamps which-ever white-space + wide-spread wrap-around +} + +% Margin to add to right of even pages, to left of odd pages. +\newdimen\bindingoffset +\newdimen\normaloffset +\newdimen\pagewidth \newdimen\pageheight + +% For a final copy, take out the rectangles +% that mark overfull boxes (in case you have decided +% that the text looks ok even though it passes the margin). +% +\def\finalout{\overfullrule=0pt } + +% Sometimes it is convenient to have everything in the transcript file +% and nothing on the terminal. We don't just call \tracingall here, +% since that produces some useless output on the terminal. We also make +% some effort to order the tracing commands to reduce output in the log +% file; cf. trace.sty in LaTeX. +% +\def\gloggingall{\begingroup \globaldefs = 1 \loggingall \endgroup}% +\def\loggingall{% + \tracingstats2 + \tracingpages1 + \tracinglostchars2 % 2 gives us more in etex + \tracingparagraphs1 + \tracingoutput1 + \tracingmacros2 + \tracingrestores1 + \showboxbreadth\maxdimen \showboxdepth\maxdimen + \ifx\eTeXversion\thisisundefined\else % etex gives us more logging + \tracingscantokens1 + \tracingifs1 + \tracinggroups1 + \tracingnesting2 + \tracingassigns1 + \fi + \tracingcommands3 % 3 gives us more in etex + \errorcontextlines16 +}% + +% @errormsg{MSG}. Do the index-like expansions on MSG, but if things +% aren't perfect, it's not the end of the world, being an error message, +% after all. +% +\def\errormsg{\begingroup \indexnofonts \doerrormsg} +\def\doerrormsg#1{\errmessage{#1}} + +% add check for \lastpenalty to plain's definitions. If the last thing +% we did was a \nobreak, we don't want to insert more space. +% +\def\smallbreak{\ifnum\lastpenalty<10000\par\ifdim\lastskip<\smallskipamount + \removelastskip\penalty-50\smallskip\fi\fi} +\def\medbreak{\ifnum\lastpenalty<10000\par\ifdim\lastskip<\medskipamount + \removelastskip\penalty-100\medskip\fi\fi} +\def\bigbreak{\ifnum\lastpenalty<10000\par\ifdim\lastskip<\bigskipamount + \removelastskip\penalty-200\bigskip\fi\fi} + +% Do @cropmarks to get crop marks. +% +\newif\ifcropmarks +\let\cropmarks = \cropmarkstrue +% +% Dimensions to add cropmarks at corners. +% Added by P. A. MacKay, 12 Nov. 1986 +% +\newdimen\outerhsize \newdimen\outervsize % set by the paper size routines +\newdimen\cornerlong \cornerlong=1pc +\newdimen\cornerthick \cornerthick=.3pt +\newdimen\topandbottommargin \topandbottommargin=.75in + +% Output a mark which sets \thischapter, \thissection and \thiscolor. +% We dump everything together because we only have one kind of mark. +% This works because we only use \botmark / \topmark, not \firstmark. +% +% A mark contains a subexpression of the \ifcase ... \fi construct. +% \get*marks macros below extract the needed part using \ifcase. +% +% Another complication is to let the user choose whether \thischapter +% (\thissection) refers to the chapter (section) in effect at the top +% of a page, or that at the bottom of a page. The solution is +% described on page 260 of The TeXbook. It involves outputting two +% marks for the sectioning macros, one before the section break, and +% one after. I won't pretend I can describe this better than DEK... +\def\domark{% + \toks0=\expandafter{\lastchapterdefs}% + \toks2=\expandafter{\lastsectiondefs}% + \toks4=\expandafter{\prevchapterdefs}% + \toks6=\expandafter{\prevsectiondefs}% + \toks8=\expandafter{\lastcolordefs}% + \mark{% + \the\toks0 \the\toks2 + \noexpand\or \the\toks4 \the\toks6 + \noexpand\else \the\toks8 + }% +} +% \topmark doesn't work for the very first chapter (after the title +% page or the contents), so we use \firstmark there -- this gets us +% the mark with the chapter defs, unless the user sneaks in, e.g., +% @setcolor (or @url, or @link, etc.) between @contents and the very +% first @chapter. +\def\gettopheadingmarks{% + \ifcase0\topmark\fi + \ifx\thischapter\empty \ifcase0\firstmark\fi \fi +} +\def\getbottomheadingmarks{\ifcase1\botmark\fi} +\def\getcolormarks{\ifcase2\topmark\fi} + +% Avoid "undefined control sequence" errors. +\def\lastchapterdefs{} +\def\lastsectiondefs{} +\def\prevchapterdefs{} +\def\prevsectiondefs{} +\def\lastcolordefs{} + +% Main output routine. +\chardef\PAGE = 255 +\output = {\onepageout{\pagecontents\PAGE}} + +\newbox\headlinebox +\newbox\footlinebox + +% \onepageout takes a vbox as an argument. Note that \pagecontents +% does insertions, but you have to call it yourself. +\def\onepageout#1{% + \ifcropmarks \hoffset=0pt \else \hoffset=\normaloffset \fi + % + \ifodd\pageno \advance\hoffset by \bindingoffset + \else \advance\hoffset by -\bindingoffset\fi + % + % Do this outside of the \shipout so @code etc. will be expanded in + % the headline as they should be, not taken literally (outputting ''code). + \ifodd\pageno \getoddheadingmarks \else \getevenheadingmarks \fi + \setbox\headlinebox = \vbox{\let\hsize=\pagewidth \makeheadline}% + \ifodd\pageno \getoddfootingmarks \else \getevenfootingmarks \fi + \setbox\footlinebox = \vbox{\let\hsize=\pagewidth \makefootline}% + % + {% + % Have to do this stuff outside the \shipout because we want it to + % take effect in \write's, yet the group defined by the \vbox ends + % before the \shipout runs. + % + \indexdummies % don't expand commands in the output. + \normalturnoffactive % \ in index entries must not stay \, e.g., if + % the page break happens to be in the middle of an example. + % We don't want .vr (or whatever) entries like this: + % \entry{{\tt \indexbackslash }acronym}{32}{\code {\acronym}} + % "\acronym" won't work when it's read back in; + % it needs to be + % {\code {{\tt \backslashcurfont }acronym} + \shipout\vbox{% + % Do this early so pdf references go to the beginning of the page. + \ifpdfmakepagedest \pdfdest name{\the\pageno} xyz\fi + % + \ifcropmarks \vbox to \outervsize\bgroup + \hsize = \outerhsize + \vskip-\topandbottommargin + \vtop to0pt{% + \line{\ewtop\hfil\ewtop}% + \nointerlineskip + \line{% + \vbox{\moveleft\cornerthick\nstop}% + \hfill + \vbox{\moveright\cornerthick\nstop}% + }% + \vss}% + \vskip\topandbottommargin + \line\bgroup + \hfil % center the page within the outer (page) hsize. + \ifodd\pageno\hskip\bindingoffset\fi + \vbox\bgroup + \fi + % + \unvbox\headlinebox + \pagebody{#1}% + \ifdim\ht\footlinebox > 0pt + % Only leave this space if the footline is nonempty. + % (We lessened \vsize for it in \oddfootingyyy.) + % The \baselineskip=24pt in plain's \makefootline has no effect. + \vskip 24pt + \unvbox\footlinebox + \fi + % + \ifcropmarks + \egroup % end of \vbox\bgroup + \hfil\egroup % end of (centering) \line\bgroup + \vskip\topandbottommargin plus1fill minus1fill + \boxmaxdepth = \cornerthick + \vbox to0pt{\vss + \line{% + \vbox{\moveleft\cornerthick\nsbot}% + \hfill + \vbox{\moveright\cornerthick\nsbot}% + }% + \nointerlineskip + \line{\ewbot\hfil\ewbot}% + }% + \egroup % \vbox from first cropmarks clause + \fi + }% end of \shipout\vbox + }% end of group with \indexdummies + \advancepageno + \ifnum\outputpenalty>-20000 \else\dosupereject\fi +} + +\newinsert\margin \dimen\margin=\maxdimen + +\def\pagebody#1{\vbox to\pageheight{\boxmaxdepth=\maxdepth #1}} +{\catcode`\@ =11 +\gdef\pagecontents#1{\ifvoid\topins\else\unvbox\topins\fi +% marginal hacks, juha@viisa.uucp (Juha Takala) +\ifvoid\margin\else % marginal info is present + \rlap{\kern\hsize\vbox to\z@{\kern1pt\box\margin \vss}}\fi +\dimen@=\dp#1\relax \unvbox#1\relax +\ifvoid\footins\else\vskip\skip\footins\footnoterule \unvbox\footins\fi +\ifr@ggedbottom \kern-\dimen@ \vfil \fi} +} + +% Here are the rules for the cropmarks. Note that they are +% offset so that the space between them is truly \outerhsize or \outervsize +% (P. A. MacKay, 12 November, 1986) +% +\def\ewtop{\vrule height\cornerthick depth0pt width\cornerlong} +\def\nstop{\vbox + {\hrule height\cornerthick depth\cornerlong width\cornerthick}} +\def\ewbot{\vrule height0pt depth\cornerthick width\cornerlong} +\def\nsbot{\vbox + {\hrule height\cornerlong depth\cornerthick width\cornerthick}} + +% Parse an argument, then pass it to #1. The argument is the rest of +% the input line (except we remove a trailing comment). #1 should be a +% macro which expects an ordinary undelimited TeX argument. +% +\def\parsearg{\parseargusing{}} +\def\parseargusing#1#2{% + \def\argtorun{#2}% + \begingroup + \obeylines + \spaceisspace + #1% + \parseargline\empty% Insert the \empty token, see \finishparsearg below. +} + +{\obeylines % + \gdef\parseargline#1^^M{% + \endgroup % End of the group started in \parsearg. + \argremovecomment #1\comment\ArgTerm% + }% +} + +% First remove any @comment, then any @c comment. +\def\argremovecomment#1\comment#2\ArgTerm{\argremovec #1\c\ArgTerm} +\def\argremovec#1\c#2\ArgTerm{\argcheckspaces#1\^^M\ArgTerm} + +% Each occurrence of `\^^M' or `\^^M' is replaced by a single space. +% +% \argremovec might leave us with trailing space, e.g., +% @end itemize @c foo +% This space token undergoes the same procedure and is eventually removed +% by \finishparsearg. +% +\def\argcheckspaces#1\^^M{\argcheckspacesX#1\^^M \^^M} +\def\argcheckspacesX#1 \^^M{\argcheckspacesY#1\^^M} +\def\argcheckspacesY#1\^^M#2\^^M#3\ArgTerm{% + \def\temp{#3}% + \ifx\temp\empty + % Do not use \next, perhaps the caller of \parsearg uses it; reuse \temp: + \let\temp\finishparsearg + \else + \let\temp\argcheckspaces + \fi + % Put the space token in: + \temp#1 #3\ArgTerm +} + +% If a _delimited_ argument is enclosed in braces, they get stripped; so +% to get _exactly_ the rest of the line, we had to prevent such situation. +% We prepended an \empty token at the very beginning and we expand it now, +% just before passing the control to \argtorun. +% (Similarly, we have to think about #3 of \argcheckspacesY above: it is +% either the null string, or it ends with \^^M---thus there is no danger +% that a pair of braces would be stripped. +% +% But first, we have to remove the trailing space token. +% +\def\finishparsearg#1 \ArgTerm{\expandafter\argtorun\expandafter{#1}} + +% \parseargdef\foo{...} +% is roughly equivalent to +% \def\foo{\parsearg\Xfoo} +% \def\Xfoo#1{...} +% +% Actually, I use \csname\string\foo\endcsname, ie. \\foo, as it is my +% favourite TeX trick. --kasal, 16nov03 + +\def\parseargdef#1{% + \expandafter \doparseargdef \csname\string#1\endcsname #1% +} +\def\doparseargdef#1#2{% + \def#2{\parsearg#1}% + \def#1##1% +} + +% Several utility definitions with active space: +{ + \obeyspaces + \gdef\obeyedspace{ } + + % Make each space character in the input produce a normal interword + % space in the output. Don't allow a line break at this space, as this + % is used only in environments like @example, where each line of input + % should produce a line of output anyway. + % + \gdef\sepspaces{\obeyspaces\let =\tie} + + % If an index command is used in an @example environment, any spaces + % therein should become regular spaces in the raw index file, not the + % expansion of \tie (\leavevmode \penalty \@M \ ). + \gdef\unsepspaces{\let =\space} +} + + +\def\flushcr{\ifx\par\lisppar \def\next##1{}\else \let\next=\relax \fi \next} + +% Define the framework for environments in texinfo.tex. It's used like this: +% +% \envdef\foo{...} +% \def\Efoo{...} +% +% It's the responsibility of \envdef to insert \begingroup before the +% actual body; @end closes the group after calling \Efoo. \envdef also +% defines \thisenv, so the current environment is known; @end checks +% whether the environment name matches. The \checkenv macro can also be +% used to check whether the current environment is the one expected. +% +% Non-false conditionals (@iftex, @ifset) don't fit into this, so they +% are not treated as environments; they don't open a group. (The +% implementation of @end takes care not to call \endgroup in this +% special case.) + + +% At run-time, environments start with this: +\def\startenvironment#1{\begingroup\def\thisenv{#1}} +% initialize +\let\thisenv\empty + +% ... but they get defined via ``\envdef\foo{...}'': +\long\def\envdef#1#2{\def#1{\startenvironment#1#2}} +\def\envparseargdef#1#2{\parseargdef#1{\startenvironment#1#2}} + +% Check whether we're in the right environment: +\def\checkenv#1{% + \def\temp{#1}% + \ifx\thisenv\temp + \else + \badenverr + \fi +} + +% Environment mismatch, #1 expected: +\def\badenverr{% + \errhelp = \EMsimple + \errmessage{This command can appear only \inenvironment\temp, + not \inenvironment\thisenv}% +} +\def\inenvironment#1{% + \ifx#1\empty + outside of any environment% + \else + in environment \expandafter\string#1% + \fi +} + +% @end foo executes the definition of \Efoo. +% But first, it executes a specialized version of \checkenv +% +\parseargdef\end{% + \if 1\csname iscond.#1\endcsname + \else + % The general wording of \badenverr may not be ideal. + \expandafter\checkenv\csname#1\endcsname + \csname E#1\endcsname + \endgroup + \fi +} + +\newhelp\EMsimple{Press RETURN to continue.} + + +% Be sure we're in horizontal mode when doing a tie, since we make space +% equivalent to this in @example-like environments. Otherwise, a space +% at the beginning of a line will start with \penalty -- and +% since \penalty is valid in vertical mode, we'd end up putting the +% penalty on the vertical list instead of in the new paragraph. +{\catcode`@ = 11 + % Avoid using \@M directly, because that causes trouble + % if the definition is written into an index file. + \global\let\tiepenalty = \@M + \gdef\tie{\leavevmode\penalty\tiepenalty\ } +} + +% @: forces normal size whitespace following. +\def\:{\spacefactor=1000 } + +% @* forces a line break. +\def\*{\hfil\break\hbox{}\ignorespaces} + +% @/ allows a line break. +\let\/=\allowbreak + +% @. is an end-of-sentence period. +\def\.{.\spacefactor=\endofsentencespacefactor\space} + +% @! is an end-of-sentence bang. +\def\!{!\spacefactor=\endofsentencespacefactor\space} + +% @? is an end-of-sentence query. +\def\?{?\spacefactor=\endofsentencespacefactor\space} + +% @frenchspacing on|off says whether to put extra space after punctuation. +% +\def\onword{on} +\def\offword{off} +% +\parseargdef\frenchspacing{% + \def\temp{#1}% + \ifx\temp\onword \plainfrenchspacing + \else\ifx\temp\offword \plainnonfrenchspacing + \else + \errhelp = \EMsimple + \errmessage{Unknown @frenchspacing option `\temp', must be on|off}% + \fi\fi +} + +% @w prevents a word break. Without the \leavevmode, @w at the +% beginning of a paragraph, when TeX is still in vertical mode, would +% produce a whole line of output instead of starting the paragraph. +\def\w#1{\leavevmode\hbox{#1}} + +% @group ... @end group forces ... to be all on one page, by enclosing +% it in a TeX vbox. We use \vtop instead of \vbox to construct the box +% to keep its height that of a normal line. According to the rules for +% \topskip (p.114 of the TeXbook), the glue inserted is +% max (\topskip - \ht (first item), 0). If that height is large, +% therefore, no glue is inserted, and the space between the headline and +% the text is small, which looks bad. +% +% Another complication is that the group might be very large. This can +% cause the glue on the previous page to be unduly stretched, because it +% does not have much material. In this case, it's better to add an +% explicit \vfill so that the extra space is at the bottom. The +% threshold for doing this is if the group is more than \vfilllimit +% percent of a page (\vfilllimit can be changed inside of @tex). +% +\newbox\groupbox +\def\vfilllimit{0.7} +% +\envdef\group{% + \ifnum\catcode`\^^M=\active \else + \errhelp = \groupinvalidhelp + \errmessage{@group invalid in context where filling is enabled}% + \fi + \startsavinginserts + % + \setbox\groupbox = \vtop\bgroup + % Do @comment since we are called inside an environment such as + % @example, where each end-of-line in the input causes an + % end-of-line in the output. We don't want the end-of-line after + % the `@group' to put extra space in the output. Since @group + % should appear on a line by itself (according to the Texinfo + % manual), we don't worry about eating any user text. + \comment +} +% +% The \vtop produces a box with normal height and large depth; thus, TeX puts +% \baselineskip glue before it, and (when the next line of text is done) +% \lineskip glue after it. Thus, space below is not quite equal to space +% above. But it's pretty close. +\def\Egroup{% + % To get correct interline space between the last line of the group + % and the first line afterwards, we have to propagate \prevdepth. + \endgraf % Not \par, as it may have been set to \lisppar. + \global\dimen1 = \prevdepth + \egroup % End the \vtop. + % \dimen0 is the vertical size of the group's box. + \dimen0 = \ht\groupbox \advance\dimen0 by \dp\groupbox + % \dimen2 is how much space is left on the page (more or less). + \dimen2 = \pageheight \advance\dimen2 by -\pagetotal + % if the group doesn't fit on the current page, and it's a big big + % group, force a page break. + \ifdim \dimen0 > \dimen2 + \ifdim \pagetotal < \vfilllimit\pageheight + \page + \fi + \fi + \box\groupbox + \prevdepth = \dimen1 + \checkinserts +} +% +% TeX puts in an \escapechar (i.e., `@') at the beginning of the help +% message, so this ends up printing `@group can only ...'. +% +\newhelp\groupinvalidhelp{% +group can only be used in environments such as @example,^^J% +where each line of input produces a line of output.} + +% @need space-in-mils +% forces a page break if there is not space-in-mils remaining. + +\newdimen\mil \mil=0.001in + +\parseargdef\need{% + % Ensure vertical mode, so we don't make a big box in the middle of a + % paragraph. + \par + % + % If the @need value is less than one line space, it's useless. + \dimen0 = #1\mil + \dimen2 = \ht\strutbox + \advance\dimen2 by \dp\strutbox + \ifdim\dimen0 > \dimen2 + % + % Do a \strut just to make the height of this box be normal, so the + % normal leading is inserted relative to the preceding line. + % And a page break here is fine. + \vtop to #1\mil{\strut\vfil}% + % + % TeX does not even consider page breaks if a penalty added to the + % main vertical list is 10000 or more. But in order to see if the + % empty box we just added fits on the page, we must make it consider + % page breaks. On the other hand, we don't want to actually break the + % page after the empty box. So we use a penalty of 9999. + % + % There is an extremely small chance that TeX will actually break the + % page at this \penalty, if there are no other feasible breakpoints in + % sight. (If the user is using lots of big @group commands, which + % almost-but-not-quite fill up a page, TeX will have a hard time doing + % good page breaking, for example.) However, I could not construct an + % example where a page broke at this \penalty; if it happens in a real + % document, then we can reconsider our strategy. + \penalty9999 + % + % Back up by the size of the box, whether we did a page break or not. + \kern -#1\mil + % + % Do not allow a page break right after this kern. + \nobreak + \fi +} + +% @br forces paragraph break (and is undocumented). + +\let\br = \par + +% @page forces the start of a new page. +% +\def\page{\par\vfill\supereject} + +% @exdent text.... +% outputs text on separate line in roman font, starting at standard page margin + +% This records the amount of indent in the innermost environment. +% That's how much \exdent should take out. +\newskip\exdentamount + +% This defn is used inside fill environments such as @defun. +\parseargdef\exdent{\hfil\break\hbox{\kern -\exdentamount{\rm#1}}\hfil\break} + +% This defn is used inside nofill environments such as @example. +\parseargdef\nofillexdent{{\advance \leftskip by -\exdentamount + \leftline{\hskip\leftskip{\rm#1}}}} + +% @inmargin{WHICH}{TEXT} puts TEXT in the WHICH margin next to the current +% paragraph. For more general purposes, use the \margin insertion +% class. WHICH is `l' or `r'. Not documented, written for gawk manual. +% +\newskip\inmarginspacing \inmarginspacing=1cm +\def\strutdepth{\dp\strutbox} +% +\def\doinmargin#1#2{\strut\vadjust{% + \nobreak + \kern-\strutdepth + \vtop to \strutdepth{% + \baselineskip=\strutdepth + \vss + % if you have multiple lines of stuff to put here, you'll need to + % make the vbox yourself of the appropriate size. + \ifx#1l% + \llap{\ignorespaces #2\hskip\inmarginspacing}% + \else + \rlap{\hskip\hsize \hskip\inmarginspacing \ignorespaces #2}% + \fi + \null + }% +}} +\def\inleftmargin{\doinmargin l} +\def\inrightmargin{\doinmargin r} +% +% @inmargin{TEXT [, RIGHT-TEXT]} +% (if RIGHT-TEXT is given, use TEXT for left page, RIGHT-TEXT for right; +% else use TEXT for both). +% +\def\inmargin#1{\parseinmargin #1,,\finish} +\def\parseinmargin#1,#2,#3\finish{% not perfect, but better than nothing. + \setbox0 = \hbox{\ignorespaces #2}% + \ifdim\wd0 > 0pt + \def\lefttext{#1}% have both texts + \def\righttext{#2}% + \else + \def\lefttext{#1}% have only one text + \def\righttext{#1}% + \fi + % + \ifodd\pageno + \def\temp{\inrightmargin\righttext}% odd page -> outside is right margin + \else + \def\temp{\inleftmargin\lefttext}% + \fi + \temp +} + +% @| inserts a changebar to the left of the current line. It should +% surround any changed text. This approach does *not* work if the +% change spans more than two lines of output. To handle that, we would +% have adopt a much more difficult approach (putting marks into the main +% vertical list for the beginning and end of each change). This command +% is not documented, not supported, and doesn't work. +% +\def\|{% + % \vadjust can only be used in horizontal mode. + \leavevmode + % + % Append this vertical mode material after the current line in the output. + \vadjust{% + % We want to insert a rule with the height and depth of the current + % leading; that is exactly what \strutbox is supposed to record. + \vskip-\baselineskip + % + % \vadjust-items are inserted at the left edge of the type. So + % the \llap here moves out into the left-hand margin. + \llap{% + % + % For a thicker or thinner bar, change the `1pt'. + \vrule height\baselineskip width1pt + % + % This is the space between the bar and the text. + \hskip 12pt + }% + }% +} + +% @include FILE -- \input text of FILE. +% +\def\include{\parseargusing\filenamecatcodes\includezzz} +\def\includezzz#1{% + \pushthisfilestack + \def\thisfile{#1}% + {% + \makevalueexpandable % we want to expand any @value in FILE. + \turnoffactive % and allow special characters in the expansion + \indexnofonts % Allow `@@' and other weird things in file names. + \wlog{texinfo.tex: doing @include of #1^^J}% + \edef\temp{\noexpand\input #1 }% + % + % This trickery is to read FILE outside of a group, in case it makes + % definitions, etc. + \expandafter + }\temp + \popthisfilestack +} +\def\filenamecatcodes{% + \catcode`\\=\other + \catcode`~=\other + \catcode`^=\other + \catcode`_=\other + \catcode`|=\other + \catcode`<=\other + \catcode`>=\other + \catcode`+=\other + \catcode`-=\other + \catcode`\`=\other + \catcode`\'=\other +} + +\def\pushthisfilestack{% + \expandafter\pushthisfilestackX\popthisfilestack\StackTerm +} +\def\pushthisfilestackX{% + \expandafter\pushthisfilestackY\thisfile\StackTerm +} +\def\pushthisfilestackY #1\StackTerm #2\StackTerm {% + \gdef\popthisfilestack{\gdef\thisfile{#1}\gdef\popthisfilestack{#2}}% +} + +\def\popthisfilestack{\errthisfilestackempty} +\def\errthisfilestackempty{\errmessage{Internal error: + the stack of filenames is empty.}} +% +\def\thisfile{} + +% @center line +% outputs that line, centered. +% +\parseargdef\center{% + \ifhmode + \let\centersub\centerH + \else + \let\centersub\centerV + \fi + \centersub{\hfil \ignorespaces#1\unskip \hfil}% + \let\centersub\relax % don't let the definition persist, just in case +} +\def\centerH#1{{% + \hfil\break + \advance\hsize by -\leftskip + \advance\hsize by -\rightskip + \line{#1}% + \break +}} +% +\newcount\centerpenalty +\def\centerV#1{% + % The idea here is the same as in \startdefun, \cartouche, etc.: if + % @center is the first thing after a section heading, we need to wipe + % out the negative parskip inserted by \sectionheading, but still + % prevent a page break here. + \centerpenalty = \lastpenalty + \ifnum\centerpenalty>10000 \vskip\parskip \fi + \ifnum\centerpenalty>9999 \penalty\centerpenalty \fi + \line{\kern\leftskip #1\kern\rightskip}% +} + +% @sp n outputs n lines of vertical space +% +\parseargdef\sp{\vskip #1\baselineskip} + +% @comment ...line which is ignored... +% @c is the same as @comment +% @ignore ... @end ignore is another way to write a comment +% +\def\comment{\begingroup \catcode`\^^M=\other% +\catcode`\@=\other \catcode`\{=\other \catcode`\}=\other% +\commentxxx} +{\catcode`\^^M=\other \gdef\commentxxx#1^^M{\endgroup}} +% +\let\c=\comment + +% @paragraphindent NCHARS +% We'll use ems for NCHARS, close enough. +% NCHARS can also be the word `asis' or `none'. +% We cannot feasibly implement @paragraphindent asis, though. +% +\def\asisword{asis} % no translation, these are keywords +\def\noneword{none} +% +\parseargdef\paragraphindent{% + \def\temp{#1}% + \ifx\temp\asisword + \else + \ifx\temp\noneword + \defaultparindent = 0pt + \else + \defaultparindent = #1em + \fi + \fi + \parindent = \defaultparindent +} + +% @exampleindent NCHARS +% We'll use ems for NCHARS like @paragraphindent. +% It seems @exampleindent asis isn't necessary, but +% I preserve it to make it similar to @paragraphindent. +\parseargdef\exampleindent{% + \def\temp{#1}% + \ifx\temp\asisword + \else + \ifx\temp\noneword + \lispnarrowing = 0pt + \else + \lispnarrowing = #1em + \fi + \fi +} + +% @firstparagraphindent WORD +% If WORD is `none', then suppress indentation of the first paragraph +% after a section heading. If WORD is `insert', then do indent at such +% paragraphs. +% +% The paragraph indentation is suppressed or not by calling +% \suppressfirstparagraphindent, which the sectioning commands do. +% We switch the definition of this back and forth according to WORD. +% By default, we suppress indentation. +% +\def\suppressfirstparagraphindent{\dosuppressfirstparagraphindent} +\def\insertword{insert} +% +\parseargdef\firstparagraphindent{% + \def\temp{#1}% + \ifx\temp\noneword + \let\suppressfirstparagraphindent = \dosuppressfirstparagraphindent + \else\ifx\temp\insertword + \let\suppressfirstparagraphindent = \relax + \else + \errhelp = \EMsimple + \errmessage{Unknown @firstparagraphindent option `\temp'}% + \fi\fi +} + +% Here is how we actually suppress indentation. Redefine \everypar to +% \kern backwards by \parindent, and then reset itself to empty. +% +% We also make \indent itself not actually do anything until the next +% paragraph. +% +\gdef\dosuppressfirstparagraphindent{% + \gdef\indent{% + \restorefirstparagraphindent + \indent + }% + \gdef\noindent{% + \restorefirstparagraphindent + \noindent + }% + \global\everypar = {% + \kern -\parindent + \restorefirstparagraphindent + }% +} + +\gdef\restorefirstparagraphindent{% + \global \let \indent = \ptexindent + \global \let \noindent = \ptexnoindent + \global \everypar = {}% +} + + +% @refill is a no-op. +\let\refill=\relax + +% If working on a large document in chapters, it is convenient to +% be able to disable indexing, cross-referencing, and contents, for test runs. +% This is done with @novalidate (before @setfilename). +% +\newif\iflinks \linkstrue % by default we want the aux files. +\let\novalidate = \linksfalse + +% @setfilename is done at the beginning of every texinfo file. +% So open here the files we need to have open while reading the input. +% This makes it possible to make a .fmt file for texinfo. +\def\setfilename{% + \fixbackslash % Turn off hack to swallow `\input texinfo'. + \iflinks + \tryauxfile + % Open the new aux file. TeX will close it automatically at exit. + \immediate\openout\auxfile=\jobname.aux + \fi % \openindices needs to do some work in any case. + \openindices + \let\setfilename=\comment % Ignore extra @setfilename cmds. + % + % If texinfo.cnf is present on the system, read it. + % Useful for site-wide @afourpaper, etc. + \openin 1 texinfo.cnf + \ifeof 1 \else \input texinfo.cnf \fi + \closein 1 + % + \comment % Ignore the actual filename. +} + +% Called from \setfilename. +% +\def\openindices{% + \newindex{cp}% + \newcodeindex{fn}% + \newcodeindex{vr}% + \newcodeindex{tp}% + \newcodeindex{ky}% + \newcodeindex{pg}% +} + +% @bye. +\outer\def\bye{\pagealignmacro\tracingstats=1\ptexend} + + +\message{pdf,} +% adobe `portable' document format +\newcount\tempnum +\newcount\lnkcount +\newtoks\filename +\newcount\filenamelength +\newcount\pgn +\newtoks\toksA +\newtoks\toksB +\newtoks\toksC +\newtoks\toksD +\newbox\boxA +\newcount\countA +\newif\ifpdf +\newif\ifpdfmakepagedest + +% when pdftex is run in dvi mode, \pdfoutput is defined (so \pdfoutput=1 +% can be set). So we test for \relax and 0 as well as being undefined. +\ifx\pdfoutput\thisisundefined +\else + \ifx\pdfoutput\relax + \else + \ifcase\pdfoutput + \else + \pdftrue + \fi + \fi +\fi + +% PDF uses PostScript string constants for the names of xref targets, +% for display in the outlines, and in other places. Thus, we have to +% double any backslashes. Otherwise, a name like "\node" will be +% interpreted as a newline (\n), followed by o, d, e. Not good. +% +% See http://www.ntg.nl/pipermail/ntg-pdftex/2004-July/000654.html and +% related messages. The final outcome is that it is up to the TeX user +% to double the backslashes and otherwise make the string valid, so +% that's what we do. pdftex 1.30.0 (ca.2005) introduced a primitive to +% do this reliably, so we use it. + +% #1 is a control sequence in which to do the replacements, +% which we \xdef. +\def\txiescapepdf#1{% + \ifx\pdfescapestring\relax + % No primitive available; should we give a warning or log? + % Many times it won't matter. + \else + % The expandable \pdfescapestring primitive escapes parentheses, + % backslashes, and other special chars. + \xdef#1{\pdfescapestring{#1}}% + \fi +} + +\newhelp\nopdfimagehelp{Texinfo supports .png, .jpg, .jpeg, and .pdf images +with PDF output, and none of those formats could be found. (.eps cannot +be supported due to the design of the PDF format; use regular TeX (DVI +output) for that.)} + +\ifpdf + % + % Color manipulation macros based on pdfcolor.tex, + % except using rgb instead of cmyk; the latter is said to render as a + % very dark gray on-screen and a very dark halftone in print, instead + % of actual black. + \def\rgbDarkRed{0.50 0.09 0.12} + \def\rgbBlack{0 0 0} + % + % k sets the color for filling (usual text, etc.); + % K sets the color for stroking (thin rules, e.g., normal _'s). + \def\pdfsetcolor#1{\pdfliteral{#1 rg #1 RG}} + % + % Set color, and create a mark which defines \thiscolor accordingly, + % so that \makeheadline knows which color to restore. + \def\setcolor#1{% + \xdef\lastcolordefs{\gdef\noexpand\thiscolor{#1}}% + \domark + \pdfsetcolor{#1}% + } + % + \def\maincolor{\rgbBlack} + \pdfsetcolor{\maincolor} + \edef\thiscolor{\maincolor} + \def\lastcolordefs{} + % + \def\makefootline{% + \baselineskip24pt + \line{\pdfsetcolor{\maincolor}\the\footline}% + } + % + \def\makeheadline{% + \vbox to 0pt{% + \vskip-22.5pt + \line{% + \vbox to8.5pt{}% + % Extract \thiscolor definition from the marks. + \getcolormarks + % Typeset the headline with \maincolor, then restore the color. + \pdfsetcolor{\maincolor}\the\headline\pdfsetcolor{\thiscolor}% + }% + \vss + }% + \nointerlineskip + } + % + % + \pdfcatalog{/PageMode /UseOutlines} + % + % #1 is image name, #2 width (might be empty/whitespace), #3 height (ditto). + \def\dopdfimage#1#2#3{% + \def\pdfimagewidth{#2}\setbox0 = \hbox{\ignorespaces #2}% + \def\pdfimageheight{#3}\setbox2 = \hbox{\ignorespaces #3}% + % + % pdftex (and the PDF format) support .pdf, .png, .jpg (among + % others). Let's try in that order, PDF first since if + % someone has a scalable image, presumably better to use that than a + % bitmap. + \let\pdfimgext=\empty + \begingroup + \openin 1 #1.pdf \ifeof 1 + \openin 1 #1.PDF \ifeof 1 + \openin 1 #1.png \ifeof 1 + \openin 1 #1.jpg \ifeof 1 + \openin 1 #1.jpeg \ifeof 1 + \openin 1 #1.JPG \ifeof 1 + \errhelp = \nopdfimagehelp + \errmessage{Could not find image file #1 for pdf}% + \else \gdef\pdfimgext{JPG}% + \fi + \else \gdef\pdfimgext{jpeg}% + \fi + \else \gdef\pdfimgext{jpg}% + \fi + \else \gdef\pdfimgext{png}% + \fi + \else \gdef\pdfimgext{PDF}% + \fi + \else \gdef\pdfimgext{pdf}% + \fi + \closein 1 + \endgroup + % + % without \immediate, ancient pdftex seg faults when the same image is + % included twice. (Version 3.14159-pre-1.0-unofficial-20010704.) + \ifnum\pdftexversion < 14 + \immediate\pdfimage + \else + \immediate\pdfximage + \fi + \ifdim \wd0 >0pt width \pdfimagewidth \fi + \ifdim \wd2 >0pt height \pdfimageheight \fi + \ifnum\pdftexversion<13 + #1.\pdfimgext + \else + {#1.\pdfimgext}% + \fi + \ifnum\pdftexversion < 14 \else + \pdfrefximage \pdflastximage + \fi} + % + \def\pdfmkdest#1{{% + % We have to set dummies so commands such as @code, and characters + % such as \, aren't expanded when present in a section title. + \indexnofonts + \turnoffactive + \makevalueexpandable + \def\pdfdestname{#1}% + \txiescapepdf\pdfdestname + \safewhatsit{\pdfdest name{\pdfdestname} xyz}% + }} + % + % used to mark target names; must be expandable. + \def\pdfmkpgn#1{#1} + % + % by default, use a color that is dark enough to print on paper as + % nearly black, but still distinguishable for online viewing. + \def\urlcolor{\rgbDarkRed} + \def\linkcolor{\rgbDarkRed} + \def\endlink{\setcolor{\maincolor}\pdfendlink} + % + % Adding outlines to PDF; macros for calculating structure of outlines + % come from Petr Olsak + \def\expnumber#1{\expandafter\ifx\csname#1\endcsname\relax 0% + \else \csname#1\endcsname \fi} + \def\advancenumber#1{\tempnum=\expnumber{#1}\relax + \advance\tempnum by 1 + \expandafter\xdef\csname#1\endcsname{\the\tempnum}} + % + % #1 is the section text, which is what will be displayed in the + % outline by the pdf viewer. #2 is the pdf expression for the number + % of subentries (or empty, for subsubsections). #3 is the node text, + % which might be empty if this toc entry had no corresponding node. + % #4 is the page number + % + \def\dopdfoutline#1#2#3#4{% + % Generate a link to the node text if that exists; else, use the + % page number. We could generate a destination for the section + % text in the case where a section has no node, but it doesn't + % seem worth the trouble, since most documents are normally structured. + \edef\pdfoutlinedest{#3}% + \ifx\pdfoutlinedest\empty + \def\pdfoutlinedest{#4}% + \else + \txiescapepdf\pdfoutlinedest + \fi + % + % Also escape PDF chars in the display string. + \edef\pdfoutlinetext{#1}% + \txiescapepdf\pdfoutlinetext + % + \pdfoutline goto name{\pdfmkpgn{\pdfoutlinedest}}#2{\pdfoutlinetext}% + } + % + \def\pdfmakeoutlines{% + \begingroup + % Read toc silently, to get counts of subentries for \pdfoutline. + \def\partentry##1##2##3##4{}% ignore parts in the outlines + \def\numchapentry##1##2##3##4{% + \def\thischapnum{##2}% + \def\thissecnum{0}% + \def\thissubsecnum{0}% + }% + \def\numsecentry##1##2##3##4{% + \advancenumber{chap\thischapnum}% + \def\thissecnum{##2}% + \def\thissubsecnum{0}% + }% + \def\numsubsecentry##1##2##3##4{% + \advancenumber{sec\thissecnum}% + \def\thissubsecnum{##2}% + }% + \def\numsubsubsecentry##1##2##3##4{% + \advancenumber{subsec\thissubsecnum}% + }% + \def\thischapnum{0}% + \def\thissecnum{0}% + \def\thissubsecnum{0}% + % + % use \def rather than \let here because we redefine \chapentry et + % al. a second time, below. + \def\appentry{\numchapentry}% + \def\appsecentry{\numsecentry}% + \def\appsubsecentry{\numsubsecentry}% + \def\appsubsubsecentry{\numsubsubsecentry}% + \def\unnchapentry{\numchapentry}% + \def\unnsecentry{\numsecentry}% + \def\unnsubsecentry{\numsubsecentry}% + \def\unnsubsubsecentry{\numsubsubsecentry}% + \readdatafile{toc}% + % + % Read toc second time, this time actually producing the outlines. + % The `-' means take the \expnumber as the absolute number of + % subentries, which we calculated on our first read of the .toc above. + % + % We use the node names as the destinations. + \def\numchapentry##1##2##3##4{% + \dopdfoutline{##1}{count-\expnumber{chap##2}}{##3}{##4}}% + \def\numsecentry##1##2##3##4{% + \dopdfoutline{##1}{count-\expnumber{sec##2}}{##3}{##4}}% + \def\numsubsecentry##1##2##3##4{% + \dopdfoutline{##1}{count-\expnumber{subsec##2}}{##3}{##4}}% + \def\numsubsubsecentry##1##2##3##4{% count is always zero + \dopdfoutline{##1}{}{##3}{##4}}% + % + % PDF outlines are displayed using system fonts, instead of + % document fonts. Therefore we cannot use special characters, + % since the encoding is unknown. For example, the eogonek from + % Latin 2 (0xea) gets translated to a | character. Info from + % Staszek Wawrykiewicz, 19 Jan 2004 04:09:24 +0100. + % + % TODO this right, we have to translate 8-bit characters to + % their "best" equivalent, based on the @documentencoding. Too + % much work for too little return. Just use the ASCII equivalents + % we use for the index sort strings. + % + \indexnofonts + \setupdatafile + % We can have normal brace characters in the PDF outlines, unlike + % Texinfo index files. So set that up. + \def\{{\lbracecharliteral}% + \def\}{\rbracecharliteral}% + \catcode`\\=\active \otherbackslash + \input \tocreadfilename + \endgroup + } + {\catcode`[=1 \catcode`]=2 + \catcode`{=\other \catcode`}=\other + \gdef\lbracecharliteral[{]% + \gdef\rbracecharliteral[}]% + ] + % + \def\skipspaces#1{\def\PP{#1}\def\D{|}% + \ifx\PP\D\let\nextsp\relax + \else\let\nextsp\skipspaces + \ifx\p\space\else\addtokens{\filename}{\PP}% + \advance\filenamelength by 1 + \fi + \fi + \nextsp} + \def\getfilename#1{% + \filenamelength=0 + % If we don't expand the argument now, \skipspaces will get + % snagged on things like "@value{foo}". + \edef\temp{#1}% + \expandafter\skipspaces\temp|\relax + } + \ifnum\pdftexversion < 14 + \let \startlink \pdfannotlink + \else + \let \startlink \pdfstartlink + \fi + % make a live url in pdf output. + \def\pdfurl#1{% + \begingroup + % it seems we really need yet another set of dummies; have not + % tried to figure out what each command should do in the context + % of @url. for now, just make @/ a no-op, that's the only one + % people have actually reported a problem with. + % + \normalturnoffactive + \def\@{@}% + \let\/=\empty + \makevalueexpandable + % do we want to go so far as to use \indexnofonts instead of just + % special-casing \var here? + \def\var##1{##1}% + % + \leavevmode\setcolor{\urlcolor}% + \startlink attr{/Border [0 0 0]}% + user{/Subtype /Link /A << /S /URI /URI (#1) >>}% + \endgroup} + \def\pdfgettoks#1.{\setbox\boxA=\hbox{\toksA={#1.}\toksB={}\maketoks}} + \def\addtokens#1#2{\edef\addtoks{\noexpand#1={\the#1#2}}\addtoks} + \def\adn#1{\addtokens{\toksC}{#1}\global\countA=1\let\next=\maketoks} + \def\poptoks#1#2|ENDTOKS|{\let\first=#1\toksD={#1}\toksA={#2}} + \def\maketoks{% + \expandafter\poptoks\the\toksA|ENDTOKS|\relax + \ifx\first0\adn0 + \else\ifx\first1\adn1 \else\ifx\first2\adn2 \else\ifx\first3\adn3 + \else\ifx\first4\adn4 \else\ifx\first5\adn5 \else\ifx\first6\adn6 + \else\ifx\first7\adn7 \else\ifx\first8\adn8 \else\ifx\first9\adn9 + \else + \ifnum0=\countA\else\makelink\fi + \ifx\first.\let\next=\done\else + \let\next=\maketoks + \addtokens{\toksB}{\the\toksD} + \ifx\first,\addtokens{\toksB}{\space}\fi + \fi + \fi\fi\fi\fi\fi\fi\fi\fi\fi\fi + \next} + \def\makelink{\addtokens{\toksB}% + {\noexpand\pdflink{\the\toksC}}\toksC={}\global\countA=0} + \def\pdflink#1{% + \startlink attr{/Border [0 0 0]} goto name{\pdfmkpgn{#1}} + \setcolor{\linkcolor}#1\endlink} + \def\done{\edef\st{\global\noexpand\toksA={\the\toksB}}\st} +\else + % non-pdf mode + \let\pdfmkdest = \gobble + \let\pdfurl = \gobble + \let\endlink = \relax + \let\setcolor = \gobble + \let\pdfsetcolor = \gobble + \let\pdfmakeoutlines = \relax +\fi % \ifx\pdfoutput + + +\message{fonts,} + +% Change the current font style to #1, remembering it in \curfontstyle. +% For now, we do not accumulate font styles: @b{@i{foo}} prints foo in +% italics, not bold italics. +% +\def\setfontstyle#1{% + \def\curfontstyle{#1}% not as a control sequence, because we are \edef'd. + \csname ten#1\endcsname % change the current font +} + +% Select #1 fonts with the current style. +% +\def\selectfonts#1{\csname #1fonts\endcsname \csname\curfontstyle\endcsname} + +\def\rm{\fam=0 \setfontstyle{rm}} +\def\it{\fam=\itfam \setfontstyle{it}} +\def\sl{\fam=\slfam \setfontstyle{sl}} +\def\bf{\fam=\bffam \setfontstyle{bf}}\def\bfstylename{bf} +\def\tt{\fam=\ttfam \setfontstyle{tt}} + +% Unfortunately, we have to override this for titles and the like, since +% in those cases "rm" is bold. Sigh. +\def\rmisbold{\rm\def\curfontstyle{bf}} + +% Texinfo sort of supports the sans serif font style, which plain TeX does not. +% So we set up a \sf. +\newfam\sffam +\def\sf{\fam=\sffam \setfontstyle{sf}} +\let\li = \sf % Sometimes we call it \li, not \sf. + +% We don't need math for this font style. +\def\ttsl{\setfontstyle{ttsl}} + + +% Default leading. +\newdimen\textleading \textleading = 13.2pt + +% Set the baselineskip to #1, and the lineskip and strut size +% correspondingly. There is no deep meaning behind these magic numbers +% used as factors; they just match (closely enough) what Knuth defined. +% +\def\lineskipfactor{.08333} +\def\strutheightpercent{.70833} +\def\strutdepthpercent {.29167} +% +% can get a sort of poor man's double spacing by redefining this. +\def\baselinefactor{1} +% +\def\setleading#1{% + \dimen0 = #1\relax + \normalbaselineskip = \baselinefactor\dimen0 + \normallineskip = \lineskipfactor\normalbaselineskip + \normalbaselines + \setbox\strutbox =\hbox{% + \vrule width0pt height\strutheightpercent\baselineskip + depth \strutdepthpercent \baselineskip + }% +} + +% PDF CMaps. See also LaTeX's t1.cmap. +% +% do nothing with this by default. +\expandafter\let\csname cmapOT1\endcsname\gobble +\expandafter\let\csname cmapOT1IT\endcsname\gobble +\expandafter\let\csname cmapOT1TT\endcsname\gobble + +% if we are producing pdf, and we have \pdffontattr, then define cmaps. +% (\pdffontattr was introduced many years ago, but people still run +% older pdftex's; it's easy to conditionalize, so we do.) +\ifpdf \ifx\pdffontattr\thisisundefined \else + \begingroup + \catcode`\^^M=\active \def^^M{^^J}% Output line endings as the ^^J char. + \catcode`\%=12 \immediate\pdfobj stream {%!PS-Adobe-3.0 Resource-CMap +%%DocumentNeededResources: ProcSet (CIDInit) +%%IncludeResource: ProcSet (CIDInit) +%%BeginResource: CMap (TeX-OT1-0) +%%Title: (TeX-OT1-0 TeX OT1 0) +%%Version: 1.000 +%%EndComments +/CIDInit /ProcSet findresource begin +12 dict begin +begincmap +/CIDSystemInfo +<< /Registry (TeX) +/Ordering (OT1) +/Supplement 0 +>> def +/CMapName /TeX-OT1-0 def +/CMapType 2 def +1 begincodespacerange +<00> <7F> +endcodespacerange +8 beginbfrange +<00> <01> <0393> +<09> <0A> <03A8> +<23> <26> <0023> +<28> <3B> <0028> +<3F> <5B> <003F> +<5D> <5E> <005D> +<61> <7A> <0061> +<7B> <7C> <2013> +endbfrange +40 beginbfchar +<02> <0398> +<03> <039B> +<04> <039E> +<05> <03A0> +<06> <03A3> +<07> <03D2> +<08> <03A6> +<0B> <00660066> +<0C> <00660069> +<0D> <0066006C> +<0E> <006600660069> +<0F> <00660066006C> +<10> <0131> +<11> <0237> +<12> <0060> +<13> <00B4> +<14> <02C7> +<15> <02D8> +<16> <00AF> +<17> <02DA> +<18> <00B8> +<19> <00DF> +<1A> <00E6> +<1B> <0153> +<1C> <00F8> +<1D> <00C6> +<1E> <0152> +<1F> <00D8> +<21> <0021> +<22> <201D> +<27> <2019> +<3C> <00A1> +<3D> <003D> +<3E> <00BF> +<5C> <201C> +<5F> <02D9> +<60> <2018> +<7D> <02DD> +<7E> <007E> +<7F> <00A8> +endbfchar +endcmap +CMapName currentdict /CMap defineresource pop +end +end +%%EndResource +%%EOF + }\endgroup + \expandafter\edef\csname cmapOT1\endcsname#1{% + \pdffontattr#1{/ToUnicode \the\pdflastobj\space 0 R}% + }% +% +% \cmapOT1IT + \begingroup + \catcode`\^^M=\active \def^^M{^^J}% Output line endings as the ^^J char. + \catcode`\%=12 \immediate\pdfobj stream {%!PS-Adobe-3.0 Resource-CMap +%%DocumentNeededResources: ProcSet (CIDInit) +%%IncludeResource: ProcSet (CIDInit) +%%BeginResource: CMap (TeX-OT1IT-0) +%%Title: (TeX-OT1IT-0 TeX OT1IT 0) +%%Version: 1.000 +%%EndComments +/CIDInit /ProcSet findresource begin +12 dict begin +begincmap +/CIDSystemInfo +<< /Registry (TeX) +/Ordering (OT1IT) +/Supplement 0 +>> def +/CMapName /TeX-OT1IT-0 def +/CMapType 2 def +1 begincodespacerange +<00> <7F> +endcodespacerange +8 beginbfrange +<00> <01> <0393> +<09> <0A> <03A8> +<25> <26> <0025> +<28> <3B> <0028> +<3F> <5B> <003F> +<5D> <5E> <005D> +<61> <7A> <0061> +<7B> <7C> <2013> +endbfrange +42 beginbfchar +<02> <0398> +<03> <039B> +<04> <039E> +<05> <03A0> +<06> <03A3> +<07> <03D2> +<08> <03A6> +<0B> <00660066> +<0C> <00660069> +<0D> <0066006C> +<0E> <006600660069> +<0F> <00660066006C> +<10> <0131> +<11> <0237> +<12> <0060> +<13> <00B4> +<14> <02C7> +<15> <02D8> +<16> <00AF> +<17> <02DA> +<18> <00B8> +<19> <00DF> +<1A> <00E6> +<1B> <0153> +<1C> <00F8> +<1D> <00C6> +<1E> <0152> +<1F> <00D8> +<21> <0021> +<22> <201D> +<23> <0023> +<24> <00A3> +<27> <2019> +<3C> <00A1> +<3D> <003D> +<3E> <00BF> +<5C> <201C> +<5F> <02D9> +<60> <2018> +<7D> <02DD> +<7E> <007E> +<7F> <00A8> +endbfchar +endcmap +CMapName currentdict /CMap defineresource pop +end +end +%%EndResource +%%EOF + }\endgroup + \expandafter\edef\csname cmapOT1IT\endcsname#1{% + \pdffontattr#1{/ToUnicode \the\pdflastobj\space 0 R}% + }% +% +% \cmapOT1TT + \begingroup + \catcode`\^^M=\active \def^^M{^^J}% Output line endings as the ^^J char. + \catcode`\%=12 \immediate\pdfobj stream {%!PS-Adobe-3.0 Resource-CMap +%%DocumentNeededResources: ProcSet (CIDInit) +%%IncludeResource: ProcSet (CIDInit) +%%BeginResource: CMap (TeX-OT1TT-0) +%%Title: (TeX-OT1TT-0 TeX OT1TT 0) +%%Version: 1.000 +%%EndComments +/CIDInit /ProcSet findresource begin +12 dict begin +begincmap +/CIDSystemInfo +<< /Registry (TeX) +/Ordering (OT1TT) +/Supplement 0 +>> def +/CMapName /TeX-OT1TT-0 def +/CMapType 2 def +1 begincodespacerange +<00> <7F> +endcodespacerange +5 beginbfrange +<00> <01> <0393> +<09> <0A> <03A8> +<21> <26> <0021> +<28> <5F> <0028> +<61> <7E> <0061> +endbfrange +32 beginbfchar +<02> <0398> +<03> <039B> +<04> <039E> +<05> <03A0> +<06> <03A3> +<07> <03D2> +<08> <03A6> +<0B> <2191> +<0C> <2193> +<0D> <0027> +<0E> <00A1> +<0F> <00BF> +<10> <0131> +<11> <0237> +<12> <0060> +<13> <00B4> +<14> <02C7> +<15> <02D8> +<16> <00AF> +<17> <02DA> +<18> <00B8> +<19> <00DF> +<1A> <00E6> +<1B> <0153> +<1C> <00F8> +<1D> <00C6> +<1E> <0152> +<1F> <00D8> +<20> <2423> +<27> <2019> +<60> <2018> +<7F> <00A8> +endbfchar +endcmap +CMapName currentdict /CMap defineresource pop +end +end +%%EndResource +%%EOF + }\endgroup + \expandafter\edef\csname cmapOT1TT\endcsname#1{% + \pdffontattr#1{/ToUnicode \the\pdflastobj\space 0 R}% + }% +\fi\fi + + +% Set the font macro #1 to the font named #2, adding on the +% specified font prefix (normally `cm'). +% #3 is the font's design size, #4 is a scale factor, #5 is the CMap +% encoding (currently only OT1, OT1IT and OT1TT are allowed, pass +% empty to omit). +\def\setfont#1#2#3#4#5{% + \font#1=\fontprefix#2#3 scaled #4 + \csname cmap#5\endcsname#1% +} +% This is what gets called when #5 of \setfont is empty. +\let\cmap\gobble +% emacs-page end of cmaps + +% Use cm as the default font prefix. +% To specify the font prefix, you must define \fontprefix +% before you read in texinfo.tex. +\ifx\fontprefix\thisisundefined +\def\fontprefix{cm} +\fi +% Support font families that don't use the same naming scheme as CM. +\def\rmshape{r} +\def\rmbshape{bx} %where the normal face is bold +\def\bfshape{b} +\def\bxshape{bx} +\def\ttshape{tt} +\def\ttbshape{tt} +\def\ttslshape{sltt} +\def\itshape{ti} +\def\itbshape{bxti} +\def\slshape{sl} +\def\slbshape{bxsl} +\def\sfshape{ss} +\def\sfbshape{ss} +\def\scshape{csc} +\def\scbshape{csc} + +% Definitions for a main text size of 11pt. This is the default in +% Texinfo. +% +\def\definetextfontsizexi{% +% Text fonts (11.2pt, magstep1). +\def\textnominalsize{11pt} +\edef\mainmagstep{\magstephalf} +\setfont\textrm\rmshape{10}{\mainmagstep}{OT1} +\setfont\texttt\ttshape{10}{\mainmagstep}{OT1TT} +\setfont\textbf\bfshape{10}{\mainmagstep}{OT1} +\setfont\textit\itshape{10}{\mainmagstep}{OT1IT} +\setfont\textsl\slshape{10}{\mainmagstep}{OT1} +\setfont\textsf\sfshape{10}{\mainmagstep}{OT1} +\setfont\textsc\scshape{10}{\mainmagstep}{OT1} +\setfont\textttsl\ttslshape{10}{\mainmagstep}{OT1TT} +\font\texti=cmmi10 scaled \mainmagstep +\font\textsy=cmsy10 scaled \mainmagstep +\def\textecsize{1095} + +% A few fonts for @defun names and args. +\setfont\defbf\bfshape{10}{\magstep1}{OT1} +\setfont\deftt\ttshape{10}{\magstep1}{OT1TT} +\setfont\defttsl\ttslshape{10}{\magstep1}{OT1TT} +\def\df{\let\tentt=\deftt \let\tenbf = \defbf \let\tenttsl=\defttsl \bf} + +% Fonts for indices, footnotes, small examples (9pt). +\def\smallnominalsize{9pt} +\setfont\smallrm\rmshape{9}{1000}{OT1} +\setfont\smalltt\ttshape{9}{1000}{OT1TT} +\setfont\smallbf\bfshape{10}{900}{OT1} +\setfont\smallit\itshape{9}{1000}{OT1IT} +\setfont\smallsl\slshape{9}{1000}{OT1} +\setfont\smallsf\sfshape{9}{1000}{OT1} +\setfont\smallsc\scshape{10}{900}{OT1} +\setfont\smallttsl\ttslshape{10}{900}{OT1TT} +\font\smalli=cmmi9 +\font\smallsy=cmsy9 +\def\smallecsize{0900} + +% Fonts for small examples (8pt). +\def\smallernominalsize{8pt} +\setfont\smallerrm\rmshape{8}{1000}{OT1} +\setfont\smallertt\ttshape{8}{1000}{OT1TT} +\setfont\smallerbf\bfshape{10}{800}{OT1} +\setfont\smallerit\itshape{8}{1000}{OT1IT} +\setfont\smallersl\slshape{8}{1000}{OT1} +\setfont\smallersf\sfshape{8}{1000}{OT1} +\setfont\smallersc\scshape{10}{800}{OT1} +\setfont\smallerttsl\ttslshape{10}{800}{OT1TT} +\font\smalleri=cmmi8 +\font\smallersy=cmsy8 +\def\smallerecsize{0800} + +% Fonts for title page (20.4pt): +\def\titlenominalsize{20pt} +\setfont\titlerm\rmbshape{12}{\magstep3}{OT1} +\setfont\titleit\itbshape{10}{\magstep4}{OT1IT} +\setfont\titlesl\slbshape{10}{\magstep4}{OT1} +\setfont\titlett\ttbshape{12}{\magstep3}{OT1TT} +\setfont\titlettsl\ttslshape{10}{\magstep4}{OT1TT} +\setfont\titlesf\sfbshape{17}{\magstep1}{OT1} +\let\titlebf=\titlerm +\setfont\titlesc\scbshape{10}{\magstep4}{OT1} +\font\titlei=cmmi12 scaled \magstep3 +\font\titlesy=cmsy10 scaled \magstep4 +\def\titleecsize{2074} + +% Chapter (and unnumbered) fonts (17.28pt). +\def\chapnominalsize{17pt} +\setfont\chaprm\rmbshape{12}{\magstep2}{OT1} +\setfont\chapit\itbshape{10}{\magstep3}{OT1IT} +\setfont\chapsl\slbshape{10}{\magstep3}{OT1} +\setfont\chaptt\ttbshape{12}{\magstep2}{OT1TT} +\setfont\chapttsl\ttslshape{10}{\magstep3}{OT1TT} +\setfont\chapsf\sfbshape{17}{1000}{OT1} +\let\chapbf=\chaprm +\setfont\chapsc\scbshape{10}{\magstep3}{OT1} +\font\chapi=cmmi12 scaled \magstep2 +\font\chapsy=cmsy10 scaled \magstep3 +\def\chapecsize{1728} + +% Section fonts (14.4pt). +\def\secnominalsize{14pt} +\setfont\secrm\rmbshape{12}{\magstep1}{OT1} +\setfont\secit\itbshape{10}{\magstep2}{OT1IT} +\setfont\secsl\slbshape{10}{\magstep2}{OT1} +\setfont\sectt\ttbshape{12}{\magstep1}{OT1TT} +\setfont\secttsl\ttslshape{10}{\magstep2}{OT1TT} +\setfont\secsf\sfbshape{12}{\magstep1}{OT1} +\let\secbf\secrm +\setfont\secsc\scbshape{10}{\magstep2}{OT1} +\font\seci=cmmi12 scaled \magstep1 +\font\secsy=cmsy10 scaled \magstep2 +\def\sececsize{1440} + +% Subsection fonts (13.15pt). +\def\ssecnominalsize{13pt} +\setfont\ssecrm\rmbshape{12}{\magstephalf}{OT1} +\setfont\ssecit\itbshape{10}{1315}{OT1IT} +\setfont\ssecsl\slbshape{10}{1315}{OT1} +\setfont\ssectt\ttbshape{12}{\magstephalf}{OT1TT} +\setfont\ssecttsl\ttslshape{10}{1315}{OT1TT} +\setfont\ssecsf\sfbshape{12}{\magstephalf}{OT1} +\let\ssecbf\ssecrm +\setfont\ssecsc\scbshape{10}{1315}{OT1} +\font\sseci=cmmi12 scaled \magstephalf +\font\ssecsy=cmsy10 scaled 1315 +\def\ssececsize{1200} + +% Reduced fonts for @acro in text (10pt). +\def\reducednominalsize{10pt} +\setfont\reducedrm\rmshape{10}{1000}{OT1} +\setfont\reducedtt\ttshape{10}{1000}{OT1TT} +\setfont\reducedbf\bfshape{10}{1000}{OT1} +\setfont\reducedit\itshape{10}{1000}{OT1IT} +\setfont\reducedsl\slshape{10}{1000}{OT1} +\setfont\reducedsf\sfshape{10}{1000}{OT1} +\setfont\reducedsc\scshape{10}{1000}{OT1} +\setfont\reducedttsl\ttslshape{10}{1000}{OT1TT} +\font\reducedi=cmmi10 +\font\reducedsy=cmsy10 +\def\reducedecsize{1000} + +\textleading = 13.2pt % line spacing for 11pt CM +\textfonts % reset the current fonts +\rm +} % end of 11pt text font size definitions + + +% Definitions to make the main text be 10pt Computer Modern, with +% section, chapter, etc., sizes following suit. This is for the GNU +% Press printing of the Emacs 22 manual. Maybe other manuals in the +% future. Used with @smallbook, which sets the leading to 12pt. +% +\def\definetextfontsizex{% +% Text fonts (10pt). +\def\textnominalsize{10pt} +\edef\mainmagstep{1000} +\setfont\textrm\rmshape{10}{\mainmagstep}{OT1} +\setfont\texttt\ttshape{10}{\mainmagstep}{OT1TT} +\setfont\textbf\bfshape{10}{\mainmagstep}{OT1} +\setfont\textit\itshape{10}{\mainmagstep}{OT1IT} +\setfont\textsl\slshape{10}{\mainmagstep}{OT1} +\setfont\textsf\sfshape{10}{\mainmagstep}{OT1} +\setfont\textsc\scshape{10}{\mainmagstep}{OT1} +\setfont\textttsl\ttslshape{10}{\mainmagstep}{OT1TT} +\font\texti=cmmi10 scaled \mainmagstep +\font\textsy=cmsy10 scaled \mainmagstep +\def\textecsize{1000} + +% A few fonts for @defun names and args. +\setfont\defbf\bfshape{10}{\magstephalf}{OT1} +\setfont\deftt\ttshape{10}{\magstephalf}{OT1TT} +\setfont\defttsl\ttslshape{10}{\magstephalf}{OT1TT} +\def\df{\let\tentt=\deftt \let\tenbf = \defbf \let\tenttsl=\defttsl \bf} + +% Fonts for indices, footnotes, small examples (9pt). +\def\smallnominalsize{9pt} +\setfont\smallrm\rmshape{9}{1000}{OT1} +\setfont\smalltt\ttshape{9}{1000}{OT1TT} +\setfont\smallbf\bfshape{10}{900}{OT1} +\setfont\smallit\itshape{9}{1000}{OT1IT} +\setfont\smallsl\slshape{9}{1000}{OT1} +\setfont\smallsf\sfshape{9}{1000}{OT1} +\setfont\smallsc\scshape{10}{900}{OT1} +\setfont\smallttsl\ttslshape{10}{900}{OT1TT} +\font\smalli=cmmi9 +\font\smallsy=cmsy9 +\def\smallecsize{0900} + +% Fonts for small examples (8pt). +\def\smallernominalsize{8pt} +\setfont\smallerrm\rmshape{8}{1000}{OT1} +\setfont\smallertt\ttshape{8}{1000}{OT1TT} +\setfont\smallerbf\bfshape{10}{800}{OT1} +\setfont\smallerit\itshape{8}{1000}{OT1IT} +\setfont\smallersl\slshape{8}{1000}{OT1} +\setfont\smallersf\sfshape{8}{1000}{OT1} +\setfont\smallersc\scshape{10}{800}{OT1} +\setfont\smallerttsl\ttslshape{10}{800}{OT1TT} +\font\smalleri=cmmi8 +\font\smallersy=cmsy8 +\def\smallerecsize{0800} + +% Fonts for title page (20.4pt): +\def\titlenominalsize{20pt} +\setfont\titlerm\rmbshape{12}{\magstep3}{OT1} +\setfont\titleit\itbshape{10}{\magstep4}{OT1IT} +\setfont\titlesl\slbshape{10}{\magstep4}{OT1} +\setfont\titlett\ttbshape{12}{\magstep3}{OT1TT} +\setfont\titlettsl\ttslshape{10}{\magstep4}{OT1TT} +\setfont\titlesf\sfbshape{17}{\magstep1}{OT1} +\let\titlebf=\titlerm +\setfont\titlesc\scbshape{10}{\magstep4}{OT1} +\font\titlei=cmmi12 scaled \magstep3 +\font\titlesy=cmsy10 scaled \magstep4 +\def\titleecsize{2074} + +% Chapter fonts (14.4pt). +\def\chapnominalsize{14pt} +\setfont\chaprm\rmbshape{12}{\magstep1}{OT1} +\setfont\chapit\itbshape{10}{\magstep2}{OT1IT} +\setfont\chapsl\slbshape{10}{\magstep2}{OT1} +\setfont\chaptt\ttbshape{12}{\magstep1}{OT1TT} +\setfont\chapttsl\ttslshape{10}{\magstep2}{OT1TT} +\setfont\chapsf\sfbshape{12}{\magstep1}{OT1} +\let\chapbf\chaprm +\setfont\chapsc\scbshape{10}{\magstep2}{OT1} +\font\chapi=cmmi12 scaled \magstep1 +\font\chapsy=cmsy10 scaled \magstep2 +\def\chapecsize{1440} + +% Section fonts (12pt). +\def\secnominalsize{12pt} +\setfont\secrm\rmbshape{12}{1000}{OT1} +\setfont\secit\itbshape{10}{\magstep1}{OT1IT} +\setfont\secsl\slbshape{10}{\magstep1}{OT1} +\setfont\sectt\ttbshape{12}{1000}{OT1TT} +\setfont\secttsl\ttslshape{10}{\magstep1}{OT1TT} +\setfont\secsf\sfbshape{12}{1000}{OT1} +\let\secbf\secrm +\setfont\secsc\scbshape{10}{\magstep1}{OT1} +\font\seci=cmmi12 +\font\secsy=cmsy10 scaled \magstep1 +\def\sececsize{1200} + +% Subsection fonts (10pt). +\def\ssecnominalsize{10pt} +\setfont\ssecrm\rmbshape{10}{1000}{OT1} +\setfont\ssecit\itbshape{10}{1000}{OT1IT} +\setfont\ssecsl\slbshape{10}{1000}{OT1} +\setfont\ssectt\ttbshape{10}{1000}{OT1TT} +\setfont\ssecttsl\ttslshape{10}{1000}{OT1TT} +\setfont\ssecsf\sfbshape{10}{1000}{OT1} +\let\ssecbf\ssecrm +\setfont\ssecsc\scbshape{10}{1000}{OT1} +\font\sseci=cmmi10 +\font\ssecsy=cmsy10 +\def\ssececsize{1000} + +% Reduced fonts for @acro in text (9pt). +\def\reducednominalsize{9pt} +\setfont\reducedrm\rmshape{9}{1000}{OT1} +\setfont\reducedtt\ttshape{9}{1000}{OT1TT} +\setfont\reducedbf\bfshape{10}{900}{OT1} +\setfont\reducedit\itshape{9}{1000}{OT1IT} +\setfont\reducedsl\slshape{9}{1000}{OT1} +\setfont\reducedsf\sfshape{9}{1000}{OT1} +\setfont\reducedsc\scshape{10}{900}{OT1} +\setfont\reducedttsl\ttslshape{10}{900}{OT1TT} +\font\reducedi=cmmi9 +\font\reducedsy=cmsy9 +\def\reducedecsize{0900} + +\divide\parskip by 2 % reduce space between paragraphs +\textleading = 12pt % line spacing for 10pt CM +\textfonts % reset the current fonts +\rm +} % end of 10pt text font size definitions + + +% We provide the user-level command +% @fonttextsize 10 +% (or 11) to redefine the text font size. pt is assumed. +% +\def\xiword{11} +\def\xword{10} +\def\xwordpt{10pt} +% +\parseargdef\fonttextsize{% + \def\textsizearg{#1}% + %\wlog{doing @fonttextsize \textsizearg}% + % + % Set \globaldefs so that documents can use this inside @tex, since + % makeinfo 4.8 does not support it, but we need it nonetheless. + % + \begingroup \globaldefs=1 + \ifx\textsizearg\xword \definetextfontsizex + \else \ifx\textsizearg\xiword \definetextfontsizexi + \else + \errhelp=\EMsimple + \errmessage{@fonttextsize only supports `10' or `11', not `\textsizearg'} + \fi\fi + \endgroup +} + + +% In order for the font changes to affect most math symbols and letters, +% we have to define the \textfont of the standard families. Since +% texinfo doesn't allow for producing subscripts and superscripts except +% in the main text, we don't bother to reset \scriptfont and +% \scriptscriptfont (which would also require loading a lot more fonts). +% +\def\resetmathfonts{% + \textfont0=\tenrm \textfont1=\teni \textfont2=\tensy + \textfont\itfam=\tenit \textfont\slfam=\tensl \textfont\bffam=\tenbf + \textfont\ttfam=\tentt \textfont\sffam=\tensf +} + +% The font-changing commands redefine the meanings of \tenSTYLE, instead +% of just \STYLE. We do this because \STYLE needs to also set the +% current \fam for math mode. Our \STYLE (e.g., \rm) commands hardwire +% \tenSTYLE to set the current font. +% +% Each font-changing command also sets the names \lsize (one size lower) +% and \lllsize (three sizes lower). These relative commands are used in +% the LaTeX logo and acronyms. +% +% This all needs generalizing, badly. +% +\def\textfonts{% + \let\tenrm=\textrm \let\tenit=\textit \let\tensl=\textsl + \let\tenbf=\textbf \let\tentt=\texttt \let\smallcaps=\textsc + \let\tensf=\textsf \let\teni=\texti \let\tensy=\textsy + \let\tenttsl=\textttsl + \def\curfontsize{text}% + \def\lsize{reduced}\def\lllsize{smaller}% + \resetmathfonts \setleading{\textleading}} +\def\titlefonts{% + \let\tenrm=\titlerm \let\tenit=\titleit \let\tensl=\titlesl + \let\tenbf=\titlebf \let\tentt=\titlett \let\smallcaps=\titlesc + \let\tensf=\titlesf \let\teni=\titlei \let\tensy=\titlesy + \let\tenttsl=\titlettsl + \def\curfontsize{title}% + \def\lsize{chap}\def\lllsize{subsec}% + \resetmathfonts \setleading{27pt}} +\def\titlefont#1{{\titlefonts\rmisbold #1}} +\def\chapfonts{% + \let\tenrm=\chaprm \let\tenit=\chapit \let\tensl=\chapsl + \let\tenbf=\chapbf \let\tentt=\chaptt \let\smallcaps=\chapsc + \let\tensf=\chapsf \let\teni=\chapi \let\tensy=\chapsy + \let\tenttsl=\chapttsl + \def\curfontsize{chap}% + \def\lsize{sec}\def\lllsize{text}% + \resetmathfonts \setleading{19pt}} +\def\secfonts{% + \let\tenrm=\secrm \let\tenit=\secit \let\tensl=\secsl + \let\tenbf=\secbf \let\tentt=\sectt \let\smallcaps=\secsc + \let\tensf=\secsf \let\teni=\seci \let\tensy=\secsy + \let\tenttsl=\secttsl + \def\curfontsize{sec}% + \def\lsize{subsec}\def\lllsize{reduced}% + \resetmathfonts \setleading{16pt}} +\def\subsecfonts{% + \let\tenrm=\ssecrm \let\tenit=\ssecit \let\tensl=\ssecsl + \let\tenbf=\ssecbf \let\tentt=\ssectt \let\smallcaps=\ssecsc + \let\tensf=\ssecsf \let\teni=\sseci \let\tensy=\ssecsy + \let\tenttsl=\ssecttsl + \def\curfontsize{ssec}% + \def\lsize{text}\def\lllsize{small}% + \resetmathfonts \setleading{15pt}} +\let\subsubsecfonts = \subsecfonts +\def\reducedfonts{% + \let\tenrm=\reducedrm \let\tenit=\reducedit \let\tensl=\reducedsl + \let\tenbf=\reducedbf \let\tentt=\reducedtt \let\reducedcaps=\reducedsc + \let\tensf=\reducedsf \let\teni=\reducedi \let\tensy=\reducedsy + \let\tenttsl=\reducedttsl + \def\curfontsize{reduced}% + \def\lsize{small}\def\lllsize{smaller}% + \resetmathfonts \setleading{10.5pt}} +\def\smallfonts{% + \let\tenrm=\smallrm \let\tenit=\smallit \let\tensl=\smallsl + \let\tenbf=\smallbf \let\tentt=\smalltt \let\smallcaps=\smallsc + \let\tensf=\smallsf \let\teni=\smalli \let\tensy=\smallsy + \let\tenttsl=\smallttsl + \def\curfontsize{small}% + \def\lsize{smaller}\def\lllsize{smaller}% + \resetmathfonts \setleading{10.5pt}} +\def\smallerfonts{% + \let\tenrm=\smallerrm \let\tenit=\smallerit \let\tensl=\smallersl + \let\tenbf=\smallerbf \let\tentt=\smallertt \let\smallcaps=\smallersc + \let\tensf=\smallersf \let\teni=\smalleri \let\tensy=\smallersy + \let\tenttsl=\smallerttsl + \def\curfontsize{smaller}% + \def\lsize{smaller}\def\lllsize{smaller}% + \resetmathfonts \setleading{9.5pt}} + +% Fonts for short table of contents. +\setfont\shortcontrm\rmshape{12}{1000}{OT1} +\setfont\shortcontbf\bfshape{10}{\magstep1}{OT1} % no cmb12 +\setfont\shortcontsl\slshape{12}{1000}{OT1} +\setfont\shortconttt\ttshape{12}{1000}{OT1TT} + +% Define these just so they can be easily changed for other fonts. +\def\angleleft{$\langle$} +\def\angleright{$\rangle$} + +% Set the fonts to use with the @small... environments. +\let\smallexamplefonts = \smallfonts + +% About \smallexamplefonts. If we use \smallfonts (9pt), @smallexample +% can fit this many characters: +% 8.5x11=86 smallbook=72 a4=90 a5=69 +% If we use \scriptfonts (8pt), then we can fit this many characters: +% 8.5x11=90+ smallbook=80 a4=90+ a5=77 +% For me, subjectively, the few extra characters that fit aren't worth +% the additional smallness of 8pt. So I'm making the default 9pt. +% +% By the way, for comparison, here's what fits with @example (10pt): +% 8.5x11=71 smallbook=60 a4=75 a5=58 +% --karl, 24jan03. + +% Set up the default fonts, so we can use them for creating boxes. +% +\definetextfontsizexi + + +\message{markup,} + +% Check if we are currently using a typewriter font. Since all the +% Computer Modern typewriter fonts have zero interword stretch (and +% shrink), and it is reasonable to expect all typewriter fonts to have +% this property, we can check that font parameter. +% +\def\ifmonospace{\ifdim\fontdimen3\font=0pt } + +% Markup style infrastructure. \defmarkupstylesetup\INITMACRO will +% define and register \INITMACRO to be called on markup style changes. +% \INITMACRO can check \currentmarkupstyle for the innermost +% style and the set of \ifmarkupSTYLE switches for all styles +% currently in effect. +\newif\ifmarkupvar +\newif\ifmarkupsamp +\newif\ifmarkupkey +%\newif\ifmarkupfile % @file == @samp. +%\newif\ifmarkupoption % @option == @samp. +\newif\ifmarkupcode +\newif\ifmarkupkbd +%\newif\ifmarkupenv % @env == @code. +%\newif\ifmarkupcommand % @command == @code. +\newif\ifmarkuptex % @tex (and part of @math, for now). +\newif\ifmarkupexample +\newif\ifmarkupverb +\newif\ifmarkupverbatim + +\let\currentmarkupstyle\empty + +\def\setupmarkupstyle#1{% + \csname markup#1true\endcsname + \def\currentmarkupstyle{#1}% + \markupstylesetup +} + +\let\markupstylesetup\empty + +\def\defmarkupstylesetup#1{% + \expandafter\def\expandafter\markupstylesetup + \expandafter{\markupstylesetup #1}% + \def#1% +} + +% Markup style setup for left and right quotes. +\defmarkupstylesetup\markupsetuplq{% + \expandafter\let\expandafter \temp + \csname markupsetuplq\currentmarkupstyle\endcsname + \ifx\temp\relax \markupsetuplqdefault \else \temp \fi +} + +\defmarkupstylesetup\markupsetuprq{% + \expandafter\let\expandafter \temp + \csname markupsetuprq\currentmarkupstyle\endcsname + \ifx\temp\relax \markupsetuprqdefault \else \temp \fi +} + +{ +\catcode`\'=\active +\catcode`\`=\active + +\gdef\markupsetuplqdefault{\let`\lq} +\gdef\markupsetuprqdefault{\let'\rq} + +\gdef\markupsetcodequoteleft{\let`\codequoteleft} +\gdef\markupsetcodequoteright{\let'\codequoteright} + +\gdef\markupsetnoligaturesquoteleft{\let`\noligaturesquoteleft} +} + +\let\markupsetuplqcode \markupsetcodequoteleft +\let\markupsetuprqcode \markupsetcodequoteright +% +\let\markupsetuplqexample \markupsetcodequoteleft +\let\markupsetuprqexample \markupsetcodequoteright +% +\let\markupsetuplqsamp \markupsetcodequoteleft +\let\markupsetuprqsamp \markupsetcodequoteright +% +\let\markupsetuplqverb \markupsetcodequoteleft +\let\markupsetuprqverb \markupsetcodequoteright +% +\let\markupsetuplqverbatim \markupsetcodequoteleft +\let\markupsetuprqverbatim \markupsetcodequoteright + +\let\markupsetuplqkbd \markupsetnoligaturesquoteleft + +% Allow an option to not use regular directed right quote/apostrophe +% (char 0x27), but instead the undirected quote from cmtt (char 0x0d). +% The undirected quote is ugly, so don't make it the default, but it +% works for pasting with more pdf viewers (at least evince), the +% lilypond developers report. xpdf does work with the regular 0x27. +% +\def\codequoteright{% + \expandafter\ifx\csname SETtxicodequoteundirected\endcsname\relax + \expandafter\ifx\csname SETcodequoteundirected\endcsname\relax + '% + \else \char'15 \fi + \else \char'15 \fi +} +% +% and a similar option for the left quote char vs. a grave accent. +% Modern fonts display ASCII 0x60 as a grave accent, so some people like +% the code environments to do likewise. +% +\def\codequoteleft{% + \expandafter\ifx\csname SETtxicodequotebacktick\endcsname\relax + \expandafter\ifx\csname SETcodequotebacktick\endcsname\relax + % [Knuth] pp. 380,381,391 + % \relax disables Spanish ligatures ?` and !` of \tt font. + \relax`% + \else \char'22 \fi + \else \char'22 \fi +} + +% Commands to set the quote options. +% +\parseargdef\codequoteundirected{% + \def\temp{#1}% + \ifx\temp\onword + \expandafter\let\csname SETtxicodequoteundirected\endcsname + = t% + \else\ifx\temp\offword + \expandafter\let\csname SETtxicodequoteundirected\endcsname + = \relax + \else + \errhelp = \EMsimple + \errmessage{Unknown @codequoteundirected value `\temp', must be on|off}% + \fi\fi +} +% +\parseargdef\codequotebacktick{% + \def\temp{#1}% + \ifx\temp\onword + \expandafter\let\csname SETtxicodequotebacktick\endcsname + = t% + \else\ifx\temp\offword + \expandafter\let\csname SETtxicodequotebacktick\endcsname + = \relax + \else + \errhelp = \EMsimple + \errmessage{Unknown @codequotebacktick value `\temp', must be on|off}% + \fi\fi +} + +% [Knuth] pp. 380,381,391, disable Spanish ligatures ?` and !` of \tt font. +\def\noligaturesquoteleft{\relax\lq} + +% Count depth in font-changes, for error checks +\newcount\fontdepth \fontdepth=0 + +% Font commands. + +% #1 is the font command (\sl or \it), #2 is the text to slant. +% If we are in a monospaced environment, however, 1) always use \ttsl, +% and 2) do not add an italic correction. +\def\dosmartslant#1#2{% + \ifusingtt + {{\ttsl #2}\let\next=\relax}% + {\def\next{{#1#2}\futurelet\next\smartitaliccorrection}}% + \next +} +\def\smartslanted{\dosmartslant\sl} +\def\smartitalic{\dosmartslant\it} + +% Output an italic correction unless \next (presumed to be the following +% character) is such as not to need one. +\def\smartitaliccorrection{% + \ifx\next,% + \else\ifx\next-% + \else\ifx\next.% + \else\ptexslash + \fi\fi\fi + \aftersmartic +} + +% like \smartslanted except unconditionally uses \ttsl, and no ic. +% @var is set to this for defun arguments. +\def\ttslanted#1{{\ttsl #1}} + +% @cite is like \smartslanted except unconditionally use \sl. We never want +% ttsl for book titles, do we? +\def\cite#1{{\sl #1}\futurelet\next\smartitaliccorrection} + +\def\aftersmartic{} +\def\var#1{% + \let\saveaftersmartic = \aftersmartic + \def\aftersmartic{\null\let\aftersmartic=\saveaftersmartic}% + \smartslanted{#1}% +} + +\let\i=\smartitalic +\let\slanted=\smartslanted +\let\dfn=\smartslanted +\let\emph=\smartitalic + +% Explicit font changes: @r, @sc, undocumented @ii. +\def\r#1{{\rm #1}} % roman font +\def\sc#1{{\smallcaps#1}} % smallcaps font +\def\ii#1{{\it #1}} % italic font + +% @b, explicit bold. Also @strong. +\def\b#1{{\bf #1}} +\let\strong=\b + +% @sansserif, explicit sans. +\def\sansserif#1{{\sf #1}} + +% We can't just use \exhyphenpenalty, because that only has effect at +% the end of a paragraph. Restore normal hyphenation at the end of the +% group within which \nohyphenation is presumably called. +% +\def\nohyphenation{\hyphenchar\font = -1 \aftergroup\restorehyphenation} +\def\restorehyphenation{\hyphenchar\font = `- } + +% Set sfcode to normal for the chars that usually have another value. +% Can't use plain's \frenchspacing because it uses the `\x notation, and +% sometimes \x has an active definition that messes things up. +% +\catcode`@=11 + \def\plainfrenchspacing{% + \sfcode\dotChar =\@m \sfcode\questChar=\@m \sfcode\exclamChar=\@m + \sfcode\colonChar=\@m \sfcode\semiChar =\@m \sfcode\commaChar =\@m + \def\endofsentencespacefactor{1000}% for @. and friends + } + \def\plainnonfrenchspacing{% + \sfcode`\.3000\sfcode`\?3000\sfcode`\!3000 + \sfcode`\:2000\sfcode`\;1500\sfcode`\,1250 + \def\endofsentencespacefactor{3000}% for @. and friends + } +\catcode`@=\other +\def\endofsentencespacefactor{3000}% default + +% @t, explicit typewriter. +\def\t#1{% + {\tt \rawbackslash \plainfrenchspacing #1}% + \null +} + +% @samp. +\def\samp#1{{\setupmarkupstyle{samp}\lq\tclose{#1}\rq\null}} + +% definition of @key that produces a lozenge. Doesn't adjust to text size. +%\setfont\keyrm\rmshape{8}{1000}{OT1} +%\font\keysy=cmsy9 +%\def\key#1{{\keyrm\textfont2=\keysy \leavevmode\hbox{% +% \raise0.4pt\hbox{\angleleft}\kern-.08em\vtop{% +% \vbox{\hrule\kern-0.4pt +% \hbox{\raise0.4pt\hbox{\vphantom{\angleleft}}#1}}% +% \kern-0.4pt\hrule}% +% \kern-.06em\raise0.4pt\hbox{\angleright}}}} + +% definition of @key with no lozenge. If the current font is already +% monospace, don't change it; that way, we respect @kbdinputstyle. But +% if it isn't monospace, then use \tt. +% +\def\key#1{{\setupmarkupstyle{key}% + \nohyphenation + \ifmonospace\else\tt\fi + #1}\null} + +% ctrl is no longer a Texinfo command. +\def\ctrl #1{{\tt \rawbackslash \hat}#1} + +% @file, @option are the same as @samp. +\let\file=\samp +\let\option=\samp + +% @code is a modification of @t, +% which makes spaces the same size as normal in the surrounding text. +\def\tclose#1{% + {% + % Change normal interword space to be same as for the current font. + \spaceskip = \fontdimen2\font + % + % Switch to typewriter. + \tt + % + % But `\ ' produces the large typewriter interword space. + \def\ {{\spaceskip = 0pt{} }}% + % + % Turn off hyphenation. + \nohyphenation + % + \rawbackslash + \plainfrenchspacing + #1% + }% + \null % reset spacefactor to 1000 +} + +% We *must* turn on hyphenation at `-' and `_' in @code. +% Otherwise, it is too hard to avoid overfull hboxes +% in the Emacs manual, the Library manual, etc. + +% Unfortunately, TeX uses one parameter (\hyphenchar) to control +% both hyphenation at - and hyphenation within words. +% We must therefore turn them both off (\tclose does that) +% and arrange explicitly to hyphenate at a dash. +% -- rms. +{ + \catcode`\-=\active \catcode`\_=\active + \catcode`\'=\active \catcode`\`=\active + \global\let'=\rq \global\let`=\lq % default definitions + % + \global\def\code{\begingroup + \setupmarkupstyle{code}% + % The following should really be moved into \setupmarkupstyle handlers. + \catcode\dashChar=\active \catcode\underChar=\active + \ifallowcodebreaks + \let-\codedash + \let_\codeunder + \else + \let-\realdash + \let_\realunder + \fi + \codex + } +} + +\def\codex #1{\tclose{#1}\endgroup} + +\def\realdash{-} +\def\codedash{-\discretionary{}{}{}} +\def\codeunder{% + % this is all so @math{@code{var_name}+1} can work. In math mode, _ + % is "active" (mathcode"8000) and \normalunderscore (or \char95, etc.) + % will therefore expand the active definition of _, which is us + % (inside @code that is), therefore an endless loop. + \ifusingtt{\ifmmode + \mathchar"075F % class 0=ordinary, family 7=ttfam, pos 0x5F=_. + \else\normalunderscore \fi + \discretionary{}{}{}}% + {\_}% +} + +% An additional complication: the above will allow breaks after, e.g., +% each of the four underscores in __typeof__. This is undesirable in +% some manuals, especially if they don't have long identifiers in +% general. @allowcodebreaks provides a way to control this. +% +\newif\ifallowcodebreaks \allowcodebreakstrue + +\def\keywordtrue{true} +\def\keywordfalse{false} + +\parseargdef\allowcodebreaks{% + \def\txiarg{#1}% + \ifx\txiarg\keywordtrue + \allowcodebreakstrue + \else\ifx\txiarg\keywordfalse + \allowcodebreaksfalse + \else + \errhelp = \EMsimple + \errmessage{Unknown @allowcodebreaks option `\txiarg', must be true|false}% + \fi\fi +} + +% @uref (abbreviation for `urlref') takes an optional (comma-separated) +% second argument specifying the text to display and an optional third +% arg as text to display instead of (rather than in addition to) the url +% itself. First (mandatory) arg is the url. +% (This \urefnobreak definition isn't used now, leaving it for a while +% for comparison.) +\def\urefnobreak#1{\dourefnobreak #1,,,\finish} +\def\dourefnobreak#1,#2,#3,#4\finish{\begingroup + \unsepspaces + \pdfurl{#1}% + \setbox0 = \hbox{\ignorespaces #3}% + \ifdim\wd0 > 0pt + \unhbox0 % third arg given, show only that + \else + \setbox0 = \hbox{\ignorespaces #2}% + \ifdim\wd0 > 0pt + \ifpdf + \unhbox0 % PDF: 2nd arg given, show only it + \else + \unhbox0\ (\code{#1})% DVI: 2nd arg given, show both it and url + \fi + \else + \code{#1}% only url given, so show it + \fi + \fi + \endlink +\endgroup} + +% This \urefbreak definition is the active one. +\def\urefbreak{\begingroup \urefcatcodes \dourefbreak} +\let\uref=\urefbreak +\def\dourefbreak#1{\urefbreakfinish #1,,,\finish} +\def\urefbreakfinish#1,#2,#3,#4\finish{% doesn't work in @example + \unsepspaces + \pdfurl{#1}% + \setbox0 = \hbox{\ignorespaces #3}% + \ifdim\wd0 > 0pt + \unhbox0 % third arg given, show only that + \else + \setbox0 = \hbox{\ignorespaces #2}% + \ifdim\wd0 > 0pt + \ifpdf + \unhbox0 % PDF: 2nd arg given, show only it + \else + \unhbox0\ (\urefcode{#1})% DVI: 2nd arg given, show both it and url + \fi + \else + \urefcode{#1}% only url given, so show it + \fi + \fi + \endlink +\endgroup} + +% Allow line breaks around only a few characters (only). +\def\urefcatcodes{% + \catcode\ampChar=\active \catcode\dotChar=\active + \catcode\hashChar=\active \catcode\questChar=\active + \catcode\slashChar=\active +} +{ + \urefcatcodes + % + \global\def\urefcode{\begingroup + \setupmarkupstyle{code}% + \urefcatcodes + \let&\urefcodeamp + \let.\urefcodedot + \let#\urefcodehash + \let?\urefcodequest + \let/\urefcodeslash + \codex + } + % + % By default, they are just regular characters. + \global\def&{\normalamp} + \global\def.{\normaldot} + \global\def#{\normalhash} + \global\def?{\normalquest} + \global\def/{\normalslash} +} + +% we put a little stretch before and after the breakable chars, to help +% line breaking of long url's. The unequal skips make look better in +% cmtt at least, especially for dots. +\def\urefprestretch{\urefprebreak \hskip0pt plus.13em } +\def\urefpoststretch{\urefpostbreak \hskip0pt plus.1em } +% +\def\urefcodeamp{\urefprestretch \&\urefpoststretch} +\def\urefcodedot{\urefprestretch .\urefpoststretch} +\def\urefcodehash{\urefprestretch \#\urefpoststretch} +\def\urefcodequest{\urefprestretch ?\urefpoststretch} +\def\urefcodeslash{\futurelet\next\urefcodeslashfinish} +{ + \catcode`\/=\active + \global\def\urefcodeslashfinish{% + \urefprestretch \slashChar + % Allow line break only after the final / in a sequence of + % slashes, to avoid line break between the slashes in http://. + \ifx\next/\else \urefpoststretch \fi + } +} + +% One more complication: by default we'll break after the special +% characters, but some people like to break before the special chars, so +% allow that. Also allow no breaking at all, for manual control. +% +\parseargdef\urefbreakstyle{% + \def\txiarg{#1}% + \ifx\txiarg\wordnone + \def\urefprebreak{\nobreak}\def\urefpostbreak{\nobreak} + \else\ifx\txiarg\wordbefore + \def\urefprebreak{\allowbreak}\def\urefpostbreak{\nobreak} + \else\ifx\txiarg\wordafter + \def\urefprebreak{\nobreak}\def\urefpostbreak{\allowbreak} + \else + \errhelp = \EMsimple + \errmessage{Unknown @urefbreakstyle setting `\txiarg'}% + \fi\fi\fi +} +\def\wordafter{after} +\def\wordbefore{before} +\def\wordnone{none} + +\urefbreakstyle after + +% @url synonym for @uref, since that's how everyone uses it. +% +\let\url=\uref + +% rms does not like angle brackets --karl, 17may97. +% So now @email is just like @uref, unless we are pdf. +% +%\def\email#1{\angleleft{\tt #1}\angleright} +\ifpdf + \def\email#1{\doemail#1,,\finish} + \def\doemail#1,#2,#3\finish{\begingroup + \unsepspaces + \pdfurl{mailto:#1}% + \setbox0 = \hbox{\ignorespaces #2}% + \ifdim\wd0>0pt\unhbox0\else\code{#1}\fi + \endlink + \endgroup} +\else + \let\email=\uref +\fi + +% @kbd is like @code, except that if the argument is just one @key command, +% then @kbd has no effect. +\def\kbd#1{{\setupmarkupstyle{kbd}\def\look{#1}\expandafter\kbdfoo\look??\par}} + +% @kbdinputstyle -- arg is `distinct' (@kbd uses slanted tty font always), +% `example' (@kbd uses ttsl only inside of @example and friends), +% or `code' (@kbd uses normal tty font always). +\parseargdef\kbdinputstyle{% + \def\txiarg{#1}% + \ifx\txiarg\worddistinct + \gdef\kbdexamplefont{\ttsl}\gdef\kbdfont{\ttsl}% + \else\ifx\txiarg\wordexample + \gdef\kbdexamplefont{\ttsl}\gdef\kbdfont{\tt}% + \else\ifx\txiarg\wordcode + \gdef\kbdexamplefont{\tt}\gdef\kbdfont{\tt}% + \else + \errhelp = \EMsimple + \errmessage{Unknown @kbdinputstyle setting `\txiarg'}% + \fi\fi\fi +} +\def\worddistinct{distinct} +\def\wordexample{example} +\def\wordcode{code} + +% Default is `distinct'. +\kbdinputstyle distinct + +\def\xkey{\key} +\def\kbdfoo#1#2#3\par{\def\one{#1}\def\three{#3}\def\threex{??}% +\ifx\one\xkey\ifx\threex\three \key{#2}% +\else{\tclose{\kbdfont\setupmarkupstyle{kbd}\look}}\fi +\else{\tclose{\kbdfont\setupmarkupstyle{kbd}\look}}\fi} + +% For @indicateurl, @env, @command quotes seem unnecessary, so use \code. +\let\indicateurl=\code +\let\env=\code +\let\command=\code + +% @clicksequence{File @click{} Open ...} +\def\clicksequence#1{\begingroup #1\endgroup} + +% @clickstyle @arrow (by default) +\parseargdef\clickstyle{\def\click{#1}} +\def\click{\arrow} + +% Typeset a dimension, e.g., `in' or `pt'. The only reason for the +% argument is to make the input look right: @dmn{pt} instead of @dmn{}pt. +% +\def\dmn#1{\thinspace #1} + +% @l was never documented to mean ``switch to the Lisp font'', +% and it is not used as such in any manual I can find. We need it for +% Polish suppressed-l. --karl, 22sep96. +%\def\l#1{{\li #1}\null} + +% @acronym for "FBI", "NATO", and the like. +% We print this one point size smaller, since it's intended for +% all-uppercase. +% +\def\acronym#1{\doacronym #1,,\finish} +\def\doacronym#1,#2,#3\finish{% + {\selectfonts\lsize #1}% + \def\temp{#2}% + \ifx\temp\empty \else + \space ({\unsepspaces \ignorespaces \temp \unskip})% + \fi + \null % reset \spacefactor=1000 +} + +% @abbr for "Comput. J." and the like. +% No font change, but don't do end-of-sentence spacing. +% +\def\abbr#1{\doabbr #1,,\finish} +\def\doabbr#1,#2,#3\finish{% + {\plainfrenchspacing #1}% + \def\temp{#2}% + \ifx\temp\empty \else + \space ({\unsepspaces \ignorespaces \temp \unskip})% + \fi + \null % reset \spacefactor=1000 +} + +% @asis just yields its argument. Used with @table, for example. +% +\def\asis#1{#1} + +% @math outputs its argument in math mode. +% +% One complication: _ usually means subscripts, but it could also mean +% an actual _ character, as in @math{@var{some_variable} + 1}. So make +% _ active, and distinguish by seeing if the current family is \slfam, +% which is what @var uses. +{ + \catcode`\_ = \active + \gdef\mathunderscore{% + \catcode`\_=\active + \def_{\ifnum\fam=\slfam \_\else\sb\fi}% + } +} +% Another complication: we want \\ (and @\) to output a math (or tt) \. +% FYI, plain.tex uses \\ as a temporary control sequence (for no +% particular reason), but this is not advertised and we don't care. +% +% The \mathchar is class=0=ordinary, family=7=ttfam, position=5C=\. +\def\mathbackslash{\ifnum\fam=\ttfam \mathchar"075C \else\backslash \fi} +% +\def\math{% + \tex + \mathunderscore + \let\\ = \mathbackslash + \mathactive + % make the texinfo accent commands work in math mode + \let\"=\ddot + \let\'=\acute + \let\==\bar + \let\^=\hat + \let\`=\grave + \let\u=\breve + \let\v=\check + \let\~=\tilde + \let\dotaccent=\dot + $\finishmath +} +\def\finishmath#1{#1$\endgroup} % Close the group opened by \tex. + +% Some active characters (such as <) are spaced differently in math. +% We have to reset their definitions in case the @math was an argument +% to a command which sets the catcodes (such as @item or @section). +% +{ + \catcode`^ = \active + \catcode`< = \active + \catcode`> = \active + \catcode`+ = \active + \catcode`' = \active + \gdef\mathactive{% + \let^ = \ptexhat + \let< = \ptexless + \let> = \ptexgtr + \let+ = \ptexplus + \let' = \ptexquoteright + } +} + +% @inlinefmt{FMTNAME,PROCESSED-TEXT} and @inlineraw{FMTNAME,RAW-TEXT}. +% Ignore unless FMTNAME == tex; then it is like @iftex and @tex, +% except specified as a normal braced arg, so no newlines to worry about. +% +\def\outfmtnametex{tex} +% +\long\def\inlinefmt#1{\doinlinefmt #1,\finish} +\long\def\doinlinefmt#1,#2,\finish{% + \def\inlinefmtname{#1}% + \ifx\inlinefmtname\outfmtnametex \ignorespaces #2\fi +} +% For raw, must switch into @tex before parsing the argument, to avoid +% setting catcodes prematurely. Doing it this way means that, for +% example, @inlineraw{html, foo{bar} gets a parse error instead of being +% ignored. But this isn't important because if people want a literal +% *right* brace they would have to use a command anyway, so they may as +% well use a command to get a left brace too. We could re-use the +% delimiter character idea from \verb, but it seems like overkill. +% +\long\def\inlineraw{\tex \doinlineraw} +\long\def\doinlineraw#1{\doinlinerawtwo #1,\finish} +\def\doinlinerawtwo#1,#2,\finish{% + \def\inlinerawname{#1}% + \ifx\inlinerawname\outfmtnametex \ignorespaces #2\fi + \endgroup % close group opened by \tex. +} + + +\message{glyphs,} +% and logos. + +% @@ prints an @, as does @atchar{}. +\def\@{\char64 } +\let\atchar=\@ + +% @{ @} @lbracechar{} @rbracechar{} all generate brace characters. +% Unless we're in typewriter, use \ecfont because the CM text fonts do +% not have braces, and we don't want to switch into math. +\def\mylbrace{{\ifmonospace\else\ecfont\fi \char123}} +\def\myrbrace{{\ifmonospace\else\ecfont\fi \char125}} +\let\{=\mylbrace \let\lbracechar=\{ +\let\}=\myrbrace \let\rbracechar=\} +\begingroup + % Definitions to produce \{ and \} commands for indices, + % and @{ and @} for the aux/toc files. + \catcode`\{ = \other \catcode`\} = \other + \catcode`\[ = 1 \catcode`\] = 2 + \catcode`\! = 0 \catcode`\\ = \other + !gdef!lbracecmd[\{]% + !gdef!rbracecmd[\}]% + !gdef!lbraceatcmd[@{]% + !gdef!rbraceatcmd[@}]% +!endgroup + +% @comma{} to avoid , parsing problems. +\let\comma = , + +% Accents: @, @dotaccent @ringaccent @ubaraccent @udotaccent +% Others are defined by plain TeX: @` @' @" @^ @~ @= @u @v @H. +\let\, = \ptexc +\let\dotaccent = \ptexdot +\def\ringaccent#1{{\accent23 #1}} +\let\tieaccent = \ptext +\let\ubaraccent = \ptexb +\let\udotaccent = \d + +% Other special characters: @questiondown @exclamdown @ordf @ordm +% Plain TeX defines: @AA @AE @O @OE @L (plus lowercase versions) @ss. +\def\questiondown{?`} +\def\exclamdown{!`} +\def\ordf{\leavevmode\raise1ex\hbox{\selectfonts\lllsize \underbar{a}}} +\def\ordm{\leavevmode\raise1ex\hbox{\selectfonts\lllsize \underbar{o}}} + +% Dotless i and dotless j, used for accents. +\def\imacro{i} +\def\jmacro{j} +\def\dotless#1{% + \def\temp{#1}% + \ifx\temp\imacro \ifmmode\imath \else\ptexi \fi + \else\ifx\temp\jmacro \ifmmode\jmath \else\j \fi + \else \errmessage{@dotless can be used only with i or j}% + \fi\fi +} + +% The \TeX{} logo, as in plain, but resetting the spacing so that a +% period following counts as ending a sentence. (Idea found in latex.) +% +\edef\TeX{\TeX \spacefactor=1000 } + +% @LaTeX{} logo. Not quite the same results as the definition in +% latex.ltx, since we use a different font for the raised A; it's most +% convenient for us to use an explicitly smaller font, rather than using +% the \scriptstyle font (since we don't reset \scriptstyle and +% \scriptscriptstyle). +% +\def\LaTeX{% + L\kern-.36em + {\setbox0=\hbox{T}% + \vbox to \ht0{\hbox{% + \ifx\textnominalsize\xwordpt + % for 10pt running text, \lllsize (8pt) is too small for the A in LaTeX. + % Revert to plain's \scriptsize, which is 7pt. + \count255=\the\fam $\fam\count255 \scriptstyle A$% + \else + % For 11pt, we can use our lllsize. + \selectfonts\lllsize A% + \fi + }% + \vss + }}% + \kern-.15em + \TeX +} + +% Some math mode symbols. +\def\bullet{$\ptexbullet$} +\def\geq{\ifmmode \ge\else $\ge$\fi} +\def\leq{\ifmmode \le\else $\le$\fi} +\def\minus{\ifmmode -\else $-$\fi} + +% @dots{} outputs an ellipsis using the current font. +% We do .5em per period so that it has the same spacing in the cm +% typewriter fonts as three actual period characters; on the other hand, +% in other typewriter fonts three periods are wider than 1.5em. So do +% whichever is larger. +% +\def\dots{% + \leavevmode + \setbox0=\hbox{...}% get width of three periods + \ifdim\wd0 > 1.5em + \dimen0 = \wd0 + \else + \dimen0 = 1.5em + \fi + \hbox to \dimen0{% + \hskip 0pt plus.25fil + .\hskip 0pt plus1fil + .\hskip 0pt plus1fil + .\hskip 0pt plus.5fil + }% +} + +% @enddots{} is an end-of-sentence ellipsis. +% +\def\enddots{% + \dots + \spacefactor=\endofsentencespacefactor +} + +% @point{}, @result{}, @expansion{}, @print{}, @equiv{}. +% +% Since these characters are used in examples, they should be an even number of +% \tt widths. Each \tt character is 1en, so two makes it 1em. +% +\def\point{$\star$} +\def\arrow{\leavevmode\raise.05ex\hbox to 1em{\hfil$\rightarrow$\hfil}} +\def\result{\leavevmode\raise.05ex\hbox to 1em{\hfil$\Rightarrow$\hfil}} +\def\expansion{\leavevmode\hbox to 1em{\hfil$\mapsto$\hfil}} +\def\print{\leavevmode\lower.1ex\hbox to 1em{\hfil$\dashv$\hfil}} +\def\equiv{\leavevmode\hbox to 1em{\hfil$\ptexequiv$\hfil}} + +% The @error{} command. +% Adapted from the TeXbook's \boxit. +% +\newbox\errorbox +% +{\tentt \global\dimen0 = 3em}% Width of the box. +\dimen2 = .55pt % Thickness of rules +% The text. (`r' is open on the right, `e' somewhat less so on the left.) +\setbox0 = \hbox{\kern-.75pt \reducedsf \putworderror\kern-1.5pt} +% +\setbox\errorbox=\hbox to \dimen0{\hfil + \hsize = \dimen0 \advance\hsize by -5.8pt % Space to left+right. + \advance\hsize by -2\dimen2 % Rules. + \vbox{% + \hrule height\dimen2 + \hbox{\vrule width\dimen2 \kern3pt % Space to left of text. + \vtop{\kern2.4pt \box0 \kern2.4pt}% Space above/below. + \kern3pt\vrule width\dimen2}% Space to right. + \hrule height\dimen2} + \hfil} +% +\def\error{\leavevmode\lower.7ex\copy\errorbox} + +% @pounds{} is a sterling sign, which Knuth put in the CM italic font. +% +\def\pounds{{\it\$}} + +% @euro{} comes from a separate font, depending on the current style. +% We use the free feym* fonts from the eurosym package by Henrik +% Theiling, which support regular, slanted, bold and bold slanted (and +% "outlined" (blackboard board, sort of) versions, which we don't need). +% It is available from http://www.ctan.org/tex-archive/fonts/eurosym. +% +% Although only regular is the truly official Euro symbol, we ignore +% that. The Euro is designed to be slightly taller than the regular +% font height. +% +% feymr - regular +% feymo - slanted +% feybr - bold +% feybo - bold slanted +% +% There is no good (free) typewriter version, to my knowledge. +% A feymr10 euro is ~7.3pt wide, while a normal cmtt10 char is ~5.25pt wide. +% Hmm. +% +% Also doesn't work in math. Do we need to do math with euro symbols? +% Hope not. +% +% +\def\euro{{\eurofont e}} +\def\eurofont{% + % We set the font at each command, rather than predefining it in + % \textfonts and the other font-switching commands, so that + % installations which never need the symbol don't have to have the + % font installed. + % + % There is only one designed size (nominal 10pt), so we always scale + % that to the current nominal size. + % + % By the way, simply using "at 1em" works for cmr10 and the like, but + % does not work for cmbx10 and other extended/shrunken fonts. + % + \def\eurosize{\csname\curfontsize nominalsize\endcsname}% + % + \ifx\curfontstyle\bfstylename + % bold: + \font\thiseurofont = \ifusingit{feybo10}{feybr10} at \eurosize + \else + % regular: + \font\thiseurofont = \ifusingit{feymo10}{feymr10} at \eurosize + \fi + \thiseurofont +} + +% Glyphs from the EC fonts. We don't use \let for the aliases, because +% sometimes we redefine the original macro, and the alias should reflect +% the redefinition. +% +% Use LaTeX names for the Icelandic letters. +\def\DH{{\ecfont \char"D0}} % Eth +\def\dh{{\ecfont \char"F0}} % eth +\def\TH{{\ecfont \char"DE}} % Thorn +\def\th{{\ecfont \char"FE}} % thorn +% +\def\guillemetleft{{\ecfont \char"13}} +\def\guillemotleft{\guillemetleft} +\def\guillemetright{{\ecfont \char"14}} +\def\guillemotright{\guillemetright} +\def\guilsinglleft{{\ecfont \char"0E}} +\def\guilsinglright{{\ecfont \char"0F}} +\def\quotedblbase{{\ecfont \char"12}} +\def\quotesinglbase{{\ecfont \char"0D}} +% +% This positioning is not perfect (see the ogonek LaTeX package), but +% we have the precomposed glyphs for the most common cases. We put the +% tests to use those glyphs in the single \ogonek macro so we have fewer +% dummy definitions to worry about for index entries, etc. +% +% ogonek is also used with other letters in Lithuanian (IOU), but using +% the precomposed glyphs for those is not so easy since they aren't in +% the same EC font. +\def\ogonek#1{{% + \def\temp{#1}% + \ifx\temp\macrocharA\Aogonek + \else\ifx\temp\macrochara\aogonek + \else\ifx\temp\macrocharE\Eogonek + \else\ifx\temp\macrochare\eogonek + \else + \ecfont \setbox0=\hbox{#1}% + \ifdim\ht0=1ex\accent"0C #1% + \else\ooalign{\unhbox0\crcr\hidewidth\char"0C \hidewidth}% + \fi + \fi\fi\fi\fi + }% +} +\def\Aogonek{{\ecfont \char"81}}\def\macrocharA{A} +\def\aogonek{{\ecfont \char"A1}}\def\macrochara{a} +\def\Eogonek{{\ecfont \char"86}}\def\macrocharE{E} +\def\eogonek{{\ecfont \char"A6}}\def\macrochare{e} +% +% Use the ec* fonts (cm-super in outline format) for non-CM glyphs. +\def\ecfont{% + % We can't distinguish serif/sans and italic/slanted, but this + % is used for crude hacks anyway (like adding French and German + % quotes to documents typeset with CM, where we lose kerning), so + % hopefully nobody will notice/care. + \edef\ecsize{\csname\curfontsize ecsize\endcsname}% + \edef\nominalsize{\csname\curfontsize nominalsize\endcsname}% + \ifx\curfontstyle\bfstylename + % bold: + \font\thisecfont = ecb\ifusingit{i}{x}\ecsize \space at \nominalsize + \else + % regular: + \font\thisecfont = ec\ifusingit{ti}{rm}\ecsize \space at \nominalsize + \fi + \thisecfont +} + +% @registeredsymbol - R in a circle. The font for the R should really +% be smaller yet, but lllsize is the best we can do for now. +% Adapted from the plain.tex definition of \copyright. +% +\def\registeredsymbol{% + $^{{\ooalign{\hfil\raise.07ex\hbox{\selectfonts\lllsize R}% + \hfil\crcr\Orb}}% + }$% +} + +% @textdegree - the normal degrees sign. +% +\def\textdegree{$^\circ$} + +% Laurent Siebenmann reports \Orb undefined with: +% Textures 1.7.7 (preloaded format=plain 93.10.14) (68K) 16 APR 2004 02:38 +% so we'll define it if necessary. +% +\ifx\Orb\thisisundefined +\def\Orb{\mathhexbox20D} +\fi + +% Quotes. +\chardef\quotedblleft="5C +\chardef\quotedblright=`\" +\chardef\quoteleft=`\` +\chardef\quoteright=`\' + + +\message{page headings,} + +\newskip\titlepagetopglue \titlepagetopglue = 1.5in +\newskip\titlepagebottomglue \titlepagebottomglue = 2pc + +% First the title page. Must do @settitle before @titlepage. +\newif\ifseenauthor +\newif\iffinishedtitlepage + +% Do an implicit @contents or @shortcontents after @end titlepage if the +% user says @setcontentsaftertitlepage or @setshortcontentsaftertitlepage. +% +\newif\ifsetcontentsaftertitlepage + \let\setcontentsaftertitlepage = \setcontentsaftertitlepagetrue +\newif\ifsetshortcontentsaftertitlepage + \let\setshortcontentsaftertitlepage = \setshortcontentsaftertitlepagetrue + +\parseargdef\shorttitlepage{% + \begingroup \hbox{}\vskip 1.5in \chaprm \centerline{#1}% + \endgroup\page\hbox{}\page} + +\envdef\titlepage{% + % Open one extra group, as we want to close it in the middle of \Etitlepage. + \begingroup + \parindent=0pt \textfonts + % Leave some space at the very top of the page. + \vglue\titlepagetopglue + % No rule at page bottom unless we print one at the top with @title. + \finishedtitlepagetrue + % + % Most title ``pages'' are actually two pages long, with space + % at the top of the second. We don't want the ragged left on the second. + \let\oldpage = \page + \def\page{% + \iffinishedtitlepage\else + \finishtitlepage + \fi + \let\page = \oldpage + \page + \null + }% +} + +\def\Etitlepage{% + \iffinishedtitlepage\else + \finishtitlepage + \fi + % It is important to do the page break before ending the group, + % because the headline and footline are only empty inside the group. + % If we use the new definition of \page, we always get a blank page + % after the title page, which we certainly don't want. + \oldpage + \endgroup + % + % Need this before the \...aftertitlepage checks so that if they are + % in effect the toc pages will come out with page numbers. + \HEADINGSon + % + % If they want short, they certainly want long too. + \ifsetshortcontentsaftertitlepage + \shortcontents + \contents + \global\let\shortcontents = \relax + \global\let\contents = \relax + \fi + % + \ifsetcontentsaftertitlepage + \contents + \global\let\contents = \relax + \global\let\shortcontents = \relax + \fi +} + +\def\finishtitlepage{% + \vskip4pt \hrule height 2pt width \hsize + \vskip\titlepagebottomglue + \finishedtitlepagetrue +} + +% Macros to be used within @titlepage: + +\let\subtitlerm=\tenrm +\def\subtitlefont{\subtitlerm \normalbaselineskip = 13pt \normalbaselines} + +\parseargdef\title{% + \checkenv\titlepage + \leftline{\titlefonts\rmisbold #1} + % print a rule at the page bottom also. + \finishedtitlepagefalse + \vskip4pt \hrule height 4pt width \hsize \vskip4pt +} + +\parseargdef\subtitle{% + \checkenv\titlepage + {\subtitlefont \rightline{#1}}% +} + +% @author should come last, but may come many times. +% It can also be used inside @quotation. +% +\parseargdef\author{% + \def\temp{\quotation}% + \ifx\thisenv\temp + \def\quotationauthor{#1}% printed in \Equotation. + \else + \checkenv\titlepage + \ifseenauthor\else \vskip 0pt plus 1filll \seenauthortrue \fi + {\secfonts\rmisbold \leftline{#1}}% + \fi +} + + +% Set up page headings and footings. + +\let\thispage=\folio + +\newtoks\evenheadline % headline on even pages +\newtoks\oddheadline % headline on odd pages +\newtoks\evenfootline % footline on even pages +\newtoks\oddfootline % footline on odd pages + +% Now make TeX use those variables +\headline={{\textfonts\rm \ifodd\pageno \the\oddheadline + \else \the\evenheadline \fi}} +\footline={{\textfonts\rm \ifodd\pageno \the\oddfootline + \else \the\evenfootline \fi}\HEADINGShook} +\let\HEADINGShook=\relax + +% Commands to set those variables. +% For example, this is what @headings on does +% @evenheading @thistitle|@thispage|@thischapter +% @oddheading @thischapter|@thispage|@thistitle +% @evenfooting @thisfile|| +% @oddfooting ||@thisfile + + +\def\evenheading{\parsearg\evenheadingxxx} +\def\evenheadingxxx #1{\evenheadingyyy #1\|\|\|\|\finish} +\def\evenheadingyyy #1\|#2\|#3\|#4\finish{% +\global\evenheadline={\rlap{\centerline{#2}}\line{#1\hfil#3}}} + +\def\oddheading{\parsearg\oddheadingxxx} +\def\oddheadingxxx #1{\oddheadingyyy #1\|\|\|\|\finish} +\def\oddheadingyyy #1\|#2\|#3\|#4\finish{% +\global\oddheadline={\rlap{\centerline{#2}}\line{#1\hfil#3}}} + +\parseargdef\everyheading{\oddheadingxxx{#1}\evenheadingxxx{#1}}% + +\def\evenfooting{\parsearg\evenfootingxxx} +\def\evenfootingxxx #1{\evenfootingyyy #1\|\|\|\|\finish} +\def\evenfootingyyy #1\|#2\|#3\|#4\finish{% +\global\evenfootline={\rlap{\centerline{#2}}\line{#1\hfil#3}}} + +\def\oddfooting{\parsearg\oddfootingxxx} +\def\oddfootingxxx #1{\oddfootingyyy #1\|\|\|\|\finish} +\def\oddfootingyyy #1\|#2\|#3\|#4\finish{% + \global\oddfootline = {\rlap{\centerline{#2}}\line{#1\hfil#3}}% + % + % Leave some space for the footline. Hopefully ok to assume + % @evenfooting will not be used by itself. + \global\advance\pageheight by -12pt + \global\advance\vsize by -12pt +} + +\parseargdef\everyfooting{\oddfootingxxx{#1}\evenfootingxxx{#1}} + +% @evenheadingmarks top \thischapter <- chapter at the top of a page +% @evenheadingmarks bottom \thischapter <- chapter at the bottom of a page +% +% The same set of arguments for: +% +% @oddheadingmarks +% @evenfootingmarks +% @oddfootingmarks +% @everyheadingmarks +% @everyfootingmarks + +\def\evenheadingmarks{\headingmarks{even}{heading}} +\def\oddheadingmarks{\headingmarks{odd}{heading}} +\def\evenfootingmarks{\headingmarks{even}{footing}} +\def\oddfootingmarks{\headingmarks{odd}{footing}} +\def\everyheadingmarks#1 {\headingmarks{even}{heading}{#1} + \headingmarks{odd}{heading}{#1} } +\def\everyfootingmarks#1 {\headingmarks{even}{footing}{#1} + \headingmarks{odd}{footing}{#1} } +% #1 = even/odd, #2 = heading/footing, #3 = top/bottom. +\def\headingmarks#1#2#3 {% + \expandafter\let\expandafter\temp \csname get#3headingmarks\endcsname + \global\expandafter\let\csname get#1#2marks\endcsname \temp +} + +\everyheadingmarks bottom +\everyfootingmarks bottom + +% @headings double turns headings on for double-sided printing. +% @headings single turns headings on for single-sided printing. +% @headings off turns them off. +% @headings on same as @headings double, retained for compatibility. +% @headings after turns on double-sided headings after this page. +% @headings doubleafter turns on double-sided headings after this page. +% @headings singleafter turns on single-sided headings after this page. +% By default, they are off at the start of a document, +% and turned `on' after @end titlepage. + +\def\headings #1 {\csname HEADINGS#1\endcsname} + +\def\headingsoff{% non-global headings elimination + \evenheadline={\hfil}\evenfootline={\hfil}% + \oddheadline={\hfil}\oddfootline={\hfil}% +} + +\def\HEADINGSoff{{\globaldefs=1 \headingsoff}} % global setting +\HEADINGSoff % it's the default + +% When we turn headings on, set the page number to 1. +% For double-sided printing, put current file name in lower left corner, +% chapter name on inside top of right hand pages, document +% title on inside top of left hand pages, and page numbers on outside top +% edge of all pages. +\def\HEADINGSdouble{% +\global\pageno=1 +\global\evenfootline={\hfil} +\global\oddfootline={\hfil} +\global\evenheadline={\line{\folio\hfil\thistitle}} +\global\oddheadline={\line{\thischapter\hfil\folio}} +\global\let\contentsalignmacro = \chapoddpage +} +\let\contentsalignmacro = \chappager + +% For single-sided printing, chapter title goes across top left of page, +% page number on top right. +\def\HEADINGSsingle{% +\global\pageno=1 +\global\evenfootline={\hfil} +\global\oddfootline={\hfil} +\global\evenheadline={\line{\thischapter\hfil\folio}} +\global\oddheadline={\line{\thischapter\hfil\folio}} +\global\let\contentsalignmacro = \chappager +} +\def\HEADINGSon{\HEADINGSdouble} + +\def\HEADINGSafter{\let\HEADINGShook=\HEADINGSdoublex} +\let\HEADINGSdoubleafter=\HEADINGSafter +\def\HEADINGSdoublex{% +\global\evenfootline={\hfil} +\global\oddfootline={\hfil} +\global\evenheadline={\line{\folio\hfil\thistitle}} +\global\oddheadline={\line{\thischapter\hfil\folio}} +\global\let\contentsalignmacro = \chapoddpage +} + +\def\HEADINGSsingleafter{\let\HEADINGShook=\HEADINGSsinglex} +\def\HEADINGSsinglex{% +\global\evenfootline={\hfil} +\global\oddfootline={\hfil} +\global\evenheadline={\line{\thischapter\hfil\folio}} +\global\oddheadline={\line{\thischapter\hfil\folio}} +\global\let\contentsalignmacro = \chappager +} + +% Subroutines used in generating headings +% This produces Day Month Year style of output. +% Only define if not already defined, in case a txi-??.tex file has set +% up a different format (e.g., txi-cs.tex does this). +\ifx\today\thisisundefined +\def\today{% + \number\day\space + \ifcase\month + \or\putwordMJan\or\putwordMFeb\or\putwordMMar\or\putwordMApr + \or\putwordMMay\or\putwordMJun\or\putwordMJul\or\putwordMAug + \or\putwordMSep\or\putwordMOct\or\putwordMNov\or\putwordMDec + \fi + \space\number\year} +\fi + +% @settitle line... specifies the title of the document, for headings. +% It generates no output of its own. +\def\thistitle{\putwordNoTitle} +\def\settitle{\parsearg{\gdef\thistitle}} + + +\message{tables,} +% Tables -- @table, @ftable, @vtable, @item(x). + +% default indentation of table text +\newdimen\tableindent \tableindent=.8in +% default indentation of @itemize and @enumerate text +\newdimen\itemindent \itemindent=.3in +% margin between end of table item and start of table text. +\newdimen\itemmargin \itemmargin=.1in + +% used internally for \itemindent minus \itemmargin +\newdimen\itemmax + +% Note @table, @ftable, and @vtable define @item, @itemx, etc., with +% these defs. +% They also define \itemindex +% to index the item name in whatever manner is desired (perhaps none). + +\newif\ifitemxneedsnegativevskip + +\def\itemxpar{\par\ifitemxneedsnegativevskip\nobreak\vskip-\parskip\nobreak\fi} + +\def\internalBitem{\smallbreak \parsearg\itemzzz} +\def\internalBitemx{\itemxpar \parsearg\itemzzz} + +\def\itemzzz #1{\begingroup % + \advance\hsize by -\rightskip + \advance\hsize by -\tableindent + \setbox0=\hbox{\itemindicate{#1}}% + \itemindex{#1}% + \nobreak % This prevents a break before @itemx. + % + % If the item text does not fit in the space we have, put it on a line + % by itself, and do not allow a page break either before or after that + % line. We do not start a paragraph here because then if the next + % command is, e.g., @kindex, the whatsit would get put into the + % horizontal list on a line by itself, resulting in extra blank space. + \ifdim \wd0>\itemmax + % + % Make this a paragraph so we get the \parskip glue and wrapping, + % but leave it ragged-right. + \begingroup + \advance\leftskip by-\tableindent + \advance\hsize by\tableindent + \advance\rightskip by0pt plus1fil\relax + \leavevmode\unhbox0\par + \endgroup + % + % We're going to be starting a paragraph, but we don't want the + % \parskip glue -- logically it's part of the @item we just started. + \nobreak \vskip-\parskip + % + % Stop a page break at the \parskip glue coming up. However, if + % what follows is an environment such as @example, there will be no + % \parskip glue; then the negative vskip we just inserted would + % cause the example and the item to crash together. So we use this + % bizarre value of 10001 as a signal to \aboveenvbreak to insert + % \parskip glue after all. Section titles are handled this way also. + % + \penalty 10001 + \endgroup + \itemxneedsnegativevskipfalse + \else + % The item text fits into the space. Start a paragraph, so that the + % following text (if any) will end up on the same line. + \noindent + % Do this with kerns and \unhbox so that if there is a footnote in + % the item text, it can migrate to the main vertical list and + % eventually be printed. + \nobreak\kern-\tableindent + \dimen0 = \itemmax \advance\dimen0 by \itemmargin \advance\dimen0 by -\wd0 + \unhbox0 + \nobreak\kern\dimen0 + \endgroup + \itemxneedsnegativevskiptrue + \fi +} + +\def\item{\errmessage{@item while not in a list environment}} +\def\itemx{\errmessage{@itemx while not in a list environment}} + +% @table, @ftable, @vtable. +\envdef\table{% + \let\itemindex\gobble + \tablecheck{table}% +} +\envdef\ftable{% + \def\itemindex ##1{\doind {fn}{\code{##1}}}% + \tablecheck{ftable}% +} +\envdef\vtable{% + \def\itemindex ##1{\doind {vr}{\code{##1}}}% + \tablecheck{vtable}% +} +\def\tablecheck#1{% + \ifnum \the\catcode`\^^M=\active + \endgroup + \errmessage{This command won't work in this context; perhaps the problem is + that we are \inenvironment\thisenv}% + \def\next{\doignore{#1}}% + \else + \let\next\tablex + \fi + \next +} +\def\tablex#1{% + \def\itemindicate{#1}% + \parsearg\tabley +} +\def\tabley#1{% + {% + \makevalueexpandable + \edef\temp{\noexpand\tablez #1\space\space\space}% + \expandafter + }\temp \endtablez +} +\def\tablez #1 #2 #3 #4\endtablez{% + \aboveenvbreak + \ifnum 0#1>0 \advance \leftskip by #1\mil \fi + \ifnum 0#2>0 \tableindent=#2\mil \fi + \ifnum 0#3>0 \advance \rightskip by #3\mil \fi + \itemmax=\tableindent + \advance \itemmax by -\itemmargin + \advance \leftskip by \tableindent + \exdentamount=\tableindent + \parindent = 0pt + \parskip = \smallskipamount + \ifdim \parskip=0pt \parskip=2pt \fi + \let\item = \internalBitem + \let\itemx = \internalBitemx +} +\def\Etable{\endgraf\afterenvbreak} +\let\Eftable\Etable +\let\Evtable\Etable +\let\Eitemize\Etable +\let\Eenumerate\Etable + +% This is the counter used by @enumerate, which is really @itemize + +\newcount \itemno + +\envdef\itemize{\parsearg\doitemize} + +\def\doitemize#1{% + \aboveenvbreak + \itemmax=\itemindent + \advance\itemmax by -\itemmargin + \advance\leftskip by \itemindent + \exdentamount=\itemindent + \parindent=0pt + \parskip=\smallskipamount + \ifdim\parskip=0pt \parskip=2pt \fi + % + % Try typesetting the item mark that if the document erroneously says + % something like @itemize @samp (intending @table), there's an error + % right away at the @itemize. It's not the best error message in the + % world, but it's better than leaving it to the @item. This means if + % the user wants an empty mark, they have to say @w{} not just @w. + \def\itemcontents{#1}% + \setbox0 = \hbox{\itemcontents}% + % + % @itemize with no arg is equivalent to @itemize @bullet. + \ifx\itemcontents\empty\def\itemcontents{\bullet}\fi + % + \let\item=\itemizeitem +} + +% Definition of @item while inside @itemize and @enumerate. +% +\def\itemizeitem{% + \advance\itemno by 1 % for enumerations + {\let\par=\endgraf \smallbreak}% reasonable place to break + {% + % If the document has an @itemize directly after a section title, a + % \nobreak will be last on the list, and \sectionheading will have + % done a \vskip-\parskip. In that case, we don't want to zero + % parskip, or the item text will crash with the heading. On the + % other hand, when there is normal text preceding the item (as there + % usually is), we do want to zero parskip, or there would be too much + % space. In that case, we won't have a \nobreak before. At least + % that's the theory. + \ifnum\lastpenalty<10000 \parskip=0in \fi + \noindent + \hbox to 0pt{\hss \itemcontents \kern\itemmargin}% + % + \vadjust{\penalty 1200}}% not good to break after first line of item. + \flushcr +} + +% \splitoff TOKENS\endmark defines \first to be the first token in +% TOKENS, and \rest to be the remainder. +% +\def\splitoff#1#2\endmark{\def\first{#1}\def\rest{#2}}% + +% Allow an optional argument of an uppercase letter, lowercase letter, +% or number, to specify the first label in the enumerated list. No +% argument is the same as `1'. +% +\envparseargdef\enumerate{\enumeratey #1 \endenumeratey} +\def\enumeratey #1 #2\endenumeratey{% + % If we were given no argument, pretend we were given `1'. + \def\thearg{#1}% + \ifx\thearg\empty \def\thearg{1}\fi + % + % Detect if the argument is a single token. If so, it might be a + % letter. Otherwise, the only valid thing it can be is a number. + % (We will always have one token, because of the test we just made. + % This is a good thing, since \splitoff doesn't work given nothing at + % all -- the first parameter is undelimited.) + \expandafter\splitoff\thearg\endmark + \ifx\rest\empty + % Only one token in the argument. It could still be anything. + % A ``lowercase letter'' is one whose \lccode is nonzero. + % An ``uppercase letter'' is one whose \lccode is both nonzero, and + % not equal to itself. + % Otherwise, we assume it's a number. + % + % We need the \relax at the end of the \ifnum lines to stop TeX from + % continuing to look for a . + % + \ifnum\lccode\expandafter`\thearg=0\relax + \numericenumerate % a number (we hope) + \else + % It's a letter. + \ifnum\lccode\expandafter`\thearg=\expandafter`\thearg\relax + \lowercaseenumerate % lowercase letter + \else + \uppercaseenumerate % uppercase letter + \fi + \fi + \else + % Multiple tokens in the argument. We hope it's a number. + \numericenumerate + \fi +} + +% An @enumerate whose labels are integers. The starting integer is +% given in \thearg. +% +\def\numericenumerate{% + \itemno = \thearg + \startenumeration{\the\itemno}% +} + +% The starting (lowercase) letter is in \thearg. +\def\lowercaseenumerate{% + \itemno = \expandafter`\thearg + \startenumeration{% + % Be sure we're not beyond the end of the alphabet. + \ifnum\itemno=0 + \errmessage{No more lowercase letters in @enumerate; get a bigger + alphabet}% + \fi + \char\lccode\itemno + }% +} + +% The starting (uppercase) letter is in \thearg. +\def\uppercaseenumerate{% + \itemno = \expandafter`\thearg + \startenumeration{% + % Be sure we're not beyond the end of the alphabet. + \ifnum\itemno=0 + \errmessage{No more uppercase letters in @enumerate; get a bigger + alphabet} + \fi + \char\uccode\itemno + }% +} + +% Call \doitemize, adding a period to the first argument and supplying the +% common last two arguments. Also subtract one from the initial value in +% \itemno, since @item increments \itemno. +% +\def\startenumeration#1{% + \advance\itemno by -1 + \doitemize{#1.}\flushcr +} + +% @alphaenumerate and @capsenumerate are abbreviations for giving an arg +% to @enumerate. +% +\def\alphaenumerate{\enumerate{a}} +\def\capsenumerate{\enumerate{A}} +\def\Ealphaenumerate{\Eenumerate} +\def\Ecapsenumerate{\Eenumerate} + + +% @multitable macros +% Amy Hendrickson, 8/18/94, 3/6/96 +% +% @multitable ... @end multitable will make as many columns as desired. +% Contents of each column will wrap at width given in preamble. Width +% can be specified either with sample text given in a template line, +% or in percent of \hsize, the current width of text on page. + +% Table can continue over pages but will only break between lines. + +% To make preamble: +% +% Either define widths of columns in terms of percent of \hsize: +% @multitable @columnfractions .25 .3 .45 +% @item ... +% +% Numbers following @columnfractions are the percent of the total +% current hsize to be used for each column. You may use as many +% columns as desired. + + +% Or use a template: +% @multitable {Column 1 template} {Column 2 template} {Column 3 template} +% @item ... +% using the widest term desired in each column. + +% Each new table line starts with @item, each subsequent new column +% starts with @tab. Empty columns may be produced by supplying @tab's +% with nothing between them for as many times as empty columns are needed, +% ie, @tab@tab@tab will produce two empty columns. + +% @item, @tab do not need to be on their own lines, but it will not hurt +% if they are. + +% Sample multitable: + +% @multitable {Column 1 template} {Column 2 template} {Column 3 template} +% @item first col stuff @tab second col stuff @tab third col +% @item +% first col stuff +% @tab +% second col stuff +% @tab +% third col +% @item first col stuff @tab second col stuff +% @tab Many paragraphs of text may be used in any column. +% +% They will wrap at the width determined by the template. +% @item@tab@tab This will be in third column. +% @end multitable + +% Default dimensions may be reset by user. +% @multitableparskip is vertical space between paragraphs in table. +% @multitableparindent is paragraph indent in table. +% @multitablecolmargin is horizontal space to be left between columns. +% @multitablelinespace is space to leave between table items, baseline +% to baseline. +% 0pt means it depends on current normal line spacing. +% +\newskip\multitableparskip +\newskip\multitableparindent +\newdimen\multitablecolspace +\newskip\multitablelinespace +\multitableparskip=0pt +\multitableparindent=6pt +\multitablecolspace=12pt +\multitablelinespace=0pt + +% Macros used to set up halign preamble: +% +\let\endsetuptable\relax +\def\xendsetuptable{\endsetuptable} +\let\columnfractions\relax +\def\xcolumnfractions{\columnfractions} +\newif\ifsetpercent + +% #1 is the @columnfraction, usually a decimal number like .5, but might +% be just 1. We just use it, whatever it is. +% +\def\pickupwholefraction#1 {% + \global\advance\colcount by 1 + \expandafter\xdef\csname col\the\colcount\endcsname{#1\hsize}% + \setuptable +} + +\newcount\colcount +\def\setuptable#1{% + \def\firstarg{#1}% + \ifx\firstarg\xendsetuptable + \let\go = \relax + \else + \ifx\firstarg\xcolumnfractions + \global\setpercenttrue + \else + \ifsetpercent + \let\go\pickupwholefraction + \else + \global\advance\colcount by 1 + \setbox0=\hbox{#1\unskip\space}% Add a normal word space as a + % separator; typically that is always in the input, anyway. + \expandafter\xdef\csname col\the\colcount\endcsname{\the\wd0}% + \fi + \fi + \ifx\go\pickupwholefraction + % Put the argument back for the \pickupwholefraction call, so + % we'll always have a period there to be parsed. + \def\go{\pickupwholefraction#1}% + \else + \let\go = \setuptable + \fi% + \fi + \go +} + +% multitable-only commands. +% +% @headitem starts a heading row, which we typeset in bold. +% Assignments have to be global since we are inside the implicit group +% of an alignment entry. \everycr resets \everytab so we don't have to +% undo it ourselves. +\def\headitemfont{\b}% for people to use in the template row; not changeable +\def\headitem{% + \checkenv\multitable + \crcr + \global\everytab={\bf}% can't use \headitemfont since the parsing differs + \the\everytab % for the first item +}% +% +% A \tab used to include \hskip1sp. But then the space in a template +% line is not enough. That is bad. So let's go back to just `&' until +% we again encounter the problem the 1sp was intended to solve. +% --karl, nathan@acm.org, 20apr99. +\def\tab{\checkenv\multitable &\the\everytab}% + +% @multitable ... @end multitable definitions: +% +\newtoks\everytab % insert after every tab. +% +\envdef\multitable{% + \vskip\parskip + \startsavinginserts + % + % @item within a multitable starts a normal row. + % We use \def instead of \let so that if one of the multitable entries + % contains an @itemize, we don't choke on the \item (seen as \crcr aka + % \endtemplate) expanding \doitemize. + \def\item{\crcr}% + % + \tolerance=9500 + \hbadness=9500 + \setmultitablespacing + \parskip=\multitableparskip + \parindent=\multitableparindent + \overfullrule=0pt + \global\colcount=0 + % + \everycr = {% + \noalign{% + \global\everytab={}% + \global\colcount=0 % Reset the column counter. + % Check for saved footnotes, etc. + \checkinserts + % Keeps underfull box messages off when table breaks over pages. + %\filbreak + % Maybe so, but it also creates really weird page breaks when the + % table breaks over pages. Wouldn't \vfil be better? Wait until the + % problem manifests itself, so it can be fixed for real --karl. + }% + }% + % + \parsearg\domultitable +} +\def\domultitable#1{% + % To parse everything between @multitable and @item: + \setuptable#1 \endsetuptable + % + % This preamble sets up a generic column definition, which will + % be used as many times as user calls for columns. + % \vtop will set a single line and will also let text wrap and + % continue for many paragraphs if desired. + \halign\bgroup &% + \global\advance\colcount by 1 + \multistrut + \vtop{% + % Use the current \colcount to find the correct column width: + \hsize=\expandafter\csname col\the\colcount\endcsname + % + % In order to keep entries from bumping into each other + % we will add a \leftskip of \multitablecolspace to all columns after + % the first one. + % + % If a template has been used, we will add \multitablecolspace + % to the width of each template entry. + % + % If the user has set preamble in terms of percent of \hsize we will + % use that dimension as the width of the column, and the \leftskip + % will keep entries from bumping into each other. Table will start at + % left margin and final column will justify at right margin. + % + % Make sure we don't inherit \rightskip from the outer environment. + \rightskip=0pt + \ifnum\colcount=1 + % The first column will be indented with the surrounding text. + \advance\hsize by\leftskip + \else + \ifsetpercent \else + % If user has not set preamble in terms of percent of \hsize + % we will advance \hsize by \multitablecolspace. + \advance\hsize by \multitablecolspace + \fi + % In either case we will make \leftskip=\multitablecolspace: + \leftskip=\multitablecolspace + \fi + % Ignoring space at the beginning and end avoids an occasional spurious + % blank line, when TeX decides to break the line at the space before the + % box from the multistrut, so the strut ends up on a line by itself. + % For example: + % @multitable @columnfractions .11 .89 + % @item @code{#} + % @tab Legal holiday which is valid in major parts of the whole country. + % Is automatically provided with highlighting sequences respectively + % marking characters. + \noindent\ignorespaces##\unskip\multistrut + }\cr +} +\def\Emultitable{% + \crcr + \egroup % end the \halign + \global\setpercentfalse +} + +\def\setmultitablespacing{% + \def\multistrut{\strut}% just use the standard line spacing + % + % Compute \multitablelinespace (if not defined by user) for use in + % \multitableparskip calculation. We used define \multistrut based on + % this, but (ironically) that caused the spacing to be off. + % See bug-texinfo report from Werner Lemberg, 31 Oct 2004 12:52:20 +0100. +\ifdim\multitablelinespace=0pt +\setbox0=\vbox{X}\global\multitablelinespace=\the\baselineskip +\global\advance\multitablelinespace by-\ht0 +\fi +% Test to see if parskip is larger than space between lines of +% table. If not, do nothing. +% If so, set to same dimension as multitablelinespace. +\ifdim\multitableparskip>\multitablelinespace +\global\multitableparskip=\multitablelinespace +\global\advance\multitableparskip-7pt % to keep parskip somewhat smaller + % than skip between lines in the table. +\fi% +\ifdim\multitableparskip=0pt +\global\multitableparskip=\multitablelinespace +\global\advance\multitableparskip-7pt % to keep parskip somewhat smaller + % than skip between lines in the table. +\fi} + + +\message{conditionals,} + +% @iftex, @ifnotdocbook, @ifnothtml, @ifnotinfo, @ifnotplaintext, +% @ifnotxml always succeed. They currently do nothing; we don't +% attempt to check whether the conditionals are properly nested. But we +% have to remember that they are conditionals, so that @end doesn't +% attempt to close an environment group. +% +\def\makecond#1{% + \expandafter\let\csname #1\endcsname = \relax + \expandafter\let\csname iscond.#1\endcsname = 1 +} +\makecond{iftex} +\makecond{ifnotdocbook} +\makecond{ifnothtml} +\makecond{ifnotinfo} +\makecond{ifnotplaintext} +\makecond{ifnotxml} + +% Ignore @ignore, @ifhtml, @ifinfo, and the like. +% +\def\direntry{\doignore{direntry}} +\def\documentdescription{\doignore{documentdescription}} +\def\docbook{\doignore{docbook}} +\def\html{\doignore{html}} +\def\ifdocbook{\doignore{ifdocbook}} +\def\ifhtml{\doignore{ifhtml}} +\def\ifinfo{\doignore{ifinfo}} +\def\ifnottex{\doignore{ifnottex}} +\def\ifplaintext{\doignore{ifplaintext}} +\def\ifxml{\doignore{ifxml}} +\def\ignore{\doignore{ignore}} +\def\menu{\doignore{menu}} +\def\xml{\doignore{xml}} + +% Ignore text until a line `@end #1', keeping track of nested conditionals. +% +% A count to remember the depth of nesting. +\newcount\doignorecount + +\def\doignore#1{\begingroup + % Scan in ``verbatim'' mode: + \obeylines + \catcode`\@ = \other + \catcode`\{ = \other + \catcode`\} = \other + % + % Make sure that spaces turn into tokens that match what \doignoretext wants. + \spaceisspace + % + % Count number of #1's that we've seen. + \doignorecount = 0 + % + % Swallow text until we reach the matching `@end #1'. + \dodoignore{#1}% +} + +{ \catcode`_=11 % We want to use \_STOP_ which cannot appear in texinfo source. + \obeylines % + % + \gdef\dodoignore#1{% + % #1 contains the command name as a string, e.g., `ifinfo'. + % + % Define a command to find the next `@end #1'. + \long\def\doignoretext##1^^M@end #1{% + \doignoretextyyy##1^^M@#1\_STOP_}% + % + % And this command to find another #1 command, at the beginning of a + % line. (Otherwise, we would consider a line `@c @ifset', for + % example, to count as an @ifset for nesting.) + \long\def\doignoretextyyy##1^^M@#1##2\_STOP_{\doignoreyyy{##2}\_STOP_}% + % + % And now expand that command. + \doignoretext ^^M% + }% +} + +\def\doignoreyyy#1{% + \def\temp{#1}% + \ifx\temp\empty % Nothing found. + \let\next\doignoretextzzz + \else % Found a nested condition, ... + \advance\doignorecount by 1 + \let\next\doignoretextyyy % ..., look for another. + % If we're here, #1 ends with ^^M\ifinfo (for example). + \fi + \next #1% the token \_STOP_ is present just after this macro. +} + +% We have to swallow the remaining "\_STOP_". +% +\def\doignoretextzzz#1{% + \ifnum\doignorecount = 0 % We have just found the outermost @end. + \let\next\enddoignore + \else % Still inside a nested condition. + \advance\doignorecount by -1 + \let\next\doignoretext % Look for the next @end. + \fi + \next +} + +% Finish off ignored text. +{ \obeylines% + % Ignore anything after the last `@end #1'; this matters in verbatim + % environments, where otherwise the newline after an ignored conditional + % would result in a blank line in the output. + \gdef\enddoignore#1^^M{\endgroup\ignorespaces}% +} + + +% @set VAR sets the variable VAR to an empty value. +% @set VAR REST-OF-LINE sets VAR to the value REST-OF-LINE. +% +% Since we want to separate VAR from REST-OF-LINE (which might be +% empty), we can't just use \parsearg; we have to insert a space of our +% own to delimit the rest of the line, and then take it out again if we +% didn't need it. +% We rely on the fact that \parsearg sets \catcode`\ =10. +% +\parseargdef\set{\setyyy#1 \endsetyyy} +\def\setyyy#1 #2\endsetyyy{% + {% + \makevalueexpandable + \def\temp{#2}% + \edef\next{\gdef\makecsname{SET#1}}% + \ifx\temp\empty + \next{}% + \else + \setzzz#2\endsetzzz + \fi + }% +} +% Remove the trailing space \setxxx inserted. +\def\setzzz#1 \endsetzzz{\next{#1}} + +% @clear VAR clears (i.e., unsets) the variable VAR. +% +\parseargdef\clear{% + {% + \makevalueexpandable + \global\expandafter\let\csname SET#1\endcsname=\relax + }% +} + +% @value{foo} gets the text saved in variable foo. +\def\value{\begingroup\makevalueexpandable\valuexxx} +\def\valuexxx#1{\expandablevalue{#1}\endgroup} +{ + \catcode`\- = \active \catcode`\_ = \active + % + \gdef\makevalueexpandable{% + \let\value = \expandablevalue + % We don't want these characters active, ... + \catcode`\-=\other \catcode`\_=\other + % ..., but we might end up with active ones in the argument if + % we're called from @code, as @code{@value{foo-bar_}}, though. + % So \let them to their normal equivalents. + \let-\realdash \let_\normalunderscore + } +} + +% We have this subroutine so that we can handle at least some @value's +% properly in indexes (we call \makevalueexpandable in \indexdummies). +% The command has to be fully expandable (if the variable is set), since +% the result winds up in the index file. This means that if the +% variable's value contains other Texinfo commands, it's almost certain +% it will fail (although perhaps we could fix that with sufficient work +% to do a one-level expansion on the result, instead of complete). +% +\def\expandablevalue#1{% + \expandafter\ifx\csname SET#1\endcsname\relax + {[No value for ``#1'']}% + \message{Variable `#1', used in @value, is not set.}% + \else + \csname SET#1\endcsname + \fi +} + +% @ifset VAR ... @end ifset reads the `...' iff VAR has been defined +% with @set. +% +% To get special treatment of `@end ifset,' call \makeond and the redefine. +% +\makecond{ifset} +\def\ifset{\parsearg{\doifset{\let\next=\ifsetfail}}} +\def\doifset#1#2{% + {% + \makevalueexpandable + \let\next=\empty + \expandafter\ifx\csname SET#2\endcsname\relax + #1% If not set, redefine \next. + \fi + \expandafter + }\next +} +\def\ifsetfail{\doignore{ifset}} + +% @ifclear VAR ... @end ifclear reads the `...' iff VAR has never been +% defined with @set, or has been undefined with @clear. +% +% The `\else' inside the `\doifset' parameter is a trick to reuse the +% above code: if the variable is not set, do nothing, if it is set, +% then redefine \next to \ifclearfail. +% +\makecond{ifclear} +\def\ifclear{\parsearg{\doifset{\else \let\next=\ifclearfail}}} +\def\ifclearfail{\doignore{ifclear}} + +% @dircategory CATEGORY -- specify a category of the dir file +% which this file should belong to. Ignore this in TeX. +\let\dircategory=\comment + +% @defininfoenclose. +\let\definfoenclose=\comment + + +\message{indexing,} +% Index generation facilities + +% Define \newwrite to be identical to plain tex's \newwrite +% except not \outer, so it can be used within macros and \if's. +\edef\newwrite{\makecsname{ptexnewwrite}} + +% \newindex {foo} defines an index named foo. +% It automatically defines \fooindex such that +% \fooindex ...rest of line... puts an entry in the index foo. +% It also defines \fooindfile to be the number of the output channel for +% the file that accumulates this index. The file's extension is foo. +% The name of an index should be no more than 2 characters long +% for the sake of vms. +% +\def\newindex#1{% + \iflinks + \expandafter\newwrite \csname#1indfile\endcsname + \openout \csname#1indfile\endcsname \jobname.#1 % Open the file + \fi + \expandafter\xdef\csname#1index\endcsname{% % Define @#1index + \noexpand\doindex{#1}} +} + +% @defindex foo == \newindex{foo} +% +\def\defindex{\parsearg\newindex} + +% Define @defcodeindex, like @defindex except put all entries in @code. +% +\def\defcodeindex{\parsearg\newcodeindex} +% +\def\newcodeindex#1{% + \iflinks + \expandafter\newwrite \csname#1indfile\endcsname + \openout \csname#1indfile\endcsname \jobname.#1 + \fi + \expandafter\xdef\csname#1index\endcsname{% + \noexpand\docodeindex{#1}}% +} + + +% @synindex foo bar makes index foo feed into index bar. +% Do this instead of @defindex foo if you don't want it as a separate index. +% +% @syncodeindex foo bar similar, but put all entries made for index foo +% inside @code. +% +\def\synindex#1 #2 {\dosynindex\doindex{#1}{#2}} +\def\syncodeindex#1 #2 {\dosynindex\docodeindex{#1}{#2}} + +% #1 is \doindex or \docodeindex, #2 the index getting redefined (foo), +% #3 the target index (bar). +\def\dosynindex#1#2#3{% + % Only do \closeout if we haven't already done it, else we'll end up + % closing the target index. + \expandafter \ifx\csname donesynindex#2\endcsname \relax + % The \closeout helps reduce unnecessary open files; the limit on the + % Acorn RISC OS is a mere 16 files. + \expandafter\closeout\csname#2indfile\endcsname + \expandafter\let\csname donesynindex#2\endcsname = 1 + \fi + % redefine \fooindfile: + \expandafter\let\expandafter\temp\expandafter=\csname#3indfile\endcsname + \expandafter\let\csname#2indfile\endcsname=\temp + % redefine \fooindex: + \expandafter\xdef\csname#2index\endcsname{\noexpand#1{#3}}% +} + +% Define \doindex, the driver for all \fooindex macros. +% Argument #1 is generated by the calling \fooindex macro, +% and it is "foo", the name of the index. + +% \doindex just uses \parsearg; it calls \doind for the actual work. +% This is because \doind is more useful to call from other macros. + +% There is also \dosubind {index}{topic}{subtopic} +% which makes an entry in a two-level index such as the operation index. + +\def\doindex#1{\edef\indexname{#1}\parsearg\singleindexer} +\def\singleindexer #1{\doind{\indexname}{#1}} + +% like the previous two, but they put @code around the argument. +\def\docodeindex#1{\edef\indexname{#1}\parsearg\singlecodeindexer} +\def\singlecodeindexer #1{\doind{\indexname}{\code{#1}}} + +% Take care of Texinfo commands that can appear in an index entry. +% Since there are some commands we want to expand, and others we don't, +% we have to laboriously prevent expansion for those that we don't. +% +\def\indexdummies{% + \escapechar = `\\ % use backslash in output files. + \def\@{@}% change to @@ when we switch to @ as escape char in index files. + \def\ {\realbackslash\space }% + % + % Need these unexpandable (because we define \tt as a dummy) + % definitions when @{ or @} appear in index entry text. Also, more + % complicated, when \tex is in effect and \{ is a \delimiter again. + % We can't use \lbracecmd and \rbracecmd because texindex assumes + % braces and backslashes are used only as delimiters. Perhaps we + % should define @lbrace and @rbrace commands a la @comma. + \def\{{{\tt\char123}}% + \def\}{{\tt\char125}}% + % + % I don't entirely understand this, but when an index entry is + % generated from a macro call, the \endinput which \scanmacro inserts + % causes processing to be prematurely terminated. This is, + % apparently, because \indexsorttmp is fully expanded, and \endinput + % is an expandable command. The redefinition below makes \endinput + % disappear altogether for that purpose -- although logging shows that + % processing continues to some further point. On the other hand, it + % seems \endinput does not hurt in the printed index arg, since that + % is still getting written without apparent harm. + % + % Sample source (mac-idx3.tex, reported by Graham Percival to + % help-texinfo, 22may06): + % @macro funindex {WORD} + % @findex xyz + % @end macro + % ... + % @funindex commtest + % + % The above is not enough to reproduce the bug, but it gives the flavor. + % + % Sample whatsit resulting: + % .@write3{\entry{xyz}{@folio }{@code {xyz@endinput }}} + % + % So: + \let\endinput = \empty + % + % Do the redefinitions. + \commondummies +} + +% For the aux and toc files, @ is the escape character. So we want to +% redefine everything using @ as the escape character (instead of +% \realbackslash, still used for index files). When everything uses @, +% this will be simpler. +% +\def\atdummies{% + \def\@{@@}% + \def\ {@ }% + \let\{ = \lbraceatcmd + \let\} = \rbraceatcmd + % + % Do the redefinitions. + \commondummies + \otherbackslash +} + +% Called from \indexdummies and \atdummies. +% +\def\commondummies{% + % + % \definedummyword defines \#1 as \string\#1\space, thus effectively + % preventing its expansion. This is used only for control words, + % not control letters, because the \space would be incorrect for + % control characters, but is needed to separate the control word + % from whatever follows. + % + % For control letters, we have \definedummyletter, which omits the + % space. + % + % These can be used both for control words that take an argument and + % those that do not. If it is followed by {arg} in the input, then + % that will dutifully get written to the index (or wherever). + % + \def\definedummyword ##1{\def##1{\string##1\space}}% + \def\definedummyletter##1{\def##1{\string##1}}% + \let\definedummyaccent\definedummyletter + % + \commondummiesnofonts + % + \definedummyletter\_% + \definedummyletter\-% + % + % Non-English letters. + \definedummyword\AA + \definedummyword\AE + \definedummyword\DH + \definedummyword\L + \definedummyword\O + \definedummyword\OE + \definedummyword\TH + \definedummyword\aa + \definedummyword\ae + \definedummyword\dh + \definedummyword\exclamdown + \definedummyword\l + \definedummyword\o + \definedummyword\oe + \definedummyword\ordf + \definedummyword\ordm + \definedummyword\questiondown + \definedummyword\ss + \definedummyword\th + % + % Although these internal commands shouldn't show up, sometimes they do. + \definedummyword\bf + \definedummyword\gtr + \definedummyword\hat + \definedummyword\less + \definedummyword\sf + \definedummyword\sl + \definedummyword\tclose + \definedummyword\tt + % + \definedummyword\LaTeX + \definedummyword\TeX + % + % Assorted special characters. + \definedummyword\arrow + \definedummyword\bullet + \definedummyword\comma + \definedummyword\copyright + \definedummyword\registeredsymbol + \definedummyword\dots + \definedummyword\enddots + \definedummyword\entrybreak + \definedummyword\equiv + \definedummyword\error + \definedummyword\euro + \definedummyword\expansion + \definedummyword\geq + \definedummyword\guillemetleft + \definedummyword\guillemetright + \definedummyword\guilsinglleft + \definedummyword\guilsinglright + \definedummyword\leq + \definedummyword\minus + \definedummyword\ogonek + \definedummyword\pounds + \definedummyword\point + \definedummyword\print + \definedummyword\quotedblbase + \definedummyword\quotedblleft + \definedummyword\quotedblright + \definedummyword\quoteleft + \definedummyword\quoteright + \definedummyword\quotesinglbase + \definedummyword\result + \definedummyword\textdegree + % + % We want to disable all macros so that they are not expanded by \write. + \macrolist + % + \normalturnoffactive + % + % Handle some cases of @value -- where it does not contain any + % (non-fully-expandable) commands. + \makevalueexpandable +} + +% \commondummiesnofonts: common to \commondummies and \indexnofonts. +% +\def\commondummiesnofonts{% + % Control letters and accents. + \definedummyletter\!% + \definedummyaccent\"% + \definedummyaccent\'% + \definedummyletter\*% + \definedummyaccent\,% + \definedummyletter\.% + \definedummyletter\/% + \definedummyletter\:% + \definedummyaccent\=% + \definedummyletter\?% + \definedummyaccent\^% + \definedummyaccent\`% + \definedummyaccent\~% + \definedummyword\u + \definedummyword\v + \definedummyword\H + \definedummyword\dotaccent + \definedummyword\ogonek + \definedummyword\ringaccent + \definedummyword\tieaccent + \definedummyword\ubaraccent + \definedummyword\udotaccent + \definedummyword\dotless + % + % Texinfo font commands. + \definedummyword\b + \definedummyword\i + \definedummyword\r + \definedummyword\sansserif + \definedummyword\sc + \definedummyword\slanted + \definedummyword\t + % + % Commands that take arguments. + \definedummyword\acronym + \definedummyword\anchor + \definedummyword\cite + \definedummyword\code + \definedummyword\command + \definedummyword\dfn + \definedummyword\dmn + \definedummyword\email + \definedummyword\emph + \definedummyword\env + \definedummyword\file + \definedummyword\indicateurl + \definedummyword\kbd + \definedummyword\key + \definedummyword\math + \definedummyword\option + \definedummyword\pxref + \definedummyword\ref + \definedummyword\samp + \definedummyword\strong + \definedummyword\tie + \definedummyword\uref + \definedummyword\url + \definedummyword\var + \definedummyword\verb + \definedummyword\w + \definedummyword\xref +} + +% \indexnofonts is used when outputting the strings to sort the index +% by, and when constructing control sequence names. It eliminates all +% control sequences and just writes whatever the best ASCII sort string +% would be for a given command (usually its argument). +% +\def\indexnofonts{% + % Accent commands should become @asis. + \def\definedummyaccent##1{\let##1\asis}% + % We can just ignore other control letters. + \def\definedummyletter##1{\let##1\empty}% + % All control words become @asis by default; overrides below. + \let\definedummyword\definedummyaccent + % + \commondummiesnofonts + % + % Don't no-op \tt, since it isn't a user-level command + % and is used in the definitions of the active chars like <, >, |, etc. + % Likewise with the other plain tex font commands. + %\let\tt=\asis + % + \def\ { }% + \def\@{@}% + \def\_{\normalunderscore}% + \def\-{}% @- shouldn't affect sorting + % + % Unfortunately, texindex is not prepared to handle braces in the + % content at all. So for index sorting, we map @{ and @} to strings + % starting with |, since that ASCII character is between ASCII { and }. + \def\{{|a}% + \def\}{|b}% + % + % Non-English letters. + \def\AA{AA}% + \def\AE{AE}% + \def\DH{DZZ}% + \def\L{L}% + \def\OE{OE}% + \def\O{O}% + \def\TH{ZZZ}% + \def\aa{aa}% + \def\ae{ae}% + \def\dh{dzz}% + \def\exclamdown{!}% + \def\l{l}% + \def\oe{oe}% + \def\ordf{a}% + \def\ordm{o}% + \def\o{o}% + \def\questiondown{?}% + \def\ss{ss}% + \def\th{zzz}% + % + \def\LaTeX{LaTeX}% + \def\TeX{TeX}% + % + % Assorted special characters. + % (The following {} will end up in the sort string, but that's ok.) + \def\arrow{->}% + \def\bullet{bullet}% + \def\comma{,}% + \def\copyright{copyright}% + \def\dots{...}% + \def\enddots{...}% + \def\equiv{==}% + \def\error{error}% + \def\euro{euro}% + \def\expansion{==>}% + \def\geq{>=}% + \def\guillemetleft{<<}% + \def\guillemetright{>>}% + \def\guilsinglleft{<}% + \def\guilsinglright{>}% + \def\leq{<=}% + \def\minus{-}% + \def\point{.}% + \def\pounds{pounds}% + \def\print{-|}% + \def\quotedblbase{"}% + \def\quotedblleft{"}% + \def\quotedblright{"}% + \def\quoteleft{`}% + \def\quoteright{'}% + \def\quotesinglbase{,}% + \def\registeredsymbol{R}% + \def\result{=>}% + \def\textdegree{o}% + % + \expandafter\ifx\csname SETtxiindexlquoteignore\endcsname\relax + \else \indexlquoteignore \fi + % + % We need to get rid of all macros, leaving only the arguments (if present). + % Of course this is not nearly correct, but it is the best we can do for now. + % makeinfo does not expand macros in the argument to @deffn, which ends up + % writing an index entry, and texindex isn't prepared for an index sort entry + % that starts with \. + % + % Since macro invocations are followed by braces, we can just redefine them + % to take a single TeX argument. The case of a macro invocation that + % goes to end-of-line is not handled. + % + \macrolist +} + +% Undocumented (for FSFS 2nd ed.): @set txiindexlquoteignore makes us +% ignore left quotes in the sort term. +{\catcode`\`=\active + \gdef\indexlquoteignore{\let`=\empty}} + +\let\indexbackslash=0 %overridden during \printindex. +\let\SETmarginindex=\relax % put index entries in margin (undocumented)? + +% Most index entries go through here, but \dosubind is the general case. +% #1 is the index name, #2 is the entry text. +\def\doind#1#2{\dosubind{#1}{#2}{}} + +% Workhorse for all \fooindexes. +% #1 is name of index, #2 is stuff to put there, #3 is subentry -- +% empty if called from \doind, as we usually are (the main exception +% is with most defuns, which call us directly). +% +\def\dosubind#1#2#3{% + \iflinks + {% + % Store the main index entry text (including the third arg). + \toks0 = {#2}% + % If third arg is present, precede it with a space. + \def\thirdarg{#3}% + \ifx\thirdarg\empty \else + \toks0 = \expandafter{\the\toks0 \space #3}% + \fi + % + \edef\writeto{\csname#1indfile\endcsname}% + % + \safewhatsit\dosubindwrite + }% + \fi +} + +% Write the entry in \toks0 to the index file: +% +\def\dosubindwrite{% + % Put the index entry in the margin if desired. + \ifx\SETmarginindex\relax\else + \insert\margin{\hbox{\vrule height8pt depth3pt width0pt \the\toks0}}% + \fi + % + % Remember, we are within a group. + \indexdummies % Must do this here, since \bf, etc expand at this stage + \def\backslashcurfont{\indexbackslash}% \indexbackslash isn't defined now + % so it will be output as is; and it will print as backslash. + % + % Process the index entry with all font commands turned off, to + % get the string to sort by. + {\indexnofonts + \edef\temp{\the\toks0}% need full expansion + \xdef\indexsorttmp{\temp}% + }% + % + % Set up the complete index entry, with both the sort key and + % the original text, including any font commands. We write + % three arguments to \entry to the .?? file (four in the + % subentry case), texindex reduces to two when writing the .??s + % sorted result. + \edef\temp{% + \write\writeto{% + \string\entry{\indexsorttmp}{\noexpand\folio}{\the\toks0}}% + }% + \temp +} + +% Take care of unwanted page breaks/skips around a whatsit: +% +% If a skip is the last thing on the list now, preserve it +% by backing up by \lastskip, doing the \write, then inserting +% the skip again. Otherwise, the whatsit generated by the +% \write or \pdfdest will make \lastskip zero. The result is that +% sequences like this: +% @end defun +% @tindex whatever +% @defun ... +% will have extra space inserted, because the \medbreak in the +% start of the @defun won't see the skip inserted by the @end of +% the previous defun. +% +% But don't do any of this if we're not in vertical mode. We +% don't want to do a \vskip and prematurely end a paragraph. +% +% Avoid page breaks due to these extra skips, too. +% +% But wait, there is a catch there: +% We'll have to check whether \lastskip is zero skip. \ifdim is not +% sufficient for this purpose, as it ignores stretch and shrink parts +% of the skip. The only way seems to be to check the textual +% representation of the skip. +% +% The following is almost like \def\zeroskipmacro{0.0pt} except that +% the ``p'' and ``t'' characters have catcode \other, not 11 (letter). +% +\edef\zeroskipmacro{\expandafter\the\csname z@skip\endcsname} +% +\newskip\whatsitskip +\newcount\whatsitpenalty +% +% ..., ready, GO: +% +\def\safewhatsit#1{\ifhmode + #1% + \else + % \lastskip and \lastpenalty cannot both be nonzero simultaneously. + \whatsitskip = \lastskip + \edef\lastskipmacro{\the\lastskip}% + \whatsitpenalty = \lastpenalty + % + % If \lastskip is nonzero, that means the last item was a + % skip. And since a skip is discardable, that means this + % -\whatsitskip glue we're inserting is preceded by a + % non-discardable item, therefore it is not a potential + % breakpoint, therefore no \nobreak needed. + \ifx\lastskipmacro\zeroskipmacro + \else + \vskip-\whatsitskip + \fi + % + #1% + % + \ifx\lastskipmacro\zeroskipmacro + % If \lastskip was zero, perhaps the last item was a penalty, and + % perhaps it was >=10000, e.g., a \nobreak. In that case, we want + % to re-insert the same penalty (values >10000 are used for various + % signals); since we just inserted a non-discardable item, any + % following glue (such as a \parskip) would be a breakpoint. For example: + % @deffn deffn-whatever + % @vindex index-whatever + % Description. + % would allow a break between the index-whatever whatsit + % and the "Description." paragraph. + \ifnum\whatsitpenalty>9999 \penalty\whatsitpenalty \fi + \else + % On the other hand, if we had a nonzero \lastskip, + % this make-up glue would be preceded by a non-discardable item + % (the whatsit from the \write), so we must insert a \nobreak. + \nobreak\vskip\whatsitskip + \fi +\fi} + +% The index entry written in the file actually looks like +% \entry {sortstring}{page}{topic} +% or +% \entry {sortstring}{page}{topic}{subtopic} +% The texindex program reads in these files and writes files +% containing these kinds of lines: +% \initial {c} +% before the first topic whose initial is c +% \entry {topic}{pagelist} +% for a topic that is used without subtopics +% \primary {topic} +% for the beginning of a topic that is used with subtopics +% \secondary {subtopic}{pagelist} +% for each subtopic. + +% Define the user-accessible indexing commands +% @findex, @vindex, @kindex, @cindex. + +\def\findex {\fnindex} +\def\kindex {\kyindex} +\def\cindex {\cpindex} +\def\vindex {\vrindex} +\def\tindex {\tpindex} +\def\pindex {\pgindex} + +\def\cindexsub {\begingroup\obeylines\cindexsub} +{\obeylines % +\gdef\cindexsub "#1" #2^^M{\endgroup % +\dosubind{cp}{#2}{#1}}} + +% Define the macros used in formatting output of the sorted index material. + +% @printindex causes a particular index (the ??s file) to get printed. +% It does not print any chapter heading (usually an @unnumbered). +% +\parseargdef\printindex{\begingroup + \dobreak \chapheadingskip{10000}% + % + \smallfonts \rm + \tolerance = 9500 + \plainfrenchspacing + \everypar = {}% don't want the \kern\-parindent from indentation suppression. + % + % See if the index file exists and is nonempty. + % Change catcode of @ here so that if the index file contains + % \initial {@} + % as its first line, TeX doesn't complain about mismatched braces + % (because it thinks @} is a control sequence). + \catcode`\@ = 11 + \openin 1 \jobname.#1s + \ifeof 1 + % \enddoublecolumns gets confused if there is no text in the index, + % and it loses the chapter title and the aux file entries for the + % index. The easiest way to prevent this problem is to make sure + % there is some text. + \putwordIndexNonexistent + \else + % + % If the index file exists but is empty, then \openin leaves \ifeof + % false. We have to make TeX try to read something from the file, so + % it can discover if there is anything in it. + \read 1 to \temp + \ifeof 1 + \putwordIndexIsEmpty + \else + % Index files are almost Texinfo source, but we use \ as the escape + % character. It would be better to use @, but that's too big a change + % to make right now. + \def\indexbackslash{\backslashcurfont}% + \catcode`\\ = 0 + \escapechar = `\\ + \begindoublecolumns + \input \jobname.#1s + \enddoublecolumns + \fi + \fi + \closein 1 +\endgroup} + +% These macros are used by the sorted index file itself. +% Change them to control the appearance of the index. + +\def\initial#1{{% + % Some minor font changes for the special characters. + \let\tentt=\sectt \let\tt=\sectt \let\sf=\sectt + % + % Remove any glue we may have, we'll be inserting our own. + \removelastskip + % + % We like breaks before the index initials, so insert a bonus. + \nobreak + \vskip 0pt plus 3\baselineskip + \penalty 0 + \vskip 0pt plus -3\baselineskip + % + % Typeset the initial. Making this add up to a whole number of + % baselineskips increases the chance of the dots lining up from column + % to column. It still won't often be perfect, because of the stretch + % we need before each entry, but it's better. + % + % No shrink because it confuses \balancecolumns. + \vskip 1.67\baselineskip plus .5\baselineskip + \leftline{\secbf #1}% + % Do our best not to break after the initial. + \nobreak + \vskip .33\baselineskip plus .1\baselineskip +}} + +% \entry typesets a paragraph consisting of the text (#1), dot leaders, and +% then page number (#2) flushed to the right margin. It is used for index +% and table of contents entries. The paragraph is indented by \leftskip. +% +% A straightforward implementation would start like this: +% \def\entry#1#2{... +% But this freezes the catcodes in the argument, and can cause problems to +% @code, which sets - active. This problem was fixed by a kludge--- +% ``-'' was active throughout whole index, but this isn't really right. +% The right solution is to prevent \entry from swallowing the whole text. +% --kasal, 21nov03 +\def\entry{% + \begingroup + % + % Start a new paragraph if necessary, so our assignments below can't + % affect previous text. + \par + % + % Do not fill out the last line with white space. + \parfillskip = 0in + % + % No extra space above this paragraph. + \parskip = 0in + % + % Do not prefer a separate line ending with a hyphen to fewer lines. + \finalhyphendemerits = 0 + % + % \hangindent is only relevant when the entry text and page number + % don't both fit on one line. In that case, bob suggests starting the + % dots pretty far over on the line. Unfortunately, a large + % indentation looks wrong when the entry text itself is broken across + % lines. So we use a small indentation and put up with long leaders. + % + % \hangafter is reset to 1 (which is the value we want) at the start + % of each paragraph, so we need not do anything with that. + \hangindent = 2em + % + % When the entry text needs to be broken, just fill out the first line + % with blank space. + \rightskip = 0pt plus1fil + % + % A bit of stretch before each entry for the benefit of balancing + % columns. + \vskip 0pt plus1pt + % + % When reading the text of entry, convert explicit line breaks + % from @* into spaces. The user might give these in long section + % titles, for instance. + \def\*{\unskip\space\ignorespaces}% + \def\entrybreak{\hfil\break}% + % + % Swallow the left brace of the text (first parameter): + \afterassignment\doentry + \let\temp = +} +\def\entrybreak{\unskip\space\ignorespaces}% +\def\doentry{% + \bgroup % Instead of the swallowed brace. + \noindent + \aftergroup\finishentry + % And now comes the text of the entry. +} +\def\finishentry#1{% + % #1 is the page number. + % + % The following is kludged to not output a line of dots in the index if + % there are no page numbers. The next person who breaks this will be + % cursed by a Unix daemon. + \setbox\boxA = \hbox{#1}% + \ifdim\wd\boxA = 0pt + \ % + \else + % + % If we must, put the page number on a line of its own, and fill out + % this line with blank space. (The \hfil is overwhelmed with the + % fill leaders glue in \indexdotfill if the page number does fit.) + \hfil\penalty50 + \null\nobreak\indexdotfill % Have leaders before the page number. + % + % The `\ ' here is removed by the implicit \unskip that TeX does as + % part of (the primitive) \par. Without it, a spurious underfull + % \hbox ensues. + \ifpdf + \pdfgettoks#1.% + \ \the\toksA + \else + \ #1% + \fi + \fi + \par + \endgroup +} + +% Like plain.tex's \dotfill, except uses up at least 1 em. +\def\indexdotfill{\cleaders + \hbox{$\mathsurround=0pt \mkern1.5mu.\mkern1.5mu$}\hskip 1em plus 1fill} + +\def\primary #1{\line{#1\hfil}} + +\newskip\secondaryindent \secondaryindent=0.5cm +\def\secondary#1#2{{% + \parfillskip=0in + \parskip=0in + \hangindent=1in + \hangafter=1 + \noindent\hskip\secondaryindent\hbox{#1}\indexdotfill + \ifpdf + \pdfgettoks#2.\ \the\toksA % The page number ends the paragraph. + \else + #2 + \fi + \par +}} + +% Define two-column mode, which we use to typeset indexes. +% Adapted from the TeXbook, page 416, which is to say, +% the manmac.tex format used to print the TeXbook itself. +\catcode`\@=11 + +\newbox\partialpage +\newdimen\doublecolumnhsize + +\def\begindoublecolumns{\begingroup % ended by \enddoublecolumns + % Grab any single-column material above us. + \output = {% + % + % Here is a possibility not foreseen in manmac: if we accumulate a + % whole lot of material, we might end up calling this \output + % routine twice in a row (see the doublecol-lose test, which is + % essentially a couple of indexes with @setchapternewpage off). In + % that case we just ship out what is in \partialpage with the normal + % output routine. Generally, \partialpage will be empty when this + % runs and this will be a no-op. See the indexspread.tex test case. + \ifvoid\partialpage \else + \onepageout{\pagecontents\partialpage}% + \fi + % + \global\setbox\partialpage = \vbox{% + % Unvbox the main output page. + \unvbox\PAGE + \kern-\topskip \kern\baselineskip + }% + }% + \eject % run that output routine to set \partialpage + % + % Use the double-column output routine for subsequent pages. + \output = {\doublecolumnout}% + % + % Change the page size parameters. We could do this once outside this + % routine, in each of @smallbook, @afourpaper, and the default 8.5x11 + % format, but then we repeat the same computation. Repeating a couple + % of assignments once per index is clearly meaningless for the + % execution time, so we may as well do it in one place. + % + % First we halve the line length, less a little for the gutter between + % the columns. We compute the gutter based on the line length, so it + % changes automatically with the paper format. The magic constant + % below is chosen so that the gutter has the same value (well, +-<1pt) + % as it did when we hard-coded it. + % + % We put the result in a separate register, \doublecolumhsize, so we + % can restore it in \pagesofar, after \hsize itself has (potentially) + % been clobbered. + % + \doublecolumnhsize = \hsize + \advance\doublecolumnhsize by -.04154\hsize + \divide\doublecolumnhsize by 2 + \hsize = \doublecolumnhsize + % + % Double the \vsize as well. (We don't need a separate register here, + % since nobody clobbers \vsize.) + \vsize = 2\vsize +} + +% The double-column output routine for all double-column pages except +% the last. +% +\def\doublecolumnout{% + \splittopskip=\topskip \splitmaxdepth=\maxdepth + % Get the available space for the double columns -- the normal + % (undoubled) page height minus any material left over from the + % previous page. + \dimen@ = \vsize + \divide\dimen@ by 2 + \advance\dimen@ by -\ht\partialpage + % + % box0 will be the left-hand column, box2 the right. + \setbox0=\vsplit255 to\dimen@ \setbox2=\vsplit255 to\dimen@ + \onepageout\pagesofar + \unvbox255 + \penalty\outputpenalty +} +% +% Re-output the contents of the output page -- any previous material, +% followed by the two boxes we just split, in box0 and box2. +\def\pagesofar{% + \unvbox\partialpage + % + \hsize = \doublecolumnhsize + \wd0=\hsize \wd2=\hsize + \hbox to\pagewidth{\box0\hfil\box2}% +} +% +% All done with double columns. +\def\enddoublecolumns{% + % The following penalty ensures that the page builder is exercised + % _before_ we change the output routine. This is necessary in the + % following situation: + % + % The last section of the index consists only of a single entry. + % Before this section, \pagetotal is less than \pagegoal, so no + % break occurs before the last section starts. However, the last + % section, consisting of \initial and the single \entry, does not + % fit on the page and has to be broken off. Without the following + % penalty the page builder will not be exercised until \eject + % below, and by that time we'll already have changed the output + % routine to the \balancecolumns version, so the next-to-last + % double-column page will be processed with \balancecolumns, which + % is wrong: The two columns will go to the main vertical list, with + % the broken-off section in the recent contributions. As soon as + % the output routine finishes, TeX starts reconsidering the page + % break. The two columns and the broken-off section both fit on the + % page, because the two columns now take up only half of the page + % goal. When TeX sees \eject from below which follows the final + % section, it invokes the new output routine that we've set after + % \balancecolumns below; \onepageout will try to fit the two columns + % and the final section into the vbox of \pageheight (see + % \pagebody), causing an overfull box. + % + % Note that glue won't work here, because glue does not exercise the + % page builder, unlike penalties (see The TeXbook, pp. 280-281). + \penalty0 + % + \output = {% + % Split the last of the double-column material. Leave it on the + % current page, no automatic page break. + \balancecolumns + % + % If we end up splitting too much material for the current page, + % though, there will be another page break right after this \output + % invocation ends. Having called \balancecolumns once, we do not + % want to call it again. Therefore, reset \output to its normal + % definition right away. (We hope \balancecolumns will never be + % called on to balance too much material, but if it is, this makes + % the output somewhat more palatable.) + \global\output = {\onepageout{\pagecontents\PAGE}}% + }% + \eject + \endgroup % started in \begindoublecolumns + % + % \pagegoal was set to the doubled \vsize above, since we restarted + % the current page. We're now back to normal single-column + % typesetting, so reset \pagegoal to the normal \vsize (after the + % \endgroup where \vsize got restored). + \pagegoal = \vsize +} +% +% Called at the end of the double column material. +\def\balancecolumns{% + \setbox0 = \vbox{\unvbox255}% like \box255 but more efficient, see p.120. + \dimen@ = \ht0 + \advance\dimen@ by \topskip + \advance\dimen@ by-\baselineskip + \divide\dimen@ by 2 % target to split to + %debug\message{final 2-column material height=\the\ht0, target=\the\dimen@.}% + \splittopskip = \topskip + % Loop until we get a decent breakpoint. + {% + \vbadness = 10000 + \loop + \global\setbox3 = \copy0 + \global\setbox1 = \vsplit3 to \dimen@ + \ifdim\ht3>\dimen@ + \global\advance\dimen@ by 1pt + \repeat + }% + %debug\message{split to \the\dimen@, column heights: \the\ht1, \the\ht3.}% + \setbox0=\vbox to\dimen@{\unvbox1}% + \setbox2=\vbox to\dimen@{\unvbox3}% + % + \pagesofar +} +\catcode`\@ = \other + + +\message{sectioning,} +% Chapters, sections, etc. + +% Let's start with @part. +\outer\parseargdef\part{\partzzz{#1}} +\def\partzzz#1{% + \chapoddpage + \null + \vskip.3\vsize % move it down on the page a bit + \begingroup + \noindent \titlefonts\rmisbold #1\par % the text + \let\lastnode=\empty % no node to associate with + \writetocentry{part}{#1}{}% but put it in the toc + \headingsoff % no headline or footline on the part page + \chapoddpage + \endgroup +} + +% \unnumberedno is an oxymoron. But we count the unnumbered +% sections so that we can refer to them unambiguously in the pdf +% outlines by their "section number". We avoid collisions with chapter +% numbers by starting them at 10000. (If a document ever has 10000 +% chapters, we're in trouble anyway, I'm sure.) +\newcount\unnumberedno \unnumberedno = 10000 +\newcount\chapno +\newcount\secno \secno=0 +\newcount\subsecno \subsecno=0 +\newcount\subsubsecno \subsubsecno=0 + +% This counter is funny since it counts through charcodes of letters A, B, ... +\newcount\appendixno \appendixno = `\@ +% +% \def\appendixletter{\char\the\appendixno} +% We do the following ugly conditional instead of the above simple +% construct for the sake of pdftex, which needs the actual +% letter in the expansion, not just typeset. +% +\def\appendixletter{% + \ifnum\appendixno=`A A% + \else\ifnum\appendixno=`B B% + \else\ifnum\appendixno=`C C% + \else\ifnum\appendixno=`D D% + \else\ifnum\appendixno=`E E% + \else\ifnum\appendixno=`F F% + \else\ifnum\appendixno=`G G% + \else\ifnum\appendixno=`H H% + \else\ifnum\appendixno=`I I% + \else\ifnum\appendixno=`J J% + \else\ifnum\appendixno=`K K% + \else\ifnum\appendixno=`L L% + \else\ifnum\appendixno=`M M% + \else\ifnum\appendixno=`N N% + \else\ifnum\appendixno=`O O% + \else\ifnum\appendixno=`P P% + \else\ifnum\appendixno=`Q Q% + \else\ifnum\appendixno=`R R% + \else\ifnum\appendixno=`S S% + \else\ifnum\appendixno=`T T% + \else\ifnum\appendixno=`U U% + \else\ifnum\appendixno=`V V% + \else\ifnum\appendixno=`W W% + \else\ifnum\appendixno=`X X% + \else\ifnum\appendixno=`Y Y% + \else\ifnum\appendixno=`Z Z% + % The \the is necessary, despite appearances, because \appendixletter is + % expanded while writing the .toc file. \char\appendixno is not + % expandable, thus it is written literally, thus all appendixes come out + % with the same letter (or @) in the toc without it. + \else\char\the\appendixno + \fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi + \fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi} + +% Each @chapter defines these (using marks) as the number+name, number +% and name of the chapter. Page headings and footings can use +% these. @section does likewise. +\def\thischapter{} +\def\thischapternum{} +\def\thischaptername{} +\def\thissection{} +\def\thissectionnum{} +\def\thissectionname{} + +\newcount\absseclevel % used to calculate proper heading level +\newcount\secbase\secbase=0 % @raisesections/@lowersections modify this count + +% @raisesections: treat @section as chapter, @subsection as section, etc. +\def\raisesections{\global\advance\secbase by -1} +\let\up=\raisesections % original BFox name + +% @lowersections: treat @chapter as section, @section as subsection, etc. +\def\lowersections{\global\advance\secbase by 1} +\let\down=\lowersections % original BFox name + +% we only have subsub. +\chardef\maxseclevel = 3 +% +% A numbered section within an unnumbered changes to unnumbered too. +% To achieve this, remember the "biggest" unnum. sec. we are currently in: +\chardef\unnlevel = \maxseclevel +% +% Trace whether the current chapter is an appendix or not: +% \chapheadtype is "N" or "A", unnumbered chapters are ignored. +\def\chapheadtype{N} + +% Choose a heading macro +% #1 is heading type +% #2 is heading level +% #3 is text for heading +\def\genhead#1#2#3{% + % Compute the abs. sec. level: + \absseclevel=#2 + \advance\absseclevel by \secbase + % Make sure \absseclevel doesn't fall outside the range: + \ifnum \absseclevel < 0 + \absseclevel = 0 + \else + \ifnum \absseclevel > 3 + \absseclevel = 3 + \fi + \fi + % The heading type: + \def\headtype{#1}% + \if \headtype U% + \ifnum \absseclevel < \unnlevel + \chardef\unnlevel = \absseclevel + \fi + \else + % Check for appendix sections: + \ifnum \absseclevel = 0 + \edef\chapheadtype{\headtype}% + \else + \if \headtype A\if \chapheadtype N% + \errmessage{@appendix... within a non-appendix chapter}% + \fi\fi + \fi + % Check for numbered within unnumbered: + \ifnum \absseclevel > \unnlevel + \def\headtype{U}% + \else + \chardef\unnlevel = 3 + \fi + \fi + % Now print the heading: + \if \headtype U% + \ifcase\absseclevel + \unnumberedzzz{#3}% + \or \unnumberedseczzz{#3}% + \or \unnumberedsubseczzz{#3}% + \or \unnumberedsubsubseczzz{#3}% + \fi + \else + \if \headtype A% + \ifcase\absseclevel + \appendixzzz{#3}% + \or \appendixsectionzzz{#3}% + \or \appendixsubseczzz{#3}% + \or \appendixsubsubseczzz{#3}% + \fi + \else + \ifcase\absseclevel + \chapterzzz{#3}% + \or \seczzz{#3}% + \or \numberedsubseczzz{#3}% + \or \numberedsubsubseczzz{#3}% + \fi + \fi + \fi + \suppressfirstparagraphindent +} + +% an interface: +\def\numhead{\genhead N} +\def\apphead{\genhead A} +\def\unnmhead{\genhead U} + +% @chapter, @appendix, @unnumbered. Increment top-level counter, reset +% all lower-level sectioning counters to zero. +% +% Also set \chaplevelprefix, which we prepend to @float sequence numbers +% (e.g., figures), q.v. By default (before any chapter), that is empty. +\let\chaplevelprefix = \empty +% +\outer\parseargdef\chapter{\numhead0{#1}} % normally numhead0 calls chapterzzz +\def\chapterzzz#1{% + % section resetting is \global in case the chapter is in a group, such + % as an @include file. + \global\secno=0 \global\subsecno=0 \global\subsubsecno=0 + \global\advance\chapno by 1 + % + % Used for \float. + \gdef\chaplevelprefix{\the\chapno.}% + \resetallfloatnos + % + % \putwordChapter can contain complex things in translations. + \toks0=\expandafter{\putwordChapter}% + \message{\the\toks0 \space \the\chapno}% + % + % Write the actual heading. + \chapmacro{#1}{Ynumbered}{\the\chapno}% + % + % So @section and the like are numbered underneath this chapter. + \global\let\section = \numberedsec + \global\let\subsection = \numberedsubsec + \global\let\subsubsection = \numberedsubsubsec +} + +\outer\parseargdef\appendix{\apphead0{#1}} % normally calls appendixzzz +% +\def\appendixzzz#1{% + \global\secno=0 \global\subsecno=0 \global\subsubsecno=0 + \global\advance\appendixno by 1 + \gdef\chaplevelprefix{\appendixletter.}% + \resetallfloatnos + % + % \putwordAppendix can contain complex things in translations. + \toks0=\expandafter{\putwordAppendix}% + \message{\the\toks0 \space \appendixletter}% + % + \chapmacro{#1}{Yappendix}{\appendixletter}% + % + \global\let\section = \appendixsec + \global\let\subsection = \appendixsubsec + \global\let\subsubsection = \appendixsubsubsec +} + +% normally unnmhead0 calls unnumberedzzz: +\outer\parseargdef\unnumbered{\unnmhead0{#1}} +\def\unnumberedzzz#1{% + \global\secno=0 \global\subsecno=0 \global\subsubsecno=0 + \global\advance\unnumberedno by 1 + % + % Since an unnumbered has no number, no prefix for figures. + \global\let\chaplevelprefix = \empty + \resetallfloatnos + % + % This used to be simply \message{#1}, but TeX fully expands the + % argument to \message. Therefore, if #1 contained @-commands, TeX + % expanded them. For example, in `@unnumbered The @cite{Book}', TeX + % expanded @cite (which turns out to cause errors because \cite is meant + % to be executed, not expanded). + % + % Anyway, we don't want the fully-expanded definition of @cite to appear + % as a result of the \message, we just want `@cite' itself. We use + % \the to achieve this: TeX expands \the only once, + % simply yielding the contents of . (We also do this for + % the toc entries.) + \toks0 = {#1}% + \message{(\the\toks0)}% + % + \chapmacro{#1}{Ynothing}{\the\unnumberedno}% + % + \global\let\section = \unnumberedsec + \global\let\subsection = \unnumberedsubsec + \global\let\subsubsection = \unnumberedsubsubsec +} + +% @centerchap is like @unnumbered, but the heading is centered. +\outer\parseargdef\centerchap{% + % Well, we could do the following in a group, but that would break + % an assumption that \chapmacro is called at the outermost level. + % Thus we are safer this way: --kasal, 24feb04 + \let\centerparametersmaybe = \centerparameters + \unnmhead0{#1}% + \let\centerparametersmaybe = \relax +} + +% @top is like @unnumbered. +\let\top\unnumbered + +% Sections. +% +\outer\parseargdef\numberedsec{\numhead1{#1}} % normally calls seczzz +\def\seczzz#1{% + \global\subsecno=0 \global\subsubsecno=0 \global\advance\secno by 1 + \sectionheading{#1}{sec}{Ynumbered}{\the\chapno.\the\secno}% +} + +% normally calls appendixsectionzzz: +\outer\parseargdef\appendixsection{\apphead1{#1}} +\def\appendixsectionzzz#1{% + \global\subsecno=0 \global\subsubsecno=0 \global\advance\secno by 1 + \sectionheading{#1}{sec}{Yappendix}{\appendixletter.\the\secno}% +} +\let\appendixsec\appendixsection + +% normally calls unnumberedseczzz: +\outer\parseargdef\unnumberedsec{\unnmhead1{#1}} +\def\unnumberedseczzz#1{% + \global\subsecno=0 \global\subsubsecno=0 \global\advance\secno by 1 + \sectionheading{#1}{sec}{Ynothing}{\the\unnumberedno.\the\secno}% +} + +% Subsections. +% +% normally calls numberedsubseczzz: +\outer\parseargdef\numberedsubsec{\numhead2{#1}} +\def\numberedsubseczzz#1{% + \global\subsubsecno=0 \global\advance\subsecno by 1 + \sectionheading{#1}{subsec}{Ynumbered}{\the\chapno.\the\secno.\the\subsecno}% +} + +% normally calls appendixsubseczzz: +\outer\parseargdef\appendixsubsec{\apphead2{#1}} +\def\appendixsubseczzz#1{% + \global\subsubsecno=0 \global\advance\subsecno by 1 + \sectionheading{#1}{subsec}{Yappendix}% + {\appendixletter.\the\secno.\the\subsecno}% +} + +% normally calls unnumberedsubseczzz: +\outer\parseargdef\unnumberedsubsec{\unnmhead2{#1}} +\def\unnumberedsubseczzz#1{% + \global\subsubsecno=0 \global\advance\subsecno by 1 + \sectionheading{#1}{subsec}{Ynothing}% + {\the\unnumberedno.\the\secno.\the\subsecno}% +} + +% Subsubsections. +% +% normally numberedsubsubseczzz: +\outer\parseargdef\numberedsubsubsec{\numhead3{#1}} +\def\numberedsubsubseczzz#1{% + \global\advance\subsubsecno by 1 + \sectionheading{#1}{subsubsec}{Ynumbered}% + {\the\chapno.\the\secno.\the\subsecno.\the\subsubsecno}% +} + +% normally appendixsubsubseczzz: +\outer\parseargdef\appendixsubsubsec{\apphead3{#1}} +\def\appendixsubsubseczzz#1{% + \global\advance\subsubsecno by 1 + \sectionheading{#1}{subsubsec}{Yappendix}% + {\appendixletter.\the\secno.\the\subsecno.\the\subsubsecno}% +} + +% normally unnumberedsubsubseczzz: +\outer\parseargdef\unnumberedsubsubsec{\unnmhead3{#1}} +\def\unnumberedsubsubseczzz#1{% + \global\advance\subsubsecno by 1 + \sectionheading{#1}{subsubsec}{Ynothing}% + {\the\unnumberedno.\the\secno.\the\subsecno.\the\subsubsecno}% +} + +% These macros control what the section commands do, according +% to what kind of chapter we are in (ordinary, appendix, or unnumbered). +% Define them by default for a numbered chapter. +\let\section = \numberedsec +\let\subsection = \numberedsubsec +\let\subsubsection = \numberedsubsubsec + +% Define @majorheading, @heading and @subheading + +% NOTE on use of \vbox for chapter headings, section headings, and such: +% 1) We use \vbox rather than the earlier \line to permit +% overlong headings to fold. +% 2) \hyphenpenalty is set to 10000 because hyphenation in a +% heading is obnoxious; this forbids it. +% 3) Likewise, headings look best if no \parindent is used, and +% if justification is not attempted. Hence \raggedright. + +\def\majorheading{% + {\advance\chapheadingskip by 10pt \chapbreak }% + \parsearg\chapheadingzzz +} + +\def\chapheading{\chapbreak \parsearg\chapheadingzzz} +\def\chapheadingzzz#1{% + {\chapfonts \vbox{\hyphenpenalty=10000\tolerance=5000 + \parindent=0pt\ptexraggedright + \rmisbold #1\hfill}}% + \bigskip \par\penalty 200\relax + \suppressfirstparagraphindent +} + +% @heading, @subheading, @subsubheading. +\parseargdef\heading{\sectionheading{#1}{sec}{Yomitfromtoc}{} + \suppressfirstparagraphindent} +\parseargdef\subheading{\sectionheading{#1}{subsec}{Yomitfromtoc}{} + \suppressfirstparagraphindent} +\parseargdef\subsubheading{\sectionheading{#1}{subsubsec}{Yomitfromtoc}{} + \suppressfirstparagraphindent} + +% These macros generate a chapter, section, etc. heading only +% (including whitespace, linebreaking, etc. around it), +% given all the information in convenient, parsed form. + +% Args are the skip and penalty (usually negative) +\def\dobreak#1#2{\par\ifdim\lastskip<#1\removelastskip\penalty#2\vskip#1\fi} + +% Parameter controlling skip before chapter headings (if needed) +\newskip\chapheadingskip + +% Define plain chapter starts, and page on/off switching for it. +\def\chapbreak{\dobreak \chapheadingskip {-4000}} +\def\chappager{\par\vfill\supereject} +% Because \domark is called before \chapoddpage, the filler page will +% get the headings for the next chapter, which is wrong. But we don't +% care -- we just disable all headings on the filler page. +\def\chapoddpage{% + \chappager + \ifodd\pageno \else + \begingroup + \headingsoff + \null + \chappager + \endgroup + \fi +} + +\def\setchapternewpage #1 {\csname CHAPPAG#1\endcsname} + +\def\CHAPPAGoff{% +\global\let\contentsalignmacro = \chappager +\global\let\pchapsepmacro=\chapbreak +\global\let\pagealignmacro=\chappager} + +\def\CHAPPAGon{% +\global\let\contentsalignmacro = \chappager +\global\let\pchapsepmacro=\chappager +\global\let\pagealignmacro=\chappager +\global\def\HEADINGSon{\HEADINGSsingle}} + +\def\CHAPPAGodd{% +\global\let\contentsalignmacro = \chapoddpage +\global\let\pchapsepmacro=\chapoddpage +\global\let\pagealignmacro=\chapoddpage +\global\def\HEADINGSon{\HEADINGSdouble}} + +\CHAPPAGon + +% Chapter opening. +% +% #1 is the text, #2 is the section type (Ynumbered, Ynothing, +% Yappendix, Yomitfromtoc), #3 the chapter number. +% +% To test against our argument. +\def\Ynothingkeyword{Ynothing} +\def\Yomitfromtockeyword{Yomitfromtoc} +\def\Yappendixkeyword{Yappendix} +% +\def\chapmacro#1#2#3{% + % Insert the first mark before the heading break (see notes for \domark). + \let\prevchapterdefs=\lastchapterdefs + \let\prevsectiondefs=\lastsectiondefs + \gdef\lastsectiondefs{\gdef\thissectionname{}\gdef\thissectionnum{}% + \gdef\thissection{}}% + % + \def\temptype{#2}% + \ifx\temptype\Ynothingkeyword + \gdef\lastchapterdefs{\gdef\thischaptername{#1}\gdef\thischapternum{}% + \gdef\thischapter{\thischaptername}}% + \else\ifx\temptype\Yomitfromtockeyword + \gdef\lastchapterdefs{\gdef\thischaptername{#1}\gdef\thischapternum{}% + \gdef\thischapter{}}% + \else\ifx\temptype\Yappendixkeyword + \toks0={#1}% + \xdef\lastchapterdefs{% + \gdef\noexpand\thischaptername{\the\toks0}% + \gdef\noexpand\thischapternum{\appendixletter}% + % \noexpand\putwordAppendix avoids expanding indigestible + % commands in some of the translations. + \gdef\noexpand\thischapter{\noexpand\putwordAppendix{} + \noexpand\thischapternum: + \noexpand\thischaptername}% + }% + \else + \toks0={#1}% + \xdef\lastchapterdefs{% + \gdef\noexpand\thischaptername{\the\toks0}% + \gdef\noexpand\thischapternum{\the\chapno}% + % \noexpand\putwordChapter avoids expanding indigestible + % commands in some of the translations. + \gdef\noexpand\thischapter{\noexpand\putwordChapter{} + \noexpand\thischapternum: + \noexpand\thischaptername}% + }% + \fi\fi\fi + % + % Output the mark. Pass it through \safewhatsit, to take care of + % the preceding space. + \safewhatsit\domark + % + % Insert the chapter heading break. + \pchapsepmacro + % + % Now the second mark, after the heading break. No break points + % between here and the heading. + \let\prevchapterdefs=\lastchapterdefs + \let\prevsectiondefs=\lastsectiondefs + \domark + % + {% + \chapfonts \rmisbold + % + % Have to define \lastsection before calling \donoderef, because the + % xref code eventually uses it. On the other hand, it has to be called + % after \pchapsepmacro, or the headline will change too soon. + \gdef\lastsection{#1}% + % + % Only insert the separating space if we have a chapter/appendix + % number, and don't print the unnumbered ``number''. + \ifx\temptype\Ynothingkeyword + \setbox0 = \hbox{}% + \def\toctype{unnchap}% + \else\ifx\temptype\Yomitfromtockeyword + \setbox0 = \hbox{}% contents like unnumbered, but no toc entry + \def\toctype{omit}% + \else\ifx\temptype\Yappendixkeyword + \setbox0 = \hbox{\putwordAppendix{} #3\enspace}% + \def\toctype{app}% + \else + \setbox0 = \hbox{#3\enspace}% + \def\toctype{numchap}% + \fi\fi\fi + % + % Write the toc entry for this chapter. Must come before the + % \donoderef, because we include the current node name in the toc + % entry, and \donoderef resets it to empty. + \writetocentry{\toctype}{#1}{#3}% + % + % For pdftex, we have to write out the node definition (aka, make + % the pdfdest) after any page break, but before the actual text has + % been typeset. If the destination for the pdf outline is after the + % text, then jumping from the outline may wind up with the text not + % being visible, for instance under high magnification. + \donoderef{#2}% + % + % Typeset the actual heading. + \nobreak % Avoid page breaks at the interline glue. + \vbox{\hyphenpenalty=10000 \tolerance=5000 \parindent=0pt \ptexraggedright + \hangindent=\wd0 \centerparametersmaybe + \unhbox0 #1\par}% + }% + \nobreak\bigskip % no page break after a chapter title + \nobreak +} + +% @centerchap -- centered and unnumbered. +\let\centerparametersmaybe = \relax +\def\centerparameters{% + \advance\rightskip by 3\rightskip + \leftskip = \rightskip + \parfillskip = 0pt +} + + +% I don't think this chapter style is supported any more, so I'm not +% updating it with the new noderef stuff. We'll see. --karl, 11aug03. +% +\def\setchapterstyle #1 {\csname CHAPF#1\endcsname} +% +\def\unnchfopen #1{% +\chapoddpage {\chapfonts \vbox{\hyphenpenalty=10000\tolerance=5000 + \parindent=0pt\ptexraggedright + \rmisbold #1\hfill}}\bigskip \par\nobreak +} +\def\chfopen #1#2{\chapoddpage {\chapfonts +\vbox to 3in{\vfil \hbox to\hsize{\hfil #2} \hbox to\hsize{\hfil #1} \vfil}}% +\par\penalty 5000 % +} +\def\centerchfopen #1{% +\chapoddpage {\chapfonts \vbox{\hyphenpenalty=10000\tolerance=5000 + \parindent=0pt + \hfill {\rmisbold #1}\hfill}}\bigskip \par\nobreak +} +\def\CHAPFopen{% + \global\let\chapmacro=\chfopen + \global\let\centerchapmacro=\centerchfopen} + + +% Section titles. These macros combine the section number parts and +% call the generic \sectionheading to do the printing. +% +\newskip\secheadingskip +\def\secheadingbreak{\dobreak \secheadingskip{-1000}} + +% Subsection titles. +\newskip\subsecheadingskip +\def\subsecheadingbreak{\dobreak \subsecheadingskip{-500}} + +% Subsubsection titles. +\def\subsubsecheadingskip{\subsecheadingskip} +\def\subsubsecheadingbreak{\subsecheadingbreak} + + +% Print any size, any type, section title. +% +% #1 is the text, #2 is the section level (sec/subsec/subsubsec), #3 is +% the section type for xrefs (Ynumbered, Ynothing, Yappendix), #4 is the +% section number. +% +\def\seckeyword{sec} +% +\def\sectionheading#1#2#3#4{% + {% + \checkenv{}% should not be in an environment. + % + % Switch to the right set of fonts. + \csname #2fonts\endcsname \rmisbold + % + \def\sectionlevel{#2}% + \def\temptype{#3}% + % + % Insert first mark before the heading break (see notes for \domark). + \let\prevsectiondefs=\lastsectiondefs + \ifx\temptype\Ynothingkeyword + \ifx\sectionlevel\seckeyword + \gdef\lastsectiondefs{\gdef\thissectionname{#1}\gdef\thissectionnum{}% + \gdef\thissection{\thissectionname}}% + \fi + \else\ifx\temptype\Yomitfromtockeyword + % Don't redefine \thissection. + \else\ifx\temptype\Yappendixkeyword + \ifx\sectionlevel\seckeyword + \toks0={#1}% + \xdef\lastsectiondefs{% + \gdef\noexpand\thissectionname{\the\toks0}% + \gdef\noexpand\thissectionnum{#4}% + % \noexpand\putwordSection avoids expanding indigestible + % commands in some of the translations. + \gdef\noexpand\thissection{\noexpand\putwordSection{} + \noexpand\thissectionnum: + \noexpand\thissectionname}% + }% + \fi + \else + \ifx\sectionlevel\seckeyword + \toks0={#1}% + \xdef\lastsectiondefs{% + \gdef\noexpand\thissectionname{\the\toks0}% + \gdef\noexpand\thissectionnum{#4}% + % \noexpand\putwordSection avoids expanding indigestible + % commands in some of the translations. + \gdef\noexpand\thissection{\noexpand\putwordSection{} + \noexpand\thissectionnum: + \noexpand\thissectionname}% + }% + \fi + \fi\fi\fi + % + % Go into vertical mode. Usually we'll already be there, but we + % don't want the following whatsit to end up in a preceding paragraph + % if the document didn't happen to have a blank line. + \par + % + % Output the mark. Pass it through \safewhatsit, to take care of + % the preceding space. + \safewhatsit\domark + % + % Insert space above the heading. + \csname #2headingbreak\endcsname + % + % Now the second mark, after the heading break. No break points + % between here and the heading. + \let\prevsectiondefs=\lastsectiondefs + \domark + % + % Only insert the space after the number if we have a section number. + \ifx\temptype\Ynothingkeyword + \setbox0 = \hbox{}% + \def\toctype{unn}% + \gdef\lastsection{#1}% + \else\ifx\temptype\Yomitfromtockeyword + % for @headings -- no section number, don't include in toc, + % and don't redefine \lastsection. + \setbox0 = \hbox{}% + \def\toctype{omit}% + \let\sectionlevel=\empty + \else\ifx\temptype\Yappendixkeyword + \setbox0 = \hbox{#4\enspace}% + \def\toctype{app}% + \gdef\lastsection{#1}% + \else + \setbox0 = \hbox{#4\enspace}% + \def\toctype{num}% + \gdef\lastsection{#1}% + \fi\fi\fi + % + % Write the toc entry (before \donoderef). See comments in \chapmacro. + \writetocentry{\toctype\sectionlevel}{#1}{#4}% + % + % Write the node reference (= pdf destination for pdftex). + % Again, see comments in \chapmacro. + \donoderef{#3}% + % + % Interline glue will be inserted when the vbox is completed. + % That glue will be a valid breakpoint for the page, since it'll be + % preceded by a whatsit (usually from the \donoderef, or from the + % \writetocentry if there was no node). We don't want to allow that + % break, since then the whatsits could end up on page n while the + % section is on page n+1, thus toc/etc. are wrong. Debian bug 276000. + \nobreak + % + % Output the actual section heading. + \vbox{\hyphenpenalty=10000 \tolerance=5000 \parindent=0pt \ptexraggedright + \hangindent=\wd0 % zero if no section number + \unhbox0 #1}% + }% + % Add extra space after the heading -- half of whatever came above it. + % Don't allow stretch, though. + \kern .5 \csname #2headingskip\endcsname + % + % Do not let the kern be a potential breakpoint, as it would be if it + % was followed by glue. + \nobreak + % + % We'll almost certainly start a paragraph next, so don't let that + % glue accumulate. (Not a breakpoint because it's preceded by a + % discardable item.) However, when a paragraph is not started next + % (\startdefun, \cartouche, \center, etc.), this needs to be wiped out + % or the negative glue will cause weirdly wrong output, typically + % obscuring the section heading with something else. + \vskip-\parskip + % + % This is so the last item on the main vertical list is a known + % \penalty > 10000, so \startdefun, etc., can recognize the situation + % and do the needful. + \penalty 10001 +} + + +\message{toc,} +% Table of contents. +\newwrite\tocfile + +% Write an entry to the toc file, opening it if necessary. +% Called from @chapter, etc. +% +% Example usage: \writetocentry{sec}{Section Name}{\the\chapno.\the\secno} +% We append the current node name (if any) and page number as additional +% arguments for the \{chap,sec,...}entry macros which will eventually +% read this. The node name is used in the pdf outlines as the +% destination to jump to. +% +% We open the .toc file for writing here instead of at @setfilename (or +% any other fixed time) so that @contents can be anywhere in the document. +% But if #1 is `omit', then we don't do anything. This is used for the +% table of contents chapter openings themselves. +% +\newif\iftocfileopened +\def\omitkeyword{omit}% +% +\def\writetocentry#1#2#3{% + \edef\writetoctype{#1}% + \ifx\writetoctype\omitkeyword \else + \iftocfileopened\else + \immediate\openout\tocfile = \jobname.toc + \global\tocfileopenedtrue + \fi + % + \iflinks + {\atdummies + \edef\temp{% + \write\tocfile{@#1entry{#2}{#3}{\lastnode}{\noexpand\folio}}}% + \temp + }% + \fi + \fi + % + % Tell \shipout to create a pdf destination on each page, if we're + % writing pdf. These are used in the table of contents. We can't + % just write one on every page because the title pages are numbered + % 1 and 2 (the page numbers aren't printed), and so are the first + % two pages of the document. Thus, we'd have two destinations named + % `1', and two named `2'. + \ifpdf \global\pdfmakepagedesttrue \fi +} + + +% These characters do not print properly in the Computer Modern roman +% fonts, so we must take special care. This is more or less redundant +% with the Texinfo input format setup at the end of this file. +% +\def\activecatcodes{% + \catcode`\"=\active + \catcode`\$=\active + \catcode`\<=\active + \catcode`\>=\active + \catcode`\\=\active + \catcode`\^=\active + \catcode`\_=\active + \catcode`\|=\active + \catcode`\~=\active +} + + +% Read the toc file, which is essentially Texinfo input. +\def\readtocfile{% + \setupdatafile + \activecatcodes + \input \tocreadfilename +} + +\newskip\contentsrightmargin \contentsrightmargin=1in +\newcount\savepageno +\newcount\lastnegativepageno \lastnegativepageno = -1 + +% Prepare to read what we've written to \tocfile. +% +\def\startcontents#1{% + % If @setchapternewpage on, and @headings double, the contents should + % start on an odd page, unlike chapters. Thus, we maintain + % \contentsalignmacro in parallel with \pagealignmacro. + % From: Torbjorn Granlund + \contentsalignmacro + \immediate\closeout\tocfile + % + % Don't need to put `Contents' or `Short Contents' in the headline. + % It is abundantly clear what they are. + \chapmacro{#1}{Yomitfromtoc}{}% + % + \savepageno = \pageno + \begingroup % Set up to handle contents files properly. + \raggedbottom % Worry more about breakpoints than the bottom. + \advance\hsize by -\contentsrightmargin % Don't use the full line length. + % + % Roman numerals for page numbers. + \ifnum \pageno>0 \global\pageno = \lastnegativepageno \fi +} + +% redefined for the two-volume lispref. We always output on +% \jobname.toc even if this is redefined. +% +\def\tocreadfilename{\jobname.toc} + +% Normal (long) toc. +% +\def\contents{% + \startcontents{\putwordTOC}% + \openin 1 \tocreadfilename\space + \ifeof 1 \else + \readtocfile + \fi + \vfill \eject + \contentsalignmacro % in case @setchapternewpage odd is in effect + \ifeof 1 \else + \pdfmakeoutlines + \fi + \closein 1 + \endgroup + \lastnegativepageno = \pageno + \global\pageno = \savepageno +} + +% And just the chapters. +\def\summarycontents{% + \startcontents{\putwordShortTOC}% + % + \let\partentry = \shortpartentry + \let\numchapentry = \shortchapentry + \let\appentry = \shortchapentry + \let\unnchapentry = \shortunnchapentry + % We want a true roman here for the page numbers. + \secfonts + \let\rm=\shortcontrm \let\bf=\shortcontbf + \let\sl=\shortcontsl \let\tt=\shortconttt + \rm + \hyphenpenalty = 10000 + \advance\baselineskip by 1pt % Open it up a little. + \def\numsecentry##1##2##3##4{} + \let\appsecentry = \numsecentry + \let\unnsecentry = \numsecentry + \let\numsubsecentry = \numsecentry + \let\appsubsecentry = \numsecentry + \let\unnsubsecentry = \numsecentry + \let\numsubsubsecentry = \numsecentry + \let\appsubsubsecentry = \numsecentry + \let\unnsubsubsecentry = \numsecentry + \openin 1 \tocreadfilename\space + \ifeof 1 \else + \readtocfile + \fi + \closein 1 + \vfill \eject + \contentsalignmacro % in case @setchapternewpage odd is in effect + \endgroup + \lastnegativepageno = \pageno + \global\pageno = \savepageno +} +\let\shortcontents = \summarycontents + +% Typeset the label for a chapter or appendix for the short contents. +% The arg is, e.g., `A' for an appendix, or `3' for a chapter. +% +\def\shortchaplabel#1{% + % This space should be enough, since a single number is .5em, and the + % widest letter (M) is 1em, at least in the Computer Modern fonts. + % But use \hss just in case. + % (This space doesn't include the extra space that gets added after + % the label; that gets put in by \shortchapentry above.) + % + % We'd like to right-justify chapter numbers, but that looks strange + % with appendix letters. And right-justifying numbers and + % left-justifying letters looks strange when there is less than 10 + % chapters. Have to read the whole toc once to know how many chapters + % there are before deciding ... + \hbox to 1em{#1\hss}% +} + +% These macros generate individual entries in the table of contents. +% The first argument is the chapter or section name. +% The last argument is the page number. +% The arguments in between are the chapter number, section number, ... + +% Parts, in the main contents. Replace the part number, which doesn't +% exist, with an empty box. Let's hope all the numbers have the same width. +% Also ignore the page number, which is conventionally not printed. +\def\numeralbox{\setbox0=\hbox{8}\hbox to \wd0{\hfil}} +\def\partentry#1#2#3#4{\dochapentry{\numeralbox\labelspace#1}{}} +% +% Parts, in the short toc. +\def\shortpartentry#1#2#3#4{% + \penalty-300 + \vskip.5\baselineskip plus.15\baselineskip minus.1\baselineskip + \shortchapentry{{\bf #1}}{\numeralbox}{}{}% +} + +% Chapters, in the main contents. +\def\numchapentry#1#2#3#4{\dochapentry{#2\labelspace#1}{#4}} +% +% Chapters, in the short toc. +% See comments in \dochapentry re vbox and related settings. +\def\shortchapentry#1#2#3#4{% + \tocentry{\shortchaplabel{#2}\labelspace #1}{\doshortpageno\bgroup#4\egroup}% +} + +% Appendices, in the main contents. +% Need the word Appendix, and a fixed-size box. +% +\def\appendixbox#1{% + % We use M since it's probably the widest letter. + \setbox0 = \hbox{\putwordAppendix{} M}% + \hbox to \wd0{\putwordAppendix{} #1\hss}} +% +\def\appentry#1#2#3#4{\dochapentry{\appendixbox{#2}\labelspace#1}{#4}} + +% Unnumbered chapters. +\def\unnchapentry#1#2#3#4{\dochapentry{#1}{#4}} +\def\shortunnchapentry#1#2#3#4{\tocentry{#1}{\doshortpageno\bgroup#4\egroup}} + +% Sections. +\def\numsecentry#1#2#3#4{\dosecentry{#2\labelspace#1}{#4}} +\let\appsecentry=\numsecentry +\def\unnsecentry#1#2#3#4{\dosecentry{#1}{#4}} + +% Subsections. +\def\numsubsecentry#1#2#3#4{\dosubsecentry{#2\labelspace#1}{#4}} +\let\appsubsecentry=\numsubsecentry +\def\unnsubsecentry#1#2#3#4{\dosubsecentry{#1}{#4}} + +% And subsubsections. +\def\numsubsubsecentry#1#2#3#4{\dosubsubsecentry{#2\labelspace#1}{#4}} +\let\appsubsubsecentry=\numsubsubsecentry +\def\unnsubsubsecentry#1#2#3#4{\dosubsubsecentry{#1}{#4}} + +% This parameter controls the indentation of the various levels. +% Same as \defaultparindent. +\newdimen\tocindent \tocindent = 15pt + +% Now for the actual typesetting. In all these, #1 is the text and #2 is the +% page number. +% +% If the toc has to be broken over pages, we want it to be at chapters +% if at all possible; hence the \penalty. +\def\dochapentry#1#2{% + \penalty-300 \vskip1\baselineskip plus.33\baselineskip minus.25\baselineskip + \begingroup + \chapentryfonts + \tocentry{#1}{\dopageno\bgroup#2\egroup}% + \endgroup + \nobreak\vskip .25\baselineskip plus.1\baselineskip +} + +\def\dosecentry#1#2{\begingroup + \secentryfonts \leftskip=\tocindent + \tocentry{#1}{\dopageno\bgroup#2\egroup}% +\endgroup} + +\def\dosubsecentry#1#2{\begingroup + \subsecentryfonts \leftskip=2\tocindent + \tocentry{#1}{\dopageno\bgroup#2\egroup}% +\endgroup} + +\def\dosubsubsecentry#1#2{\begingroup + \subsubsecentryfonts \leftskip=3\tocindent + \tocentry{#1}{\dopageno\bgroup#2\egroup}% +\endgroup} + +% We use the same \entry macro as for the index entries. +\let\tocentry = \entry + +% Space between chapter (or whatever) number and the title. +\def\labelspace{\hskip1em \relax} + +\def\dopageno#1{{\rm #1}} +\def\doshortpageno#1{{\rm #1}} + +\def\chapentryfonts{\secfonts \rm} +\def\secentryfonts{\textfonts} +\def\subsecentryfonts{\textfonts} +\def\subsubsecentryfonts{\textfonts} + + +\message{environments,} +% @foo ... @end foo. + +% @tex ... @end tex escapes into raw TeX temporarily. +% One exception: @ is still an escape character, so that @end tex works. +% But \@ or @@ will get a plain @ character. + +\envdef\tex{% + \setupmarkupstyle{tex}% + \catcode `\\=0 \catcode `\{=1 \catcode `\}=2 + \catcode `\$=3 \catcode `\&=4 \catcode `\#=6 + \catcode `\^=7 \catcode `\_=8 \catcode `\~=\active \let~=\tie + \catcode `\%=14 + \catcode `\+=\other + \catcode `\"=\other + \catcode `\|=\other + \catcode `\<=\other + \catcode `\>=\other + \catcode`\`=\other + \catcode`\'=\other + \escapechar=`\\ + % + % ' is active in math mode (mathcode"8000). So reset it, and all our + % other math active characters (just in case), to plain's definitions. + \mathactive + % + \let\b=\ptexb + \let\bullet=\ptexbullet + \let\c=\ptexc + \let\,=\ptexcomma + \let\.=\ptexdot + \let\dots=\ptexdots + \let\equiv=\ptexequiv + \let\!=\ptexexclam + \let\i=\ptexi + \let\indent=\ptexindent + \let\noindent=\ptexnoindent + \let\{=\ptexlbrace + \let\+=\tabalign + \let\}=\ptexrbrace + \let\/=\ptexslash + \let\*=\ptexstar + \let\t=\ptext + \expandafter \let\csname top\endcsname=\ptextop % outer + \let\frenchspacing=\plainfrenchspacing + % + \def\endldots{\mathinner{\ldots\ldots\ldots\ldots}}% + \def\enddots{\relax\ifmmode\endldots\else$\mathsurround=0pt \endldots\,$\fi}% + \def\@{@}% +} +% There is no need to define \Etex. + +% Define @lisp ... @end lisp. +% @lisp environment forms a group so it can rebind things, +% including the definition of @end lisp (which normally is erroneous). + +% Amount to narrow the margins by for @lisp. +\newskip\lispnarrowing \lispnarrowing=0.4in + +% This is the definition that ^^M gets inside @lisp, @example, and other +% such environments. \null is better than a space, since it doesn't +% have any width. +\def\lisppar{\null\endgraf} + +% This space is always present above and below environments. +\newskip\envskipamount \envskipamount = 0pt + +% Make spacing and below environment symmetrical. We use \parskip here +% to help in doing that, since in @example-like environments \parskip +% is reset to zero; thus the \afterenvbreak inserts no space -- but the +% start of the next paragraph will insert \parskip. +% +\def\aboveenvbreak{{% + % =10000 instead of <10000 because of a special case in \itemzzz and + % \sectionheading, q.v. + \ifnum \lastpenalty=10000 \else + \advance\envskipamount by \parskip + \endgraf + \ifdim\lastskip<\envskipamount + \removelastskip + % it's not a good place to break if the last penalty was \nobreak + % or better ... + \ifnum\lastpenalty<10000 \penalty-50 \fi + \vskip\envskipamount + \fi + \fi +}} + +\let\afterenvbreak = \aboveenvbreak + +% \nonarrowing is a flag. If "set", @lisp etc don't narrow margins; it will +% also clear it, so that its embedded environments do the narrowing again. +\let\nonarrowing=\relax + +% @cartouche ... @end cartouche: draw rectangle w/rounded corners around +% environment contents. +\font\circle=lcircle10 +\newdimen\circthick +\newdimen\cartouter\newdimen\cartinner +\newskip\normbskip\newskip\normpskip\newskip\normlskip +\circthick=\fontdimen8\circle +% +\def\ctl{{\circle\char'013\hskip -6pt}}% 6pt from pl file: 1/2charwidth +\def\ctr{{\hskip 6pt\circle\char'010}} +\def\cbl{{\circle\char'012\hskip -6pt}} +\def\cbr{{\hskip 6pt\circle\char'011}} +\def\carttop{\hbox to \cartouter{\hskip\lskip + \ctl\leaders\hrule height\circthick\hfil\ctr + \hskip\rskip}} +\def\cartbot{\hbox to \cartouter{\hskip\lskip + \cbl\leaders\hrule height\circthick\hfil\cbr + \hskip\rskip}} +% +\newskip\lskip\newskip\rskip + +\envdef\cartouche{% + \ifhmode\par\fi % can't be in the midst of a paragraph. + \startsavinginserts + \lskip=\leftskip \rskip=\rightskip + \leftskip=0pt\rightskip=0pt % we want these *outside*. + \cartinner=\hsize \advance\cartinner by-\lskip + \advance\cartinner by-\rskip + \cartouter=\hsize + \advance\cartouter by 18.4pt % allow for 3pt kerns on either + % side, and for 6pt waste from + % each corner char, and rule thickness + \normbskip=\baselineskip \normpskip=\parskip \normlskip=\lineskip + % Flag to tell @lisp, etc., not to narrow margin. + \let\nonarrowing = t% + % + % If this cartouche directly follows a sectioning command, we need the + % \parskip glue (backspaced over by default) or the cartouche can + % collide with the section heading. + \ifnum\lastpenalty>10000 \vskip\parskip \penalty\lastpenalty \fi + % + \vbox\bgroup + \baselineskip=0pt\parskip=0pt\lineskip=0pt + \carttop + \hbox\bgroup + \hskip\lskip + \vrule\kern3pt + \vbox\bgroup + \kern3pt + \hsize=\cartinner + \baselineskip=\normbskip + \lineskip=\normlskip + \parskip=\normpskip + \vskip -\parskip + \comment % For explanation, see the end of def\group. +} +\def\Ecartouche{% + \ifhmode\par\fi + \kern3pt + \egroup + \kern3pt\vrule + \hskip\rskip + \egroup + \cartbot + \egroup + \checkinserts +} + + +% This macro is called at the beginning of all the @example variants, +% inside a group. +\newdimen\nonfillparindent +\def\nonfillstart{% + \aboveenvbreak + \hfuzz = 12pt % Don't be fussy + \sepspaces % Make spaces be word-separators rather than space tokens. + \let\par = \lisppar % don't ignore blank lines + \obeylines % each line of input is a line of output + \parskip = 0pt + % Turn off paragraph indentation but redefine \indent to emulate + % the normal \indent. + \nonfillparindent=\parindent + \parindent = 0pt + \let\indent\nonfillindent + % + \emergencystretch = 0pt % don't try to avoid overfull boxes + \ifx\nonarrowing\relax + \advance \leftskip by \lispnarrowing + \exdentamount=\lispnarrowing + \else + \let\nonarrowing = \relax + \fi + \let\exdent=\nofillexdent +} + +\begingroup +\obeyspaces +% We want to swallow spaces (but not other tokens) after the fake +% @indent in our nonfill-environments, where spaces are normally +% active and set to @tie, resulting in them not being ignored after +% @indent. +\gdef\nonfillindent{\futurelet\temp\nonfillindentcheck}% +\gdef\nonfillindentcheck{% +\ifx\temp % +\expandafter\nonfillindentgobble% +\else% +\leavevmode\nonfillindentbox% +\fi% +}% +\endgroup +\def\nonfillindentgobble#1{\nonfillindent} +\def\nonfillindentbox{\hbox to \nonfillparindent{\hss}} + +% If you want all examples etc. small: @set dispenvsize small. +% If you want even small examples the full size: @set dispenvsize nosmall. +% This affects the following displayed environments: +% @example, @display, @format, @lisp +% +\def\smallword{small} +\def\nosmallword{nosmall} +\let\SETdispenvsize\relax +\def\setnormaldispenv{% + \ifx\SETdispenvsize\smallword + % end paragraph for sake of leading, in case document has no blank + % line. This is redundant with what happens in \aboveenvbreak, but + % we need to do it before changing the fonts, and it's inconvenient + % to change the fonts afterward. + \ifnum \lastpenalty=10000 \else \endgraf \fi + \smallexamplefonts \rm + \fi +} +\def\setsmalldispenv{% + \ifx\SETdispenvsize\nosmallword + \else + \ifnum \lastpenalty=10000 \else \endgraf \fi + \smallexamplefonts \rm + \fi +} + +% We often define two environments, @foo and @smallfoo. +% Let's do it in one command. #1 is the env name, #2 the definition. +\def\makedispenvdef#1#2{% + \expandafter\envdef\csname#1\endcsname {\setnormaldispenv #2}% + \expandafter\envdef\csname small#1\endcsname {\setsmalldispenv #2}% + \expandafter\let\csname E#1\endcsname \afterenvbreak + \expandafter\let\csname Esmall#1\endcsname \afterenvbreak +} + +% Define two environment synonyms (#1 and #2) for an environment. +\def\maketwodispenvdef#1#2#3{% + \makedispenvdef{#1}{#3}% + \makedispenvdef{#2}{#3}% +} +% +% @lisp: indented, narrowed, typewriter font; +% @example: same as @lisp. +% +% @smallexample and @smalllisp: use smaller fonts. +% Originally contributed by Pavel@xerox. +% +\maketwodispenvdef{lisp}{example}{% + \nonfillstart + \tt\setupmarkupstyle{example}% + \let\kbdfont = \kbdexamplefont % Allow @kbd to do something special. + \gobble % eat return +} +% @display/@smalldisplay: same as @lisp except keep current font. +% +\makedispenvdef{display}{% + \nonfillstart + \gobble +} + +% @format/@smallformat: same as @display except don't narrow margins. +% +\makedispenvdef{format}{% + \let\nonarrowing = t% + \nonfillstart + \gobble +} + +% @flushleft: same as @format, but doesn't obey \SETdispenvsize. +\envdef\flushleft{% + \let\nonarrowing = t% + \nonfillstart + \gobble +} +\let\Eflushleft = \afterenvbreak + +% @flushright. +% +\envdef\flushright{% + \let\nonarrowing = t% + \nonfillstart + \advance\leftskip by 0pt plus 1fill\relax + \gobble +} +\let\Eflushright = \afterenvbreak + + +% @raggedright does more-or-less normal line breaking but no right +% justification. From plain.tex. +\envdef\raggedright{% + \rightskip0pt plus2em \spaceskip.3333em \xspaceskip.5em\relax +} +\let\Eraggedright\par + +\envdef\raggedleft{% + \parindent=0pt \leftskip0pt plus2em + \spaceskip.3333em \xspaceskip.5em \parfillskip=0pt + \hbadness=10000 % Last line will usually be underfull, so turn off + % badness reporting. +} +\let\Eraggedleft\par + +\envdef\raggedcenter{% + \parindent=0pt \rightskip0pt plus1em \leftskip0pt plus1em + \spaceskip.3333em \xspaceskip.5em \parfillskip=0pt + \hbadness=10000 % Last line will usually be underfull, so turn off + % badness reporting. +} +\let\Eraggedcenter\par + + +% @quotation does normal linebreaking (hence we can't use \nonfillstart) +% and narrows the margins. We keep \parskip nonzero in general, since +% we're doing normal filling. So, when using \aboveenvbreak and +% \afterenvbreak, temporarily make \parskip 0. +% +\makedispenvdef{quotation}{\quotationstart} +% +\def\quotationstart{% + {\parskip=0pt \aboveenvbreak}% because \aboveenvbreak inserts \parskip + \parindent=0pt + % + % @cartouche defines \nonarrowing to inhibit narrowing at next level down. + \ifx\nonarrowing\relax + \advance\leftskip by \lispnarrowing + \advance\rightskip by \lispnarrowing + \exdentamount = \lispnarrowing + \else + \let\nonarrowing = \relax + \fi + \parsearg\quotationlabel +} + +% We have retained a nonzero parskip for the environment, since we're +% doing normal filling. +% +\def\Equotation{% + \par + \ifx\quotationauthor\thisisundefined\else + % indent a bit. + \leftline{\kern 2\leftskip \sl ---\quotationauthor}% + \fi + {\parskip=0pt \afterenvbreak}% +} +\def\Esmallquotation{\Equotation} + +% If we're given an argument, typeset it in bold with a colon after. +\def\quotationlabel#1{% + \def\temp{#1}% + \ifx\temp\empty \else + {\bf #1: }% + \fi +} + + +% LaTeX-like @verbatim...@end verbatim and @verb{...} +% If we want to allow any as delimiter, +% we need the curly braces so that makeinfo sees the @verb command, eg: +% `@verbx...x' would look like the '@verbx' command. --janneke@gnu.org +% +% [Knuth]: Donald Ervin Knuth, 1996. The TeXbook. +% +% [Knuth] p.344; only we need to do the other characters Texinfo sets +% active too. Otherwise, they get lost as the first character on a +% verbatim line. +\def\dospecials{% + \do\ \do\\\do\{\do\}\do\$\do\&% + \do\#\do\^\do\^^K\do\_\do\^^A\do\%\do\~% + \do\<\do\>\do\|\do\@\do+\do\"% + % Don't do the quotes -- if we do, @set txicodequoteundirected and + % @set txicodequotebacktick will not have effect on @verb and + % @verbatim, and ?` and !` ligatures won't get disabled. + %\do\`\do\'% +} +% +% [Knuth] p. 380 +\def\uncatcodespecials{% + \def\do##1{\catcode`##1=\other}\dospecials} +% +% Setup for the @verb command. +% +% Eight spaces for a tab +\begingroup + \catcode`\^^I=\active + \gdef\tabeightspaces{\catcode`\^^I=\active\def^^I{\ \ \ \ \ \ \ \ }} +\endgroup +% +\def\setupverb{% + \tt % easiest (and conventionally used) font for verbatim + \def\par{\leavevmode\endgraf}% + \setupmarkupstyle{verb}% + \tabeightspaces + % Respect line breaks, + % print special symbols as themselves, and + % make each space count + % must do in this order: + \obeylines \uncatcodespecials \sepspaces +} + +% Setup for the @verbatim environment +% +% Real tab expansion. +\newdimen\tabw \setbox0=\hbox{\tt\space} \tabw=8\wd0 % tab amount +% +% We typeset each line of the verbatim in an \hbox, so we can handle +% tabs. The \global is in case the verbatim line starts with an accent, +% or some other command that starts with a begin-group. Otherwise, the +% entire \verbbox would disappear at the corresponding end-group, before +% it is typeset. Meanwhile, we can't have nested verbatim commands +% (can we?), so the \global won't be overwriting itself. +\newbox\verbbox +\def\starttabbox{\global\setbox\verbbox=\hbox\bgroup} +% +\begingroup + \catcode`\^^I=\active + \gdef\tabexpand{% + \catcode`\^^I=\active + \def^^I{\leavevmode\egroup + \dimen\verbbox=\wd\verbbox % the width so far, or since the previous tab + \divide\dimen\verbbox by\tabw + \multiply\dimen\verbbox by\tabw % compute previous multiple of \tabw + \advance\dimen\verbbox by\tabw % advance to next multiple of \tabw + \wd\verbbox=\dimen\verbbox \box\verbbox \starttabbox + }% + } +\endgroup + +% start the verbatim environment. +\def\setupverbatim{% + \let\nonarrowing = t% + \nonfillstart + \tt % easiest (and conventionally used) font for verbatim + % The \leavevmode here is for blank lines. Otherwise, we would + % never \starttabox and the \egroup would end verbatim mode. + \def\par{\leavevmode\egroup\box\verbbox\endgraf}% + \tabexpand + \setupmarkupstyle{verbatim}% + % Respect line breaks, + % print special symbols as themselves, and + % make each space count. + % Must do in this order: + \obeylines \uncatcodespecials \sepspaces + \everypar{\starttabbox}% +} + +% Do the @verb magic: verbatim text is quoted by unique +% delimiter characters. Before first delimiter expect a +% right brace, after last delimiter expect closing brace: +% +% \def\doverb'{'#1'}'{#1} +% +% [Knuth] p. 382; only eat outer {} +\begingroup + \catcode`[=1\catcode`]=2\catcode`\{=\other\catcode`\}=\other + \gdef\doverb{#1[\def\next##1#1}[##1\endgroup]\next] +\endgroup +% +\def\verb{\begingroup\setupverb\doverb} +% +% +% Do the @verbatim magic: define the macro \doverbatim so that +% the (first) argument ends when '@end verbatim' is reached, ie: +% +% \def\doverbatim#1@end verbatim{#1} +% +% For Texinfo it's a lot easier than for LaTeX, +% because texinfo's \verbatim doesn't stop at '\end{verbatim}': +% we need not redefine '\', '{' and '}'. +% +% Inspired by LaTeX's verbatim command set [latex.ltx] +% +\begingroup + \catcode`\ =\active + \obeylines % + % ignore everything up to the first ^^M, that's the newline at the end + % of the @verbatim input line itself. Otherwise we get an extra blank + % line in the output. + \xdef\doverbatim#1^^M#2@end verbatim{#2\noexpand\end\gobble verbatim}% + % We really want {...\end verbatim} in the body of the macro, but + % without the active space; thus we have to use \xdef and \gobble. +\endgroup +% +\envdef\verbatim{% + \setupverbatim\doverbatim +} +\let\Everbatim = \afterenvbreak + + +% @verbatiminclude FILE - insert text of file in verbatim environment. +% +\def\verbatiminclude{\parseargusing\filenamecatcodes\doverbatiminclude} +% +\def\doverbatiminclude#1{% + {% + \makevalueexpandable + \setupverbatim + \indexnofonts % Allow `@@' and other weird things in file names. + \wlog{texinfo.tex: doing @verbatiminclude of #1^^J}% + \input #1 + \afterenvbreak + }% +} + +% @copying ... @end copying. +% Save the text away for @insertcopying later. +% +% We save the uninterpreted tokens, rather than creating a box. +% Saving the text in a box would be much easier, but then all the +% typesetting commands (@smallbook, font changes, etc.) have to be done +% beforehand -- and a) we want @copying to be done first in the source +% file; b) letting users define the frontmatter in as flexible order as +% possible is very desirable. +% +\def\copying{\checkenv{}\begingroup\scanargctxt\docopying} +\def\docopying#1@end copying{\endgroup\def\copyingtext{#1}} +% +\def\insertcopying{% + \begingroup + \parindent = 0pt % paragraph indentation looks wrong on title page + \scanexp\copyingtext + \endgroup +} + + +\message{defuns,} +% @defun etc. + +\newskip\defbodyindent \defbodyindent=.4in +\newskip\defargsindent \defargsindent=50pt +\newskip\deflastargmargin \deflastargmargin=18pt +\newcount\defunpenalty + +% Start the processing of @deffn: +\def\startdefun{% + \ifnum\lastpenalty<10000 + \medbreak + \defunpenalty=10003 % Will keep this @deffn together with the + % following @def command, see below. + \else + % If there are two @def commands in a row, we'll have a \nobreak, + % which is there to keep the function description together with its + % header. But if there's nothing but headers, we need to allow a + % break somewhere. Check specifically for penalty 10002, inserted + % by \printdefunline, instead of 10000, since the sectioning + % commands also insert a nobreak penalty, and we don't want to allow + % a break between a section heading and a defun. + % + % As a further refinement, we avoid "club" headers by signalling + % with penalty of 10003 after the very first @deffn in the + % sequence (see above), and penalty of 10002 after any following + % @def command. + \ifnum\lastpenalty=10002 \penalty2000 \else \defunpenalty=10002 \fi + % + % Similarly, after a section heading, do not allow a break. + % But do insert the glue. + \medskip % preceded by discardable penalty, so not a breakpoint + \fi + % + \parindent=0in + \advance\leftskip by \defbodyindent + \exdentamount=\defbodyindent +} + +\def\dodefunx#1{% + % First, check whether we are in the right environment: + \checkenv#1% + % + % As above, allow line break if we have multiple x headers in a row. + % It's not a great place, though. + \ifnum\lastpenalty=10002 \penalty3000 \else \defunpenalty=10002 \fi + % + % And now, it's time to reuse the body of the original defun: + \expandafter\gobbledefun#1% +} +\def\gobbledefun#1\startdefun{} + +% \printdefunline \deffnheader{text} +% +\def\printdefunline#1#2{% + \begingroup + % call \deffnheader: + #1#2 \endheader + % common ending: + \interlinepenalty = 10000 + \advance\rightskip by 0pt plus 1fil\relax + \endgraf + \nobreak\vskip -\parskip + \penalty\defunpenalty % signal to \startdefun and \dodefunx + % Some of the @defun-type tags do not enable magic parentheses, + % rendering the following check redundant. But we don't optimize. + \checkparencounts + \endgroup +} + +\def\Edefun{\endgraf\medbreak} + +% \makedefun{deffn} creates \deffn, \deffnx and \Edeffn; +% the only thing remaining is to define \deffnheader. +% +\def\makedefun#1{% + \expandafter\let\csname E#1\endcsname = \Edefun + \edef\temp{\noexpand\domakedefun + \makecsname{#1}\makecsname{#1x}\makecsname{#1header}}% + \temp +} + +% \domakedefun \deffn \deffnx \deffnheader +% +% Define \deffn and \deffnx, without parameters. +% \deffnheader has to be defined explicitly. +% +\def\domakedefun#1#2#3{% + \envdef#1{% + \startdefun + \doingtypefnfalse % distinguish typed functions from all else + \parseargusing\activeparens{\printdefunline#3}% + }% + \def#2{\dodefunx#1}% + \def#3% +} + +\newif\ifdoingtypefn % doing typed function? +\newif\ifrettypeownline % typeset return type on its own line? + +% @deftypefnnewline on|off says whether the return type of typed functions +% are printed on their own line. This affects @deftypefn, @deftypefun, +% @deftypeop, and @deftypemethod. +% +\parseargdef\deftypefnnewline{% + \def\temp{#1}% + \ifx\temp\onword + \expandafter\let\csname SETtxideftypefnnl\endcsname + = \empty + \else\ifx\temp\offword + \expandafter\let\csname SETtxideftypefnnl\endcsname + = \relax + \else + \errhelp = \EMsimple + \errmessage{Unknown @txideftypefnnl value `\temp', + must be on|off}% + \fi\fi +} + +% Untyped functions: + +% @deffn category name args +\makedefun{deffn}{\deffngeneral{}} + +% @deffn category class name args +\makedefun{defop}#1 {\defopon{#1\ \putwordon}} + +% \defopon {category on}class name args +\def\defopon#1#2 {\deffngeneral{\putwordon\ \code{#2}}{#1\ \code{#2}} } + +% \deffngeneral {subind}category name args +% +\def\deffngeneral#1#2 #3 #4\endheader{% + % Remember that \dosubind{fn}{foo}{} is equivalent to \doind{fn}{foo}. + \dosubind{fn}{\code{#3}}{#1}% + \defname{#2}{}{#3}\magicamp\defunargs{#4\unskip}% +} + +% Typed functions: + +% @deftypefn category type name args +\makedefun{deftypefn}{\deftypefngeneral{}} + +% @deftypeop category class type name args +\makedefun{deftypeop}#1 {\deftypeopon{#1\ \putwordon}} + +% \deftypeopon {category on}class type name args +\def\deftypeopon#1#2 {\deftypefngeneral{\putwordon\ \code{#2}}{#1\ \code{#2}} } + +% \deftypefngeneral {subind}category type name args +% +\def\deftypefngeneral#1#2 #3 #4 #5\endheader{% + \dosubind{fn}{\code{#4}}{#1}% + \doingtypefntrue + \defname{#2}{#3}{#4}\defunargs{#5\unskip}% +} + +% Typed variables: + +% @deftypevr category type var args +\makedefun{deftypevr}{\deftypecvgeneral{}} + +% @deftypecv category class type var args +\makedefun{deftypecv}#1 {\deftypecvof{#1\ \putwordof}} + +% \deftypecvof {category of}class type var args +\def\deftypecvof#1#2 {\deftypecvgeneral{\putwordof\ \code{#2}}{#1\ \code{#2}} } + +% \deftypecvgeneral {subind}category type var args +% +\def\deftypecvgeneral#1#2 #3 #4 #5\endheader{% + \dosubind{vr}{\code{#4}}{#1}% + \defname{#2}{#3}{#4}\defunargs{#5\unskip}% +} + +% Untyped variables: + +% @defvr category var args +\makedefun{defvr}#1 {\deftypevrheader{#1} {} } + +% @defcv category class var args +\makedefun{defcv}#1 {\defcvof{#1\ \putwordof}} + +% \defcvof {category of}class var args +\def\defcvof#1#2 {\deftypecvof{#1}#2 {} } + +% Types: + +% @deftp category name args +\makedefun{deftp}#1 #2 #3\endheader{% + \doind{tp}{\code{#2}}% + \defname{#1}{}{#2}\defunargs{#3\unskip}% +} + +% Remaining @defun-like shortcuts: +\makedefun{defun}{\deffnheader{\putwordDeffunc} } +\makedefun{defmac}{\deffnheader{\putwordDefmac} } +\makedefun{defspec}{\deffnheader{\putwordDefspec} } +\makedefun{deftypefun}{\deftypefnheader{\putwordDeffunc} } +\makedefun{defvar}{\defvrheader{\putwordDefvar} } +\makedefun{defopt}{\defvrheader{\putwordDefopt} } +\makedefun{deftypevar}{\deftypevrheader{\putwordDefvar} } +\makedefun{defmethod}{\defopon\putwordMethodon} +\makedefun{deftypemethod}{\deftypeopon\putwordMethodon} +\makedefun{defivar}{\defcvof\putwordInstanceVariableof} +\makedefun{deftypeivar}{\deftypecvof\putwordInstanceVariableof} + +% \defname, which formats the name of the @def (not the args). +% #1 is the category, such as "Function". +% #2 is the return type, if any. +% #3 is the function name. +% +% We are followed by (but not passed) the arguments, if any. +% +\def\defname#1#2#3{% + \par + % Get the values of \leftskip and \rightskip as they were outside the @def... + \advance\leftskip by -\defbodyindent + % + % Determine if we are typesetting the return type of a typed function + % on a line by itself. + \rettypeownlinefalse + \ifdoingtypefn % doing a typed function specifically? + % then check user option for putting return type on its own line: + \expandafter\ifx\csname SETtxideftypefnnl\endcsname\relax \else + \rettypeownlinetrue + \fi + \fi + % + % How we'll format the category name. Putting it in brackets helps + % distinguish it from the body text that may end up on the next line + % just below it. + \def\temp{#1}% + \setbox0=\hbox{\kern\deflastargmargin \ifx\temp\empty\else [\rm\temp]\fi} + % + % Figure out line sizes for the paragraph shape. We'll always have at + % least two. + \tempnum = 2 + % + % The first line needs space for \box0; but if \rightskip is nonzero, + % we need only space for the part of \box0 which exceeds it: + \dimen0=\hsize \advance\dimen0 by -\wd0 \advance\dimen0 by \rightskip + % + % If doing a return type on its own line, we'll have another line. + \ifrettypeownline + \advance\tempnum by 1 + \def\maybeshapeline{0in \hsize}% + \else + \def\maybeshapeline{}% + \fi + % + % The continuations: + \dimen2=\hsize \advance\dimen2 by -\defargsindent + % + % The final paragraph shape: + \parshape \tempnum 0in \dimen0 \maybeshapeline \defargsindent \dimen2 + % + % Put the category name at the right margin. + \noindent + \hbox to 0pt{% + \hfil\box0 \kern-\hsize + % \hsize has to be shortened this way: + \kern\leftskip + % Intentionally do not respect \rightskip, since we need the space. + }% + % + % Allow all lines to be underfull without complaint: + \tolerance=10000 \hbadness=10000 + \exdentamount=\defbodyindent + {% + % defun fonts. We use typewriter by default (used to be bold) because: + % . we're printing identifiers, they should be in tt in principle. + % . in languages with many accents, such as Czech or French, it's + % common to leave accents off identifiers. The result looks ok in + % tt, but exceedingly strange in rm. + % . we don't want -- and --- to be treated as ligatures. + % . this still does not fix the ?` and !` ligatures, but so far no + % one has made identifiers using them :). + \df \tt + \def\temp{#2}% text of the return type + \ifx\temp\empty\else + \tclose{\temp}% typeset the return type + \ifrettypeownline + % put return type on its own line; prohibit line break following: + \hfil\vadjust{\nobreak}\break + \else + \space % type on same line, so just followed by a space + \fi + \fi % no return type + #3% output function name + }% + {\rm\enskip}% hskip 0.5 em of \tenrm + % + \boldbrax + % arguments will be output next, if any. +} + +% Print arguments in slanted roman (not ttsl), inconsistently with using +% tt for the name. This is because literal text is sometimes needed in +% the argument list (groff manual), and ttsl and tt are not very +% distinguishable. Prevent hyphenation at `-' chars. +% +\def\defunargs#1{% + % use sl by default (not ttsl), + % tt for the names. + \df \sl \hyphenchar\font=0 + % + % On the other hand, if an argument has two dashes (for instance), we + % want a way to get ttsl. Let's try @var for that. + \def\var##1{{\setupmarkupstyle{var}\ttslanted{##1}}}% + #1% + \sl\hyphenchar\font=45 +} + +% We want ()&[] to print specially on the defun line. +% +\def\activeparens{% + \catcode`\(=\active \catcode`\)=\active + \catcode`\[=\active \catcode`\]=\active + \catcode`\&=\active +} + +% Make control sequences which act like normal parenthesis chars. +\let\lparen = ( \let\rparen = ) + +% Be sure that we always have a definition for `(', etc. For example, +% if the fn name has parens in it, \boldbrax will not be in effect yet, +% so TeX would otherwise complain about undefined control sequence. +{ + \activeparens + \global\let(=\lparen \global\let)=\rparen + \global\let[=\lbrack \global\let]=\rbrack + \global\let& = \& + + \gdef\boldbrax{\let(=\opnr\let)=\clnr\let[=\lbrb\let]=\rbrb} + \gdef\magicamp{\let&=\amprm} +} + +\newcount\parencount + +% If we encounter &foo, then turn on ()-hacking afterwards +\newif\ifampseen +\def\amprm#1 {\ampseentrue{\bf\ }} + +\def\parenfont{% + \ifampseen + % At the first level, print parens in roman, + % otherwise use the default font. + \ifnum \parencount=1 \rm \fi + \else + % The \sf parens (in \boldbrax) actually are a little bolder than + % the contained text. This is especially needed for [ and ] . + \sf + \fi +} +\def\infirstlevel#1{% + \ifampseen + \ifnum\parencount=1 + #1% + \fi + \fi +} +\def\bfafterword#1 {#1 \bf} + +\def\opnr{% + \global\advance\parencount by 1 + {\parenfont(}% + \infirstlevel \bfafterword +} +\def\clnr{% + {\parenfont)}% + \infirstlevel \sl + \global\advance\parencount by -1 +} + +\newcount\brackcount +\def\lbrb{% + \global\advance\brackcount by 1 + {\bf[}% +} +\def\rbrb{% + {\bf]}% + \global\advance\brackcount by -1 +} + +\def\checkparencounts{% + \ifnum\parencount=0 \else \badparencount \fi + \ifnum\brackcount=0 \else \badbrackcount \fi +} +% these should not use \errmessage; the glibc manual, at least, actually +% has such constructs (when documenting function pointers). +\def\badparencount{% + \message{Warning: unbalanced parentheses in @def...}% + \global\parencount=0 +} +\def\badbrackcount{% + \message{Warning: unbalanced square brackets in @def...}% + \global\brackcount=0 +} + + +\message{macros,} +% @macro. + +% To do this right we need a feature of e-TeX, \scantokens, +% which we arrange to emulate with a temporary file in ordinary TeX. +\ifx\eTeXversion\thisisundefined + \newwrite\macscribble + \def\scantokens#1{% + \toks0={#1}% + \immediate\openout\macscribble=\jobname.tmp + \immediate\write\macscribble{\the\toks0}% + \immediate\closeout\macscribble + \input \jobname.tmp + } +\fi + +\def\scanmacro#1{\begingroup + \newlinechar`\^^M + \let\xeatspaces\eatspaces + % + % Undo catcode changes of \startcontents and \doprintindex + % When called from @insertcopying or (short)caption, we need active + % backslash to get it printed correctly. Previously, we had + % \catcode`\\=\other instead. We'll see whether a problem appears + % with macro expansion. --kasal, 19aug04 + \catcode`\@=0 \catcode`\\=\active \escapechar=`\@ + % + % ... and for \example: + \spaceisspace + % + % The \empty here causes a following catcode 5 newline to be eaten as + % part of reading whitespace after a control sequence. It does not + % eat a catcode 13 newline. There's no good way to handle the two + % cases (untried: maybe e-TeX's \everyeof could help, though plain TeX + % would then have different behavior). See the Macro Details node in + % the manual for the workaround we recommend for macros and + % line-oriented commands. + % + \scantokens{#1\empty}% +\endgroup} + +\def\scanexp#1{% + \edef\temp{\noexpand\scanmacro{#1}}% + \temp +} + +\newcount\paramno % Count of parameters +\newtoks\macname % Macro name +\newif\ifrecursive % Is it recursive? + +% List of all defined macros in the form +% \definedummyword\macro1\definedummyword\macro2... +% Currently is also contains all @aliases; the list can be split +% if there is a need. +\def\macrolist{} + +% Add the macro to \macrolist +\def\addtomacrolist#1{\expandafter \addtomacrolistxxx \csname#1\endcsname} +\def\addtomacrolistxxx#1{% + \toks0 = \expandafter{\macrolist\definedummyword#1}% + \xdef\macrolist{\the\toks0}% +} + +% Utility routines. +% This does \let #1 = #2, with \csnames; that is, +% \let \csname#1\endcsname = \csname#2\endcsname +% (except of course we have to play expansion games). +% +\def\cslet#1#2{% + \expandafter\let + \csname#1\expandafter\endcsname + \csname#2\endcsname +} + +% Trim leading and trailing spaces off a string. +% Concepts from aro-bend problem 15 (see CTAN). +{\catcode`\@=11 +\gdef\eatspaces #1{\expandafter\trim@\expandafter{#1 }} +\gdef\trim@ #1{\trim@@ @#1 @ #1 @ @@} +\gdef\trim@@ #1@ #2@ #3@@{\trim@@@\empty #2 @} +\def\unbrace#1{#1} +\unbrace{\gdef\trim@@@ #1 } #2@{#1} +} + +% Trim a single trailing ^^M off a string. +{\catcode`\^^M=\other \catcode`\Q=3% +\gdef\eatcr #1{\eatcra #1Q^^MQ}% +\gdef\eatcra#1^^MQ{\eatcrb#1Q}% +\gdef\eatcrb#1Q#2Q{#1}% +} + +% Macro bodies are absorbed as an argument in a context where +% all characters are catcode 10, 11 or 12, except \ which is active +% (as in normal texinfo). It is necessary to change the definition of \ +% to recognize macro arguments; this is the job of \mbodybackslash. +% +% Non-ASCII encodings make 8-bit characters active, so un-activate +% them to avoid their expansion. Must do this non-globally, to +% confine the change to the current group. +% +% It's necessary to have hard CRs when the macro is executed. This is +% done by making ^^M (\endlinechar) catcode 12 when reading the macro +% body, and then making it the \newlinechar in \scanmacro. +% +\def\scanctxt{% used as subroutine + \catcode`\"=\other + \catcode`\+=\other + \catcode`\<=\other + \catcode`\>=\other + \catcode`\@=\other + \catcode`\^=\other + \catcode`\_=\other + \catcode`\|=\other + \catcode`\~=\other + \ifx\declaredencoding\ascii \else \setnonasciicharscatcodenonglobal\other \fi +} + +\def\scanargctxt{% used for copying and captions, not macros. + \scanctxt + \catcode`\\=\other + \catcode`\^^M=\other +} + +\def\macrobodyctxt{% used for @macro definitions + \scanctxt + \catcode`\{=\other + \catcode`\}=\other + \catcode`\^^M=\other + \usembodybackslash +} + +\def\macroargctxt{% used when scanning invocations + \scanctxt + \catcode`\\=0 +} +% why catcode 0 for \ in the above? To recognize \\ \{ \} as "escapes" +% for the single characters \ { }. Thus, we end up with the "commands" +% that would be written @\ @{ @} in a Texinfo document. +% +% We already have @{ and @}. For @\, we define it here, and only for +% this purpose, to produce a typewriter backslash (so, the @\ that we +% define for @math can't be used with @macro calls): +% +\def\\{\normalbackslash}% +% +% We would like to do this for \, too, since that is what makeinfo does. +% But it is not possible, because Texinfo already has a command @, for a +% cedilla accent. Documents must use @comma{} instead. +% +% \anythingelse will almost certainly be an error of some kind. + + +% \mbodybackslash is the definition of \ in @macro bodies. +% It maps \foo\ => \csname macarg.foo\endcsname => #N +% where N is the macro parameter number. +% We define \csname macarg.\endcsname to be \realbackslash, so +% \\ in macro replacement text gets you a backslash. +% +{\catcode`@=0 @catcode`@\=@active + @gdef@usembodybackslash{@let\=@mbodybackslash} + @gdef@mbodybackslash#1\{@csname macarg.#1@endcsname} +} +\expandafter\def\csname macarg.\endcsname{\realbackslash} + +\def\margbackslash#1{\char`\#1 } + +\def\macro{\recursivefalse\parsearg\macroxxx} +\def\rmacro{\recursivetrue\parsearg\macroxxx} + +\def\macroxxx#1{% + \getargs{#1}% now \macname is the macname and \argl the arglist + \ifx\argl\empty % no arguments + \paramno=0\relax + \else + \expandafter\parsemargdef \argl;% + \if\paramno>256\relax + \ifx\eTeXversion\thisisundefined + \errhelp = \EMsimple + \errmessage{You need eTeX to compile a file with macros with more than 256 arguments} + \fi + \fi + \fi + \if1\csname ismacro.\the\macname\endcsname + \message{Warning: redefining \the\macname}% + \else + \expandafter\ifx\csname \the\macname\endcsname \relax + \else \errmessage{Macro name \the\macname\space already defined}\fi + \global\cslet{macsave.\the\macname}{\the\macname}% + \global\expandafter\let\csname ismacro.\the\macname\endcsname=1% + \addtomacrolist{\the\macname}% + \fi + \begingroup \macrobodyctxt + \ifrecursive \expandafter\parsermacbody + \else \expandafter\parsemacbody + \fi} + +\parseargdef\unmacro{% + \if1\csname ismacro.#1\endcsname + \global\cslet{#1}{macsave.#1}% + \global\expandafter\let \csname ismacro.#1\endcsname=0% + % Remove the macro name from \macrolist: + \begingroup + \expandafter\let\csname#1\endcsname \relax + \let\definedummyword\unmacrodo + \xdef\macrolist{\macrolist}% + \endgroup + \else + \errmessage{Macro #1 not defined}% + \fi +} + +% Called by \do from \dounmacro on each macro. The idea is to omit any +% macro definitions that have been changed to \relax. +% +\def\unmacrodo#1{% + \ifx #1\relax + % remove this + \else + \noexpand\definedummyword \noexpand#1% + \fi +} + +% This makes use of the obscure feature that if the last token of a +% is #, then the preceding argument is delimited by +% an opening brace, and that opening brace is not consumed. +\def\getargs#1{\getargsxxx#1{}} +\def\getargsxxx#1#{\getmacname #1 \relax\getmacargs} +\def\getmacname#1 #2\relax{\macname={#1}} +\def\getmacargs#1{\def\argl{#1}} + +% For macro processing make @ a letter so that we can make Texinfo private macro names. +\edef\texiatcatcode{\the\catcode`\@} +\catcode `@=11\relax + +% Parse the optional {params} list. Set up \paramno and \paramlist +% so \defmacro knows what to do. Define \macarg.BLAH for each BLAH +% in the params list to some hook where the argument si to be expanded. If +% there are less than 10 arguments that hook is to be replaced by ##N where N +% is the position in that list, that is to say the macro arguments are to be +% defined `a la TeX in the macro body. +% +% That gets used by \mbodybackslash (above). +% +% We need to get `macro parameter char #' into several definitions. +% The technique used is stolen from LaTeX: let \hash be something +% unexpandable, insert that wherever you need a #, and then redefine +% it to # just before using the token list produced. +% +% The same technique is used to protect \eatspaces till just before +% the macro is used. +% +% If there are 10 or more arguments, a different technique is used, where the +% hook remains in the body, and when macro is to be expanded the body is +% processed again to replace the arguments. +% +% In that case, the hook is \the\toks N-1, and we simply set \toks N-1 to the +% argument N value and then \edef the body (nothing else will expand because of +% the catcode regime underwhich the body was input). +% +% If you compile with TeX (not eTeX), and you have macros with 10 or more +% arguments, you need that no macro has more than 256 arguments, otherwise an +% error is produced. +\def\parsemargdef#1;{% + \paramno=0\def\paramlist{}% + \let\hash\relax + \let\xeatspaces\relax + \parsemargdefxxx#1,;,% + % In case that there are 10 or more arguments we parse again the arguments + % list to set new definitions for the \macarg.BLAH macros corresponding to + % each BLAH argument. It was anyhow needed to parse already once this list + % in order to count the arguments, and as macros with at most 9 arguments + % are by far more frequent than macro with 10 or more arguments, defining + % twice the \macarg.BLAH macros does not cost too much processing power. + \ifnum\paramno<10\relax\else + \paramno0\relax + \parsemmanyargdef@@#1,;,% 10 or more arguments + \fi +} +\def\parsemargdefxxx#1,{% + \if#1;\let\next=\relax + \else \let\next=\parsemargdefxxx + \advance\paramno by 1 + \expandafter\edef\csname macarg.\eatspaces{#1}\endcsname + {\xeatspaces{\hash\the\paramno}}% + \edef\paramlist{\paramlist\hash\the\paramno,}% + \fi\next} + +\def\parsemmanyargdef@@#1,{% + \if#1;\let\next=\relax + \else + \let\next=\parsemmanyargdef@@ + \edef\tempb{\eatspaces{#1}}% + \expandafter\def\expandafter\tempa + \expandafter{\csname macarg.\tempb\endcsname}% + % Note that we need some extra \noexpand\noexpand, this is because we + % don't want \the to be expanded in the \parsermacbody as it uses an + % \xdef . + \expandafter\edef\tempa + {\noexpand\noexpand\noexpand\the\toks\the\paramno}% + \advance\paramno by 1\relax + \fi\next} + +% These two commands read recursive and nonrecursive macro bodies. +% (They're different since rec and nonrec macros end differently.) +% + +\catcode `\@\texiatcatcode +\long\def\parsemacbody#1@end macro% +{\xdef\temp{\eatcr{#1}}\endgroup\defmacro}% +\long\def\parsermacbody#1@end rmacro% +{\xdef\temp{\eatcr{#1}}\endgroup\defmacro}% +\catcode `\@=11\relax + +\let\endargs@\relax +\let\nil@\relax +\def\nilm@{\nil@}% +\long\def\nillm@{\nil@}% + +% This macro is expanded during the Texinfo macro expansion, not during its +% definition. It gets all the arguments values and assigns them to macros +% macarg.ARGNAME +% +% #1 is the macro name +% #2 is the list of argument names +% #3 is the list of argument values +\def\getargvals@#1#2#3{% + \def\macargdeflist@{}% + \def\saveparamlist@{#2}% Need to keep a copy for parameter expansion. + \def\paramlist{#2,\nil@}% + \def\macroname{#1}% + \begingroup + \macroargctxt + \def\argvaluelist{#3,\nil@}% + \def\@tempa{#3}% + \ifx\@tempa\empty + \setemptyargvalues@ + \else + \getargvals@@ + \fi +} + +% +\def\getargvals@@{% + \ifx\paramlist\nilm@ + % Some sanity check needed here that \argvaluelist is also empty. + \ifx\argvaluelist\nillm@ + \else + \errhelp = \EMsimple + \errmessage{Too many arguments in macro `\macroname'!}% + \fi + \let\next\macargexpandinbody@ + \else + \ifx\argvaluelist\nillm@ + % No more arguments values passed to macro. Set remaining named-arg + % macros to empty. + \let\next\setemptyargvalues@ + \else + % pop current arg name into \@tempb + \def\@tempa##1{\pop@{\@tempb}{\paramlist}##1\endargs@}% + \expandafter\@tempa\expandafter{\paramlist}% + % pop current argument value into \@tempc + \def\@tempa##1{\longpop@{\@tempc}{\argvaluelist}##1\endargs@}% + \expandafter\@tempa\expandafter{\argvaluelist}% + % Here \@tempb is the current arg name and \@tempc is the current arg value. + % First place the new argument macro definition into \@tempd + \expandafter\macname\expandafter{\@tempc}% + \expandafter\let\csname macarg.\@tempb\endcsname\relax + \expandafter\def\expandafter\@tempe\expandafter{% + \csname macarg.\@tempb\endcsname}% + \edef\@tempd{\long\def\@tempe{\the\macname}}% + \push@\@tempd\macargdeflist@ + \let\next\getargvals@@ + \fi + \fi + \next +} + +\def\push@#1#2{% + \expandafter\expandafter\expandafter\def + \expandafter\expandafter\expandafter#2% + \expandafter\expandafter\expandafter{% + \expandafter#1#2}% +} + +% Replace arguments by their values in the macro body, and place the result +% in macro \@tempa +\def\macvalstoargs@{% + % To do this we use the property that token registers that are \the'ed + % within an \edef expand only once. So we are going to place all argument + % values into respective token registers. + % + % First we save the token context, and initialize argument numbering. + \begingroup + \paramno0\relax + % Then, for each argument number #N, we place the corresponding argument + % value into a new token list register \toks#N + \expandafter\putargsintokens@\saveparamlist@,;,% + % Then, we expand the body so that argument are replaced by their + % values. The trick for values not to be expanded themselves is that they + % are within tokens and that tokens expand only once in an \edef . + \edef\@tempc{\csname mac.\macroname .body\endcsname}% + % Now we restore the token stack pointer to free the token list registers + % which we have used, but we make sure that expanded body is saved after + % group. + \expandafter + \endgroup + \expandafter\def\expandafter\@tempa\expandafter{\@tempc}% + } + +\def\macargexpandinbody@{% + %% Define the named-macro outside of this group and then close this group. + \expandafter + \endgroup + \macargdeflist@ + % First the replace in body the macro arguments by their values, the result + % is in \@tempa . + \macvalstoargs@ + % Then we point at the \norecurse or \gobble (for recursive) macro value + % with \@tempb . + \expandafter\let\expandafter\@tempb\csname mac.\macroname .recurse\endcsname + % Depending on whether it is recursive or not, we need some tailing + % \egroup . + \ifx\@tempb\gobble + \let\@tempc\relax + \else + \let\@tempc\egroup + \fi + % And now we do the real job: + \edef\@tempd{\noexpand\@tempb{\macroname}\noexpand\scanmacro{\@tempa}\@tempc}% + \@tempd +} + +\def\putargsintokens@#1,{% + \if#1;\let\next\relax + \else + \let\next\putargsintokens@ + % First we allocate the new token list register, and give it a temporary + % alias \@tempb . + \toksdef\@tempb\the\paramno + % Then we place the argument value into that token list register. + \expandafter\let\expandafter\@tempa\csname macarg.#1\endcsname + \expandafter\@tempb\expandafter{\@tempa}% + \advance\paramno by 1\relax + \fi + \next +} + +% Save the token stack pointer into macro #1 +\def\texisavetoksstackpoint#1{\edef#1{\the\@cclvi}} +% Restore the token stack pointer from number in macro #1 +\def\texirestoretoksstackpoint#1{\expandafter\mathchardef\expandafter\@cclvi#1\relax} +% newtoks that can be used non \outer . +\def\texinonouternewtoks{\alloc@ 5\toks \toksdef \@cclvi} + +% Tailing missing arguments are set to empty +\def\setemptyargvalues@{% + \ifx\paramlist\nilm@ + \let\next\macargexpandinbody@ + \else + \expandafter\setemptyargvaluesparser@\paramlist\endargs@ + \let\next\setemptyargvalues@ + \fi + \next +} + +\def\setemptyargvaluesparser@#1,#2\endargs@{% + \expandafter\def\expandafter\@tempa\expandafter{% + \expandafter\def\csname macarg.#1\endcsname{}}% + \push@\@tempa\macargdeflist@ + \def\paramlist{#2}% +} + +% #1 is the element target macro +% #2 is the list macro +% #3,#4\endargs@ is the list value +\def\pop@#1#2#3,#4\endargs@{% + \def#1{#3}% + \def#2{#4}% +} +\long\def\longpop@#1#2#3,#4\endargs@{% + \long\def#1{#3}% + \long\def#2{#4}% +} + +% This defines a Texinfo @macro. There are eight cases: recursive and +% nonrecursive macros of zero, one, up to nine, and many arguments. +% Much magic with \expandafter here. +% \xdef is used so that macro definitions will survive the file +% they're defined in; @include reads the file inside a group. +% +\def\defmacro{% + \let\hash=##% convert placeholders to macro parameter chars + \ifrecursive + \ifcase\paramno + % 0 + \expandafter\xdef\csname\the\macname\endcsname{% + \noexpand\scanmacro{\temp}}% + \or % 1 + \expandafter\xdef\csname\the\macname\endcsname{% + \bgroup\noexpand\macroargctxt + \noexpand\braceorline + \expandafter\noexpand\csname\the\macname xxx\endcsname}% + \expandafter\xdef\csname\the\macname xxx\endcsname##1{% + \egroup\noexpand\scanmacro{\temp}}% + \else + \ifnum\paramno<10\relax % at most 9 + \expandafter\xdef\csname\the\macname\endcsname{% + \bgroup\noexpand\macroargctxt + \noexpand\csname\the\macname xx\endcsname}% + \expandafter\xdef\csname\the\macname xx\endcsname##1{% + \expandafter\noexpand\csname\the\macname xxx\endcsname ##1,}% + \expandafter\expandafter + \expandafter\xdef + \expandafter\expandafter + \csname\the\macname xxx\endcsname + \paramlist{\egroup\noexpand\scanmacro{\temp}}% + \else % 10 or more + \expandafter\xdef\csname\the\macname\endcsname{% + \noexpand\getargvals@{\the\macname}{\argl}% + }% + \global\expandafter\let\csname mac.\the\macname .body\endcsname\temp + \global\expandafter\let\csname mac.\the\macname .recurse\endcsname\gobble + \fi + \fi + \else + \ifcase\paramno + % 0 + \expandafter\xdef\csname\the\macname\endcsname{% + \noexpand\norecurse{\the\macname}% + \noexpand\scanmacro{\temp}\egroup}% + \or % 1 + \expandafter\xdef\csname\the\macname\endcsname{% + \bgroup\noexpand\macroargctxt + \noexpand\braceorline + \expandafter\noexpand\csname\the\macname xxx\endcsname}% + \expandafter\xdef\csname\the\macname xxx\endcsname##1{% + \egroup + \noexpand\norecurse{\the\macname}% + \noexpand\scanmacro{\temp}\egroup}% + \else % at most 9 + \ifnum\paramno<10\relax + \expandafter\xdef\csname\the\macname\endcsname{% + \bgroup\noexpand\macroargctxt + \expandafter\noexpand\csname\the\macname xx\endcsname}% + \expandafter\xdef\csname\the\macname xx\endcsname##1{% + \expandafter\noexpand\csname\the\macname xxx\endcsname ##1,}% + \expandafter\expandafter + \expandafter\xdef + \expandafter\expandafter + \csname\the\macname xxx\endcsname + \paramlist{% + \egroup + \noexpand\norecurse{\the\macname}% + \noexpand\scanmacro{\temp}\egroup}% + \else % 10 or more: + \expandafter\xdef\csname\the\macname\endcsname{% + \noexpand\getargvals@{\the\macname}{\argl}% + }% + \global\expandafter\let\csname mac.\the\macname .body\endcsname\temp + \global\expandafter\let\csname mac.\the\macname .recurse\endcsname\norecurse + \fi + \fi + \fi} + +\catcode `\@\texiatcatcode\relax + +\def\norecurse#1{\bgroup\cslet{#1}{macsave.#1}} + +% \braceorline decides whether the next nonwhitespace character is a +% {. If so it reads up to the closing }, if not, it reads the whole +% line. Whatever was read is then fed to the next control sequence +% as an argument (by \parsebrace or \parsearg). +% +\def\braceorline#1{\let\macnamexxx=#1\futurelet\nchar\braceorlinexxx} +\def\braceorlinexxx{% + \ifx\nchar\bgroup\else + \expandafter\parsearg + \fi \macnamexxx} + + +% @alias. +% We need some trickery to remove the optional spaces around the equal +% sign. Make them active and then expand them all to nothing. +% +\def\alias{\parseargusing\obeyspaces\aliasxxx} +\def\aliasxxx #1{\aliasyyy#1\relax} +\def\aliasyyy #1=#2\relax{% + {% + \expandafter\let\obeyedspace=\empty + \addtomacrolist{#1}% + \xdef\next{\global\let\makecsname{#1}=\makecsname{#2}}% + }% + \next +} + + +\message{cross references,} + +\newwrite\auxfile +\newif\ifhavexrefs % True if xref values are known. +\newif\ifwarnedxrefs % True if we warned once that they aren't known. + +% @inforef is relatively simple. +\def\inforef #1{\inforefzzz #1,,,,**} +\def\inforefzzz #1,#2,#3,#4**{% + \putwordSee{} \putwordInfo{} \putwordfile{} \file{\ignorespaces #3{}}, + node \samp{\ignorespaces#1{}}} + +% @node's only job in TeX is to define \lastnode, which is used in +% cross-references. The @node line might or might not have commas, and +% might or might not have spaces before the first comma, like: +% @node foo , bar , ... +% We don't want such trailing spaces in the node name. +% +\parseargdef\node{\checkenv{}\donode #1 ,\finishnodeparse} +% +% also remove a trailing comma, in case of something like this: +% @node Help-Cross, , , Cross-refs +\def\donode#1 ,#2\finishnodeparse{\dodonode #1,\finishnodeparse} +\def\dodonode#1,#2\finishnodeparse{\gdef\lastnode{#1}} + +\let\nwnode=\node +\let\lastnode=\empty + +% Write a cross-reference definition for the current node. #1 is the +% type (Ynumbered, Yappendix, Ynothing). +% +\def\donoderef#1{% + \ifx\lastnode\empty\else + \setref{\lastnode}{#1}% + \global\let\lastnode=\empty + \fi +} + +% @anchor{NAME} -- define xref target at arbitrary point. +% +\newcount\savesfregister +% +\def\savesf{\relax \ifhmode \savesfregister=\spacefactor \fi} +\def\restoresf{\relax \ifhmode \spacefactor=\savesfregister \fi} +\def\anchor#1{\savesf \setref{#1}{Ynothing}\restoresf \ignorespaces} + +% \setref{NAME}{SNT} defines a cross-reference point NAME (a node or an +% anchor), which consists of three parts: +% 1) NAME-title - the current sectioning name taken from \lastsection, +% or the anchor name. +% 2) NAME-snt - section number and type, passed as the SNT arg, or +% empty for anchors. +% 3) NAME-pg - the page number. +% +% This is called from \donoderef, \anchor, and \dofloat. In the case of +% floats, there is an additional part, which is not written here: +% 4) NAME-lof - the text as it should appear in a @listoffloats. +% +\def\setref#1#2{% + \pdfmkdest{#1}% + \iflinks + {% + \atdummies % preserve commands, but don't expand them + \edef\writexrdef##1##2{% + \write\auxfile{@xrdef{#1-% #1 of \setref, expanded by the \edef + ##1}{##2}}% these are parameters of \writexrdef + }% + \toks0 = \expandafter{\lastsection}% + \immediate \writexrdef{title}{\the\toks0 }% + \immediate \writexrdef{snt}{\csname #2\endcsname}% \Ynumbered etc. + \safewhatsit{\writexrdef{pg}{\folio}}% will be written later, at \shipout + }% + \fi +} + +% @xrefautosectiontitle on|off says whether @section(ing) names are used +% automatically in xrefs, if the third arg is not explicitly specified. +% This was provided as a "secret" @set xref-automatic-section-title +% variable, now it's official. +% +\parseargdef\xrefautomaticsectiontitle{% + \def\temp{#1}% + \ifx\temp\onword + \expandafter\let\csname SETxref-automatic-section-title\endcsname + = \empty + \else\ifx\temp\offword + \expandafter\let\csname SETxref-automatic-section-title\endcsname + = \relax + \else + \errhelp = \EMsimple + \errmessage{Unknown @xrefautomaticsectiontitle value `\temp', + must be on|off}% + \fi\fi +} + + +% @xref, @pxref, and @ref generate cross-references. For \xrefX, #1 is +% the node name, #2 the name of the Info cross-reference, #3 the printed +% node name, #4 the name of the Info file, #5 the name of the printed +% manual. All but the node name can be omitted. +% +\def\pxref#1{\putwordsee{} \xrefX[#1,,,,,,,]} +\def\xref#1{\putwordSee{} \xrefX[#1,,,,,,,]} +\def\ref#1{\xrefX[#1,,,,,,,]} +% +\newbox\topbox +\newbox\printedrefnamebox +\newbox\printedmanualbox +% +\def\xrefX[#1,#2,#3,#4,#5,#6]{\begingroup + \unsepspaces + % + \def\printedrefname{\ignorespaces #3}% + \setbox\printedrefnamebox = \hbox{\printedrefname\unskip}% + % + \def\printedmanual{\ignorespaces #5}% + \setbox\printedmanualbox = \hbox{\printedmanual\unskip}% + % + % If the printed reference name (arg #3) was not explicitly given in + % the @xref, figure out what we want to use. + \ifdim \wd\printedrefnamebox = 0pt + % No printed node name was explicitly given. + \expandafter\ifx\csname SETxref-automatic-section-title\endcsname \relax + % Not auto section-title: use node name inside the square brackets. + \def\printedrefname{\ignorespaces #1}% + \else + % Auto section-title: use chapter/section title inside + % the square brackets if we have it. + \ifdim \wd\printedmanualbox > 0pt + % It is in another manual, so we don't have it; use node name. + \def\printedrefname{\ignorespaces #1}% + \else + \ifhavexrefs + % We (should) know the real title if we have the xref values. + \def\printedrefname{\refx{#1-title}{}}% + \else + % Otherwise just copy the Info node name. + \def\printedrefname{\ignorespaces #1}% + \fi% + \fi + \fi + \fi + % + % Make link in pdf output. + \ifpdf + {\indexnofonts + \turnoffactive + \makevalueexpandable + % This expands tokens, so do it after making catcode changes, so _ + % etc. don't get their TeX definitions. + \getfilename{#4}% + % + \edef\pdfxrefdest{#1}% + \txiescapepdf\pdfxrefdest + % + \leavevmode + \startlink attr{/Border [0 0 0]}% + \ifnum\filenamelength>0 + goto file{\the\filename.pdf} name{\pdfxrefdest}% + \else + goto name{\pdfmkpgn{\pdfxrefdest}}% + \fi + }% + \setcolor{\linkcolor}% + \fi + % + % Float references are printed completely differently: "Figure 1.2" + % instead of "[somenode], p.3". We distinguish them by the + % LABEL-title being set to a magic string. + {% + % Have to otherify everything special to allow the \csname to + % include an _ in the xref name, etc. + \indexnofonts + \turnoffactive + \expandafter\global\expandafter\let\expandafter\Xthisreftitle + \csname XR#1-title\endcsname + }% + \iffloat\Xthisreftitle + % If the user specified the print name (third arg) to the ref, + % print it instead of our usual "Figure 1.2". + \ifdim\wd\printedrefnamebox = 0pt + \refx{#1-snt}{}% + \else + \printedrefname + \fi + % + % if the user also gave the printed manual name (fifth arg), append + % "in MANUALNAME". + \ifdim \wd\printedmanualbox > 0pt + \space \putwordin{} \cite{\printedmanual}% + \fi + \else + % node/anchor (non-float) references. + % + % If we use \unhbox to print the node names, TeX does not insert + % empty discretionaries after hyphens, which means that it will not + % find a line break at a hyphen in a node names. Since some manuals + % are best written with fairly long node names, containing hyphens, + % this is a loss. Therefore, we give the text of the node name + % again, so it is as if TeX is seeing it for the first time. + % + % Cross-manual reference. Only include the "Section ``foo'' in" if + % the foo is neither missing or Top. Thus, @xref{,,,foo,The Foo Manual} + % outputs simply "see The Foo Manual". + \ifdim \wd\printedmanualbox > 0pt + % What is the 7sp about? The idea is that we also want to omit + % the Section part if we would be printing "Top", since they are + % clearly trying to refer to the whole manual. But, this being + % TeX, we can't easily compare strings while ignoring the possible + % spaces before and after in the input. By adding the arbitrary + % 7sp, we make it much less likely that a real node name would + % happen to have the same width as "Top" (e.g., in a monospaced font). + % I hope it will never happen in practice. + % + % For the same basic reason, we retypeset the "Top" at every + % reference, since the current font is indeterminate. + % + \setbox\topbox = \hbox{Top\kern7sp}% + \setbox2 = \hbox{\ignorespaces \printedrefname \unskip \kern7sp}% + \ifdim \wd2 > 7sp + \ifdim \wd2 = \wd\topbox \else + \putwordSection{} ``\printedrefname'' \putwordin{}\space + \fi + \fi + \cite{\printedmanual}% + \else + % Reference in this manual. + % + % _ (for example) has to be the character _ for the purposes of the + % control sequence corresponding to the node, but it has to expand + % into the usual \leavevmode...\vrule stuff for purposes of + % printing. So we \turnoffactive for the \refx-snt, back on for the + % printing, back off for the \refx-pg. + {\turnoffactive + % Only output a following space if the -snt ref is nonempty; for + % @unnumbered and @anchor, it won't be. + \setbox2 = \hbox{\ignorespaces \refx{#1-snt}{}}% + \ifdim \wd2 > 0pt \refx{#1-snt}\space\fi + }% + % output the `[mynode]' via the macro below so it can be overridden. + \xrefprintnodename\printedrefname + % + % But we always want a comma and a space: + ,\space + % + % output the `page 3'. + \turnoffactive \putwordpage\tie\refx{#1-pg}{}% + \fi + \fi + \endlink +\endgroup} + +% This macro is called from \xrefX for the `[nodename]' part of xref +% output. It's a separate macro only so it can be changed more easily, +% since square brackets don't work well in some documents. Particularly +% one that Bob is working on :). +% +\def\xrefprintnodename#1{[#1]} + +% Things referred to by \setref. +% +\def\Ynothing{} +\def\Yomitfromtoc{} +\def\Ynumbered{% + \ifnum\secno=0 + \putwordChapter@tie \the\chapno + \else \ifnum\subsecno=0 + \putwordSection@tie \the\chapno.\the\secno + \else \ifnum\subsubsecno=0 + \putwordSection@tie \the\chapno.\the\secno.\the\subsecno + \else + \putwordSection@tie \the\chapno.\the\secno.\the\subsecno.\the\subsubsecno + \fi\fi\fi +} +\def\Yappendix{% + \ifnum\secno=0 + \putwordAppendix@tie @char\the\appendixno{}% + \else \ifnum\subsecno=0 + \putwordSection@tie @char\the\appendixno.\the\secno + \else \ifnum\subsubsecno=0 + \putwordSection@tie @char\the\appendixno.\the\secno.\the\subsecno + \else + \putwordSection@tie + @char\the\appendixno.\the\secno.\the\subsecno.\the\subsubsecno + \fi\fi\fi +} + +% Define \refx{NAME}{SUFFIX} to reference a cross-reference string named NAME. +% If its value is nonempty, SUFFIX is output afterward. +% +\def\refx#1#2{% + {% + \indexnofonts + \otherbackslash + \expandafter\global\expandafter\let\expandafter\thisrefX + \csname XR#1\endcsname + }% + \ifx\thisrefX\relax + % If not defined, say something at least. + \angleleft un\-de\-fined\angleright + \iflinks + \ifhavexrefs + {\toks0 = {#1}% avoid expansion of possibly-complex value + \message{\linenumber Undefined cross reference `\the\toks0'.}}% + \else + \ifwarnedxrefs\else + \global\warnedxrefstrue + \message{Cross reference values unknown; you must run TeX again.}% + \fi + \fi + \fi + \else + % It's defined, so just use it. + \thisrefX + \fi + #2% Output the suffix in any case. +} + +% This is the macro invoked by entries in the aux file. Usually it's +% just a \def (we prepend XR to the control sequence name to avoid +% collisions). But if this is a float type, we have more work to do. +% +\def\xrdef#1#2{% + {% The node name might contain 8-bit characters, which in our current + % implementation are changed to commands like @'e. Don't let these + % mess up the control sequence name. + \indexnofonts + \turnoffactive + \xdef\safexrefname{#1}% + }% + % + \expandafter\gdef\csname XR\safexrefname\endcsname{#2}% remember this xref + % + % Was that xref control sequence that we just defined for a float? + \expandafter\iffloat\csname XR\safexrefname\endcsname + % it was a float, and we have the (safe) float type in \iffloattype. + \expandafter\let\expandafter\floatlist + \csname floatlist\iffloattype\endcsname + % + % Is this the first time we've seen this float type? + \expandafter\ifx\floatlist\relax + \toks0 = {\do}% yes, so just \do + \else + % had it before, so preserve previous elements in list. + \toks0 = \expandafter{\floatlist\do}% + \fi + % + % Remember this xref in the control sequence \floatlistFLOATTYPE, + % for later use in \listoffloats. + \expandafter\xdef\csname floatlist\iffloattype\endcsname{\the\toks0 + {\safexrefname}}% + \fi +} + +% Read the last existing aux file, if any. No error if none exists. +% +\def\tryauxfile{% + \openin 1 \jobname.aux + \ifeof 1 \else + \readdatafile{aux}% + \global\havexrefstrue + \fi + \closein 1 +} + +\def\setupdatafile{% + \catcode`\^^@=\other + \catcode`\^^A=\other + \catcode`\^^B=\other + \catcode`\^^C=\other + \catcode`\^^D=\other + \catcode`\^^E=\other + \catcode`\^^F=\other + \catcode`\^^G=\other + \catcode`\^^H=\other + \catcode`\^^K=\other + \catcode`\^^L=\other + \catcode`\^^N=\other + \catcode`\^^P=\other + \catcode`\^^Q=\other + \catcode`\^^R=\other + \catcode`\^^S=\other + \catcode`\^^T=\other + \catcode`\^^U=\other + \catcode`\^^V=\other + \catcode`\^^W=\other + \catcode`\^^X=\other + \catcode`\^^Z=\other + \catcode`\^^[=\other + \catcode`\^^\=\other + \catcode`\^^]=\other + \catcode`\^^^=\other + \catcode`\^^_=\other + % It was suggested to set the catcode of ^ to 7, which would allow ^^e4 etc. + % in xref tags, i.e., node names. But since ^^e4 notation isn't + % supported in the main text, it doesn't seem desirable. Furthermore, + % that is not enough: for node names that actually contain a ^ + % character, we would end up writing a line like this: 'xrdef {'hat + % b-title}{'hat b} and \xrdef does a \csname...\endcsname on the first + % argument, and \hat is not an expandable control sequence. It could + % all be worked out, but why? Either we support ^^ or we don't. + % + % The other change necessary for this was to define \auxhat: + % \def\auxhat{\def^{'hat }}% extra space so ok if followed by letter + % and then to call \auxhat in \setq. + % + \catcode`\^=\other + % + % Special characters. Should be turned off anyway, but... + \catcode`\~=\other + \catcode`\[=\other + \catcode`\]=\other + \catcode`\"=\other + \catcode`\_=\other + \catcode`\|=\other + \catcode`\<=\other + \catcode`\>=\other + \catcode`\$=\other + \catcode`\#=\other + \catcode`\&=\other + \catcode`\%=\other + \catcode`+=\other % avoid \+ for paranoia even though we've turned it off + % + % This is to support \ in node names and titles, since the \ + % characters end up in a \csname. It's easier than + % leaving it active and making its active definition an actual \ + % character. What I don't understand is why it works in the *value* + % of the xrdef. Seems like it should be a catcode12 \, and that + % should not typeset properly. But it works, so I'm moving on for + % now. --karl, 15jan04. + \catcode`\\=\other + % + % Make the characters 128-255 be printing characters. + {% + \count1=128 + \def\loop{% + \catcode\count1=\other + \advance\count1 by 1 + \ifnum \count1<256 \loop \fi + }% + }% + % + % @ is our escape character in .aux files, and we need braces. + \catcode`\{=1 + \catcode`\}=2 + \catcode`\@=0 +} + +\def\readdatafile#1{% +\begingroup + \setupdatafile + \input\jobname.#1 +\endgroup} + + +\message{insertions,} +% including footnotes. + +\newcount \footnoteno + +% The trailing space in the following definition for supereject is +% vital for proper filling; pages come out unaligned when you do a +% pagealignmacro call if that space before the closing brace is +% removed. (Generally, numeric constants should always be followed by a +% space to prevent strange expansion errors.) +\def\supereject{\par\penalty -20000\footnoteno =0 } + +% @footnotestyle is meaningful for Info output only. +\let\footnotestyle=\comment + +{\catcode `\@=11 +% +% Auto-number footnotes. Otherwise like plain. +\gdef\footnote{% + \let\indent=\ptexindent + \let\noindent=\ptexnoindent + \global\advance\footnoteno by \@ne + \edef\thisfootno{$^{\the\footnoteno}$}% + % + % In case the footnote comes at the end of a sentence, preserve the + % extra spacing after we do the footnote number. + \let\@sf\empty + \ifhmode\edef\@sf{\spacefactor\the\spacefactor}\ptexslash\fi + % + % Remove inadvertent blank space before typesetting the footnote number. + \unskip + \thisfootno\@sf + \dofootnote +}% + +% Don't bother with the trickery in plain.tex to not require the +% footnote text as a parameter. Our footnotes don't need to be so general. +% +% Oh yes, they do; otherwise, @ifset (and anything else that uses +% \parseargline) fails inside footnotes because the tokens are fixed when +% the footnote is read. --karl, 16nov96. +% +\gdef\dofootnote{% + \insert\footins\bgroup + % We want to typeset this text as a normal paragraph, even if the + % footnote reference occurs in (for example) a display environment. + % So reset some parameters. + \hsize=\pagewidth + \interlinepenalty\interfootnotelinepenalty + \splittopskip\ht\strutbox % top baseline for broken footnotes + \splitmaxdepth\dp\strutbox + \floatingpenalty\@MM + \leftskip\z@skip + \rightskip\z@skip + \spaceskip\z@skip + \xspaceskip\z@skip + \parindent\defaultparindent + % + \smallfonts \rm + % + % Because we use hanging indentation in footnotes, a @noindent appears + % to exdent this text, so make it be a no-op. makeinfo does not use + % hanging indentation so @noindent can still be needed within footnote + % text after an @example or the like (not that this is good style). + \let\noindent = \relax + % + % Hang the footnote text off the number. Use \everypar in case the + % footnote extends for more than one paragraph. + \everypar = {\hang}% + \textindent{\thisfootno}% + % + % Don't crash into the line above the footnote text. Since this + % expands into a box, it must come within the paragraph, lest it + % provide a place where TeX can split the footnote. + \footstrut + % + % Invoke rest of plain TeX footnote routine. + \futurelet\next\fo@t +} +}%end \catcode `\@=11 + +% In case a @footnote appears in a vbox, save the footnote text and create +% the real \insert just after the vbox finished. Otherwise, the insertion +% would be lost. +% Similarly, if a @footnote appears inside an alignment, save the footnote +% text to a box and make the \insert when a row of the table is finished. +% And the same can be done for other insert classes. --kasal, 16nov03. + +% Replace the \insert primitive by a cheating macro. +% Deeper inside, just make sure that the saved insertions are not spilled +% out prematurely. +% +\def\startsavinginserts{% + \ifx \insert\ptexinsert + \let\insert\saveinsert + \else + \let\checkinserts\relax + \fi +} + +% This \insert replacement works for both \insert\footins{foo} and +% \insert\footins\bgroup foo\egroup, but it doesn't work for \insert27{foo}. +% +\def\saveinsert#1{% + \edef\next{\noexpand\savetobox \makeSAVEname#1}% + \afterassignment\next + % swallow the left brace + \let\temp = +} +\def\makeSAVEname#1{\makecsname{SAVE\expandafter\gobble\string#1}} +\def\savetobox#1{\global\setbox#1 = \vbox\bgroup \unvbox#1} + +\def\checksaveins#1{\ifvoid#1\else \placesaveins#1\fi} + +\def\placesaveins#1{% + \ptexinsert \csname\expandafter\gobblesave\string#1\endcsname + {\box#1}% +} + +% eat @SAVE -- beware, all of them have catcode \other: +{ + \def\dospecials{\do S\do A\do V\do E} \uncatcodespecials % ;-) + \gdef\gobblesave @SAVE{} +} + +% initialization: +\def\newsaveins #1{% + \edef\next{\noexpand\newsaveinsX \makeSAVEname#1}% + \next +} +\def\newsaveinsX #1{% + \csname newbox\endcsname #1% + \expandafter\def\expandafter\checkinserts\expandafter{\checkinserts + \checksaveins #1}% +} + +% initialize: +\let\checkinserts\empty +\newsaveins\footins +\newsaveins\margin + + +% @image. We use the macros from epsf.tex to support this. +% If epsf.tex is not installed and @image is used, we complain. +% +% Check for and read epsf.tex up front. If we read it only at @image +% time, we might be inside a group, and then its definitions would get +% undone and the next image would fail. +\openin 1 = epsf.tex +\ifeof 1 \else + % Do not bother showing banner with epsf.tex v2.7k (available in + % doc/epsf.tex and on ctan). + \def\epsfannounce{\toks0 = }% + \input epsf.tex +\fi +\closein 1 +% +% We will only complain once about lack of epsf.tex. +\newif\ifwarnednoepsf +\newhelp\noepsfhelp{epsf.tex must be installed for images to + work. It is also included in the Texinfo distribution, or you can get + it from ftp://tug.org/tex/epsf.tex.} +% +\def\image#1{% + \ifx\epsfbox\thisisundefined + \ifwarnednoepsf \else + \errhelp = \noepsfhelp + \errmessage{epsf.tex not found, images will be ignored}% + \global\warnednoepsftrue + \fi + \else + \imagexxx #1,,,,,\finish + \fi +} +% +% Arguments to @image: +% #1 is (mandatory) image filename; we tack on .eps extension. +% #2 is (optional) width, #3 is (optional) height. +% #4 is (ignored optional) html alt text. +% #5 is (ignored optional) extension. +% #6 is just the usual extra ignored arg for parsing stuff. +\newif\ifimagevmode +\def\imagexxx#1,#2,#3,#4,#5,#6\finish{\begingroup + \catcode`\^^M = 5 % in case we're inside an example + \normalturnoffactive % allow _ et al. in names + % If the image is by itself, center it. + \ifvmode + \imagevmodetrue + \else \ifx\centersub\centerV + % for @center @image, we need a vbox so we can have our vertical space + \imagevmodetrue + \vbox\bgroup % vbox has better behavior than vtop herev + \fi\fi + % + \ifimagevmode + \nobreak\medskip + % Usually we'll have text after the image which will insert + % \parskip glue, so insert it here too to equalize the space + % above and below. + \nobreak\vskip\parskip + \nobreak + \fi + % + % Leave vertical mode so that indentation from an enclosing + % environment such as @quotation is respected. + % However, if we're at the top level, we don't want the + % normal paragraph indentation. + % On the other hand, if we are in the case of @center @image, we don't + % want to start a paragraph, which will create a hsize-width box and + % eradicate the centering. + \ifx\centersub\centerV\else \noindent \fi + % + % Output the image. + \ifpdf + \dopdfimage{#1}{#2}{#3}% + \else + % \epsfbox itself resets \epsf?size at each figure. + \setbox0 = \hbox{\ignorespaces #2}\ifdim\wd0 > 0pt \epsfxsize=#2\relax \fi + \setbox0 = \hbox{\ignorespaces #3}\ifdim\wd0 > 0pt \epsfysize=#3\relax \fi + \epsfbox{#1.eps}% + \fi + % + \ifimagevmode + \medskip % space after a standalone image + \fi + \ifx\centersub\centerV \egroup \fi +\endgroup} + + +% @float FLOATTYPE,LABEL,LOC ... @end float for displayed figures, tables, +% etc. We don't actually implement floating yet, we always include the +% float "here". But it seemed the best name for the future. +% +\envparseargdef\float{\eatcommaspace\eatcommaspace\dofloat#1, , ,\finish} + +% There may be a space before second and/or third parameter; delete it. +\def\eatcommaspace#1, {#1,} + +% #1 is the optional FLOATTYPE, the text label for this float, typically +% "Figure", "Table", "Example", etc. Can't contain commas. If omitted, +% this float will not be numbered and cannot be referred to. +% +% #2 is the optional xref label. Also must be present for the float to +% be referable. +% +% #3 is the optional positioning argument; for now, it is ignored. It +% will somehow specify the positions allowed to float to (here, top, bottom). +% +% We keep a separate counter for each FLOATTYPE, which we reset at each +% chapter-level command. +\let\resetallfloatnos=\empty +% +\def\dofloat#1,#2,#3,#4\finish{% + \let\thiscaption=\empty + \let\thisshortcaption=\empty + % + % don't lose footnotes inside @float. + % + % BEWARE: when the floats start float, we have to issue warning whenever an + % insert appears inside a float which could possibly float. --kasal, 26may04 + % + \startsavinginserts + % + % We can't be used inside a paragraph. + \par + % + \vtop\bgroup + \def\floattype{#1}% + \def\floatlabel{#2}% + \def\floatloc{#3}% we do nothing with this yet. + % + \ifx\floattype\empty + \let\safefloattype=\empty + \else + {% + % the floattype might have accents or other special characters, + % but we need to use it in a control sequence name. + \indexnofonts + \turnoffactive + \xdef\safefloattype{\floattype}% + }% + \fi + % + % If label is given but no type, we handle that as the empty type. + \ifx\floatlabel\empty \else + % We want each FLOATTYPE to be numbered separately (Figure 1, + % Table 1, Figure 2, ...). (And if no label, no number.) + % + \expandafter\getfloatno\csname\safefloattype floatno\endcsname + \global\advance\floatno by 1 + % + {% + % This magic value for \lastsection is output by \setref as the + % XREFLABEL-title value. \xrefX uses it to distinguish float + % labels (which have a completely different output format) from + % node and anchor labels. And \xrdef uses it to construct the + % lists of floats. + % + \edef\lastsection{\floatmagic=\safefloattype}% + \setref{\floatlabel}{Yfloat}% + }% + \fi + % + % start with \parskip glue, I guess. + \vskip\parskip + % + % Don't suppress indentation if a float happens to start a section. + \restorefirstparagraphindent +} + +% we have these possibilities: +% @float Foo,lbl & @caption{Cap}: Foo 1.1: Cap +% @float Foo,lbl & no caption: Foo 1.1 +% @float Foo & @caption{Cap}: Foo: Cap +% @float Foo & no caption: Foo +% @float ,lbl & Caption{Cap}: 1.1: Cap +% @float ,lbl & no caption: 1.1 +% @float & @caption{Cap}: Cap +% @float & no caption: +% +\def\Efloat{% + \let\floatident = \empty + % + % In all cases, if we have a float type, it comes first. + \ifx\floattype\empty \else \def\floatident{\floattype}\fi + % + % If we have an xref label, the number comes next. + \ifx\floatlabel\empty \else + \ifx\floattype\empty \else % if also had float type, need tie first. + \appendtomacro\floatident{\tie}% + \fi + % the number. + \appendtomacro\floatident{\chaplevelprefix\the\floatno}% + \fi + % + % Start the printed caption with what we've constructed in + % \floatident, but keep it separate; we need \floatident again. + \let\captionline = \floatident + % + \ifx\thiscaption\empty \else + \ifx\floatident\empty \else + \appendtomacro\captionline{: }% had ident, so need a colon between + \fi + % + % caption text. + \appendtomacro\captionline{\scanexp\thiscaption}% + \fi + % + % If we have anything to print, print it, with space before. + % Eventually this needs to become an \insert. + \ifx\captionline\empty \else + \vskip.5\parskip + \captionline + % + % Space below caption. + \vskip\parskip + \fi + % + % If have an xref label, write the list of floats info. Do this + % after the caption, to avoid chance of it being a breakpoint. + \ifx\floatlabel\empty \else + % Write the text that goes in the lof to the aux file as + % \floatlabel-lof. Besides \floatident, we include the short + % caption if specified, else the full caption if specified, else nothing. + {% + \atdummies + % + % since we read the caption text in the macro world, where ^^M + % is turned into a normal character, we have to scan it back, so + % we don't write the literal three characters "^^M" into the aux file. + \scanexp{% + \xdef\noexpand\gtemp{% + \ifx\thisshortcaption\empty + \thiscaption + \else + \thisshortcaption + \fi + }% + }% + \immediate\write\auxfile{@xrdef{\floatlabel-lof}{\floatident + \ifx\gtemp\empty \else : \gtemp \fi}}% + }% + \fi + \egroup % end of \vtop + % + % place the captured inserts + % + % BEWARE: when the floats start floating, we have to issue warning + % whenever an insert appears inside a float which could possibly + % float. --kasal, 26may04 + % + \checkinserts +} + +% Append the tokens #2 to the definition of macro #1, not expanding either. +% +\def\appendtomacro#1#2{% + \expandafter\def\expandafter#1\expandafter{#1#2}% +} + +% @caption, @shortcaption +% +\def\caption{\docaption\thiscaption} +\def\shortcaption{\docaption\thisshortcaption} +\def\docaption{\checkenv\float \bgroup\scanargctxt\defcaption} +\def\defcaption#1#2{\egroup \def#1{#2}} + +% The parameter is the control sequence identifying the counter we are +% going to use. Create it if it doesn't exist and assign it to \floatno. +\def\getfloatno#1{% + \ifx#1\relax + % Haven't seen this figure type before. + \csname newcount\endcsname #1% + % + % Remember to reset this floatno at the next chap. + \expandafter\gdef\expandafter\resetallfloatnos + \expandafter{\resetallfloatnos #1=0 }% + \fi + \let\floatno#1% +} + +% \setref calls this to get the XREFLABEL-snt value. We want an @xref +% to the FLOATLABEL to expand to "Figure 3.1". We call \setref when we +% first read the @float command. +% +\def\Yfloat{\floattype@tie \chaplevelprefix\the\floatno}% + +% Magic string used for the XREFLABEL-title value, so \xrefX can +% distinguish floats from other xref types. +\def\floatmagic{!!float!!} + +% #1 is the control sequence we are passed; we expand into a conditional +% which is true if #1 represents a float ref. That is, the magic +% \lastsection value which we \setref above. +% +\def\iffloat#1{\expandafter\doiffloat#1==\finish} +% +% #1 is (maybe) the \floatmagic string. If so, #2 will be the +% (safe) float type for this float. We set \iffloattype to #2. +% +\def\doiffloat#1=#2=#3\finish{% + \def\temp{#1}% + \def\iffloattype{#2}% + \ifx\temp\floatmagic +} + +% @listoffloats FLOATTYPE - print a list of floats like a table of contents. +% +\parseargdef\listoffloats{% + \def\floattype{#1}% floattype + {% + % the floattype might have accents or other special characters, + % but we need to use it in a control sequence name. + \indexnofonts + \turnoffactive + \xdef\safefloattype{\floattype}% + }% + % + % \xrdef saves the floats as a \do-list in \floatlistSAFEFLOATTYPE. + \expandafter\ifx\csname floatlist\safefloattype\endcsname \relax + \ifhavexrefs + % if the user said @listoffloats foo but never @float foo. + \message{\linenumber No `\safefloattype' floats to list.}% + \fi + \else + \begingroup + \leftskip=\tocindent % indent these entries like a toc + \let\do=\listoffloatsdo + \csname floatlist\safefloattype\endcsname + \endgroup + \fi +} + +% This is called on each entry in a list of floats. We're passed the +% xref label, in the form LABEL-title, which is how we save it in the +% aux file. We strip off the -title and look up \XRLABEL-lof, which +% has the text we're supposed to typeset here. +% +% Figures without xref labels will not be included in the list (since +% they won't appear in the aux file). +% +\def\listoffloatsdo#1{\listoffloatsdoentry#1\finish} +\def\listoffloatsdoentry#1-title\finish{{% + % Can't fully expand XR#1-lof because it can contain anything. Just + % pass the control sequence. On the other hand, XR#1-pg is just the + % page number, and we want to fully expand that so we can get a link + % in pdf output. + \toksA = \expandafter{\csname XR#1-lof\endcsname}% + % + % use the same \entry macro we use to generate the TOC and index. + \edef\writeentry{\noexpand\entry{\the\toksA}{\csname XR#1-pg\endcsname}}% + \writeentry +}} + + +\message{localization,} + +% For single-language documents, @documentlanguage is usually given very +% early, just after @documentencoding. Single argument is the language +% (de) or locale (de_DE) abbreviation. +% +{ + \catcode`\_ = \active + \globaldefs=1 +\parseargdef\documentlanguage{\begingroup + \let_=\normalunderscore % normal _ character for filenames + \tex % read txi-??.tex file in plain TeX. + % Read the file by the name they passed if it exists. + \openin 1 txi-#1.tex + \ifeof 1 + \documentlanguagetrywithoutunderscore{#1_\finish}% + \else + \globaldefs = 1 % everything in the txi-LL files needs to persist + \input txi-#1.tex + \fi + \closein 1 + \endgroup % end raw TeX +\endgroup} +% +% If they passed de_DE, and txi-de_DE.tex doesn't exist, +% try txi-de.tex. +% +\gdef\documentlanguagetrywithoutunderscore#1_#2\finish{% + \openin 1 txi-#1.tex + \ifeof 1 + \errhelp = \nolanghelp + \errmessage{Cannot read language file txi-#1.tex}% + \else + \globaldefs = 1 % everything in the txi-LL files needs to persist + \input txi-#1.tex + \fi + \closein 1 +} +}% end of special _ catcode +% +\newhelp\nolanghelp{The given language definition file cannot be found or +is empty. Maybe you need to install it? Putting it in the current +directory should work if nowhere else does.} + +% This macro is called from txi-??.tex files; the first argument is the +% \language name to set (without the "\lang@" prefix), the second and +% third args are \{left,right}hyphenmin. +% +% The language names to pass are determined when the format is built. +% See the etex.log file created at that time, e.g., +% /usr/local/texlive/2008/texmf-var/web2c/pdftex/etex.log. +% +% With TeX Live 2008, etex now includes hyphenation patterns for all +% available languages. This means we can support hyphenation in +% Texinfo, at least to some extent. (This still doesn't solve the +% accented characters problem.) +% +\catcode`@=11 +\def\txisetlanguage#1#2#3{% + % do not set the language if the name is undefined in the current TeX. + \expandafter\ifx\csname lang@#1\endcsname \relax + \message{no patterns for #1}% + \else + \global\language = \csname lang@#1\endcsname + \fi + % but there is no harm in adjusting the hyphenmin values regardless. + \global\lefthyphenmin = #2\relax + \global\righthyphenmin = #3\relax +} + +% Helpers for encodings. +% Set the catcode of characters 128 through 255 to the specified number. +% +\def\setnonasciicharscatcode#1{% + \count255=128 + \loop\ifnum\count255<256 + \global\catcode\count255=#1\relax + \advance\count255 by 1 + \repeat +} + +\def\setnonasciicharscatcodenonglobal#1{% + \count255=128 + \loop\ifnum\count255<256 + \catcode\count255=#1\relax + \advance\count255 by 1 + \repeat +} + +% @documentencoding sets the definition of non-ASCII characters +% according to the specified encoding. +% +\parseargdef\documentencoding{% + % Encoding being declared for the document. + \def\declaredencoding{\csname #1.enc\endcsname}% + % + % Supported encodings: names converted to tokens in order to be able + % to compare them with \ifx. + \def\ascii{\csname US-ASCII.enc\endcsname}% + \def\latnine{\csname ISO-8859-15.enc\endcsname}% + \def\latone{\csname ISO-8859-1.enc\endcsname}% + \def\lattwo{\csname ISO-8859-2.enc\endcsname}% + \def\utfeight{\csname UTF-8.enc\endcsname}% + % + \ifx \declaredencoding \ascii + \asciichardefs + % + \else \ifx \declaredencoding \lattwo + \setnonasciicharscatcode\active + \lattwochardefs + % + \else \ifx \declaredencoding \latone + \setnonasciicharscatcode\active + \latonechardefs + % + \else \ifx \declaredencoding \latnine + \setnonasciicharscatcode\active + \latninechardefs + % + \else \ifx \declaredencoding \utfeight + \setnonasciicharscatcode\active + \utfeightchardefs + % + \else + \message{Unknown document encoding #1, ignoring.}% + % + \fi % utfeight + \fi % latnine + \fi % latone + \fi % lattwo + \fi % ascii +} + +% A message to be logged when using a character that isn't available +% the default font encoding (OT1). +% +\def\missingcharmsg#1{\message{Character missing in OT1 encoding: #1.}} + +% Take account of \c (plain) vs. \, (Texinfo) difference. +\def\cedilla#1{\ifx\c\ptexc\c{#1}\else\,{#1}\fi} + +% First, make active non-ASCII characters in order for them to be +% correctly categorized when TeX reads the replacement text of +% macros containing the character definitions. +\setnonasciicharscatcode\active +% +% Latin1 (ISO-8859-1) character definitions. +\def\latonechardefs{% + \gdef^^a0{\tie} + \gdef^^a1{\exclamdown} + \gdef^^a2{\missingcharmsg{CENT SIGN}} + \gdef^^a3{{\pounds}} + \gdef^^a4{\missingcharmsg{CURRENCY SIGN}} + \gdef^^a5{\missingcharmsg{YEN SIGN}} + \gdef^^a6{\missingcharmsg{BROKEN BAR}} + \gdef^^a7{\S} + \gdef^^a8{\"{}} + \gdef^^a9{\copyright} + \gdef^^aa{\ordf} + \gdef^^ab{\guillemetleft} + \gdef^^ac{$\lnot$} + \gdef^^ad{\-} + \gdef^^ae{\registeredsymbol} + \gdef^^af{\={}} + % + \gdef^^b0{\textdegree} + \gdef^^b1{$\pm$} + \gdef^^b2{$^2$} + \gdef^^b3{$^3$} + \gdef^^b4{\'{}} + \gdef^^b5{$\mu$} + \gdef^^b6{\P} + % + \gdef^^b7{$^.$} + \gdef^^b8{\cedilla\ } + \gdef^^b9{$^1$} + \gdef^^ba{\ordm} + % + \gdef^^bb{\guillemetright} + \gdef^^bc{$1\over4$} + \gdef^^bd{$1\over2$} + \gdef^^be{$3\over4$} + \gdef^^bf{\questiondown} + % + \gdef^^c0{\`A} + \gdef^^c1{\'A} + \gdef^^c2{\^A} + \gdef^^c3{\~A} + \gdef^^c4{\"A} + \gdef^^c5{\ringaccent A} + \gdef^^c6{\AE} + \gdef^^c7{\cedilla C} + \gdef^^c8{\`E} + \gdef^^c9{\'E} + \gdef^^ca{\^E} + \gdef^^cb{\"E} + \gdef^^cc{\`I} + \gdef^^cd{\'I} + \gdef^^ce{\^I} + \gdef^^cf{\"I} + % + \gdef^^d0{\DH} + \gdef^^d1{\~N} + \gdef^^d2{\`O} + \gdef^^d3{\'O} + \gdef^^d4{\^O} + \gdef^^d5{\~O} + \gdef^^d6{\"O} + \gdef^^d7{$\times$} + \gdef^^d8{\O} + \gdef^^d9{\`U} + \gdef^^da{\'U} + \gdef^^db{\^U} + \gdef^^dc{\"U} + \gdef^^dd{\'Y} + \gdef^^de{\TH} + \gdef^^df{\ss} + % + \gdef^^e0{\`a} + \gdef^^e1{\'a} + \gdef^^e2{\^a} + \gdef^^e3{\~a} + \gdef^^e4{\"a} + \gdef^^e5{\ringaccent a} + \gdef^^e6{\ae} + \gdef^^e7{\cedilla c} + \gdef^^e8{\`e} + \gdef^^e9{\'e} + \gdef^^ea{\^e} + \gdef^^eb{\"e} + \gdef^^ec{\`{\dotless i}} + \gdef^^ed{\'{\dotless i}} + \gdef^^ee{\^{\dotless i}} + \gdef^^ef{\"{\dotless i}} + % + \gdef^^f0{\dh} + \gdef^^f1{\~n} + \gdef^^f2{\`o} + \gdef^^f3{\'o} + \gdef^^f4{\^o} + \gdef^^f5{\~o} + \gdef^^f6{\"o} + \gdef^^f7{$\div$} + \gdef^^f8{\o} + \gdef^^f9{\`u} + \gdef^^fa{\'u} + \gdef^^fb{\^u} + \gdef^^fc{\"u} + \gdef^^fd{\'y} + \gdef^^fe{\th} + \gdef^^ff{\"y} +} + +% Latin9 (ISO-8859-15) encoding character definitions. +\def\latninechardefs{% + % Encoding is almost identical to Latin1. + \latonechardefs + % + \gdef^^a4{\euro} + \gdef^^a6{\v S} + \gdef^^a8{\v s} + \gdef^^b4{\v Z} + \gdef^^b8{\v z} + \gdef^^bc{\OE} + \gdef^^bd{\oe} + \gdef^^be{\"Y} +} + +% Latin2 (ISO-8859-2) character definitions. +\def\lattwochardefs{% + \gdef^^a0{\tie} + \gdef^^a1{\ogonek{A}} + \gdef^^a2{\u{}} + \gdef^^a3{\L} + \gdef^^a4{\missingcharmsg{CURRENCY SIGN}} + \gdef^^a5{\v L} + \gdef^^a6{\'S} + \gdef^^a7{\S} + \gdef^^a8{\"{}} + \gdef^^a9{\v S} + \gdef^^aa{\cedilla S} + \gdef^^ab{\v T} + \gdef^^ac{\'Z} + \gdef^^ad{\-} + \gdef^^ae{\v Z} + \gdef^^af{\dotaccent Z} + % + \gdef^^b0{\textdegree} + \gdef^^b1{\ogonek{a}} + \gdef^^b2{\ogonek{ }} + \gdef^^b3{\l} + \gdef^^b4{\'{}} + \gdef^^b5{\v l} + \gdef^^b6{\'s} + \gdef^^b7{\v{}} + \gdef^^b8{\cedilla\ } + \gdef^^b9{\v s} + \gdef^^ba{\cedilla s} + \gdef^^bb{\v t} + \gdef^^bc{\'z} + \gdef^^bd{\H{}} + \gdef^^be{\v z} + \gdef^^bf{\dotaccent z} + % + \gdef^^c0{\'R} + \gdef^^c1{\'A} + \gdef^^c2{\^A} + \gdef^^c3{\u A} + \gdef^^c4{\"A} + \gdef^^c5{\'L} + \gdef^^c6{\'C} + \gdef^^c7{\cedilla C} + \gdef^^c8{\v C} + \gdef^^c9{\'E} + \gdef^^ca{\ogonek{E}} + \gdef^^cb{\"E} + \gdef^^cc{\v E} + \gdef^^cd{\'I} + \gdef^^ce{\^I} + \gdef^^cf{\v D} + % + \gdef^^d0{\DH} + \gdef^^d1{\'N} + \gdef^^d2{\v N} + \gdef^^d3{\'O} + \gdef^^d4{\^O} + \gdef^^d5{\H O} + \gdef^^d6{\"O} + \gdef^^d7{$\times$} + \gdef^^d8{\v R} + \gdef^^d9{\ringaccent U} + \gdef^^da{\'U} + \gdef^^db{\H U} + \gdef^^dc{\"U} + \gdef^^dd{\'Y} + \gdef^^de{\cedilla T} + \gdef^^df{\ss} + % + \gdef^^e0{\'r} + \gdef^^e1{\'a} + \gdef^^e2{\^a} + \gdef^^e3{\u a} + \gdef^^e4{\"a} + \gdef^^e5{\'l} + \gdef^^e6{\'c} + \gdef^^e7{\cedilla c} + \gdef^^e8{\v c} + \gdef^^e9{\'e} + \gdef^^ea{\ogonek{e}} + \gdef^^eb{\"e} + \gdef^^ec{\v e} + \gdef^^ed{\'{\dotless{i}}} + \gdef^^ee{\^{\dotless{i}}} + \gdef^^ef{\v d} + % + \gdef^^f0{\dh} + \gdef^^f1{\'n} + \gdef^^f2{\v n} + \gdef^^f3{\'o} + \gdef^^f4{\^o} + \gdef^^f5{\H o} + \gdef^^f6{\"o} + \gdef^^f7{$\div$} + \gdef^^f8{\v r} + \gdef^^f9{\ringaccent u} + \gdef^^fa{\'u} + \gdef^^fb{\H u} + \gdef^^fc{\"u} + \gdef^^fd{\'y} + \gdef^^fe{\cedilla t} + \gdef^^ff{\dotaccent{}} +} + +% UTF-8 character definitions. +% +% This code to support UTF-8 is based on LaTeX's utf8.def, with some +% changes for Texinfo conventions. It is included here under the GPL by +% permission from Frank Mittelbach and the LaTeX team. +% +\newcount\countUTFx +\newcount\countUTFy +\newcount\countUTFz + +\gdef\UTFviiiTwoOctets#1#2{\expandafter + \UTFviiiDefined\csname u8:#1\string #2\endcsname} +% +\gdef\UTFviiiThreeOctets#1#2#3{\expandafter + \UTFviiiDefined\csname u8:#1\string #2\string #3\endcsname} +% +\gdef\UTFviiiFourOctets#1#2#3#4{\expandafter + \UTFviiiDefined\csname u8:#1\string #2\string #3\string #4\endcsname} + +\gdef\UTFviiiDefined#1{% + \ifx #1\relax + \message{\linenumber Unicode char \string #1 not defined for Texinfo}% + \else + \expandafter #1% + \fi +} + +\begingroup + \catcode`\~13 + \catcode`\"12 + + \def\UTFviiiLoop{% + \global\catcode\countUTFx\active + \uccode`\~\countUTFx + \uppercase\expandafter{\UTFviiiTmp}% + \advance\countUTFx by 1 + \ifnum\countUTFx < \countUTFy + \expandafter\UTFviiiLoop + \fi} + + \countUTFx = "C2 + \countUTFy = "E0 + \def\UTFviiiTmp{% + \xdef~{\noexpand\UTFviiiTwoOctets\string~}} + \UTFviiiLoop + + \countUTFx = "E0 + \countUTFy = "F0 + \def\UTFviiiTmp{% + \xdef~{\noexpand\UTFviiiThreeOctets\string~}} + \UTFviiiLoop + + \countUTFx = "F0 + \countUTFy = "F4 + \def\UTFviiiTmp{% + \xdef~{\noexpand\UTFviiiFourOctets\string~}} + \UTFviiiLoop +\endgroup + +\begingroup + \catcode`\"=12 + \catcode`\<=12 + \catcode`\.=12 + \catcode`\,=12 + \catcode`\;=12 + \catcode`\!=12 + \catcode`\~=13 + + \gdef\DeclareUnicodeCharacter#1#2{% + \countUTFz = "#1\relax + %\wlog{\space\space defining Unicode char U+#1 (decimal \the\countUTFz)}% + \begingroup + \parseXMLCharref + \def\UTFviiiTwoOctets##1##2{% + \csname u8:##1\string ##2\endcsname}% + \def\UTFviiiThreeOctets##1##2##3{% + \csname u8:##1\string ##2\string ##3\endcsname}% + \def\UTFviiiFourOctets##1##2##3##4{% + \csname u8:##1\string ##2\string ##3\string ##4\endcsname}% + \expandafter\expandafter\expandafter\expandafter + \expandafter\expandafter\expandafter + \gdef\UTFviiiTmp{#2}% + \endgroup} + + \gdef\parseXMLCharref{% + \ifnum\countUTFz < "A0\relax + \errhelp = \EMsimple + \errmessage{Cannot define Unicode char value < 00A0}% + \else\ifnum\countUTFz < "800\relax + \parseUTFviiiA,% + \parseUTFviiiB C\UTFviiiTwoOctets.,% + \else\ifnum\countUTFz < "10000\relax + \parseUTFviiiA;% + \parseUTFviiiA,% + \parseUTFviiiB E\UTFviiiThreeOctets.{,;}% + \else + \parseUTFviiiA;% + \parseUTFviiiA,% + \parseUTFviiiA!% + \parseUTFviiiB F\UTFviiiFourOctets.{!,;}% + \fi\fi\fi + } + + \gdef\parseUTFviiiA#1{% + \countUTFx = \countUTFz + \divide\countUTFz by 64 + \countUTFy = \countUTFz + \multiply\countUTFz by 64 + \advance\countUTFx by -\countUTFz + \advance\countUTFx by 128 + \uccode `#1\countUTFx + \countUTFz = \countUTFy} + + \gdef\parseUTFviiiB#1#2#3#4{% + \advance\countUTFz by "#10\relax + \uccode `#3\countUTFz + \uppercase{\gdef\UTFviiiTmp{#2#3#4}}} +\endgroup + +\def\utfeightchardefs{% + \DeclareUnicodeCharacter{00A0}{\tie} + \DeclareUnicodeCharacter{00A1}{\exclamdown} + \DeclareUnicodeCharacter{00A3}{\pounds} + \DeclareUnicodeCharacter{00A8}{\"{ }} + \DeclareUnicodeCharacter{00A9}{\copyright} + \DeclareUnicodeCharacter{00AA}{\ordf} + \DeclareUnicodeCharacter{00AB}{\guillemetleft} + \DeclareUnicodeCharacter{00AD}{\-} + \DeclareUnicodeCharacter{00AE}{\registeredsymbol} + \DeclareUnicodeCharacter{00AF}{\={ }} + + \DeclareUnicodeCharacter{00B0}{\ringaccent{ }} + \DeclareUnicodeCharacter{00B4}{\'{ }} + \DeclareUnicodeCharacter{00B8}{\cedilla{ }} + \DeclareUnicodeCharacter{00BA}{\ordm} + \DeclareUnicodeCharacter{00BB}{\guillemetright} + \DeclareUnicodeCharacter{00BF}{\questiondown} + + \DeclareUnicodeCharacter{00C0}{\`A} + \DeclareUnicodeCharacter{00C1}{\'A} + \DeclareUnicodeCharacter{00C2}{\^A} + \DeclareUnicodeCharacter{00C3}{\~A} + \DeclareUnicodeCharacter{00C4}{\"A} + \DeclareUnicodeCharacter{00C5}{\AA} + \DeclareUnicodeCharacter{00C6}{\AE} + \DeclareUnicodeCharacter{00C7}{\cedilla{C}} + \DeclareUnicodeCharacter{00C8}{\`E} + \DeclareUnicodeCharacter{00C9}{\'E} + \DeclareUnicodeCharacter{00CA}{\^E} + \DeclareUnicodeCharacter{00CB}{\"E} + \DeclareUnicodeCharacter{00CC}{\`I} + \DeclareUnicodeCharacter{00CD}{\'I} + \DeclareUnicodeCharacter{00CE}{\^I} + \DeclareUnicodeCharacter{00CF}{\"I} + + \DeclareUnicodeCharacter{00D0}{\DH} + \DeclareUnicodeCharacter{00D1}{\~N} + \DeclareUnicodeCharacter{00D2}{\`O} + \DeclareUnicodeCharacter{00D3}{\'O} + \DeclareUnicodeCharacter{00D4}{\^O} + \DeclareUnicodeCharacter{00D5}{\~O} + \DeclareUnicodeCharacter{00D6}{\"O} + \DeclareUnicodeCharacter{00D8}{\O} + \DeclareUnicodeCharacter{00D9}{\`U} + \DeclareUnicodeCharacter{00DA}{\'U} + \DeclareUnicodeCharacter{00DB}{\^U} + \DeclareUnicodeCharacter{00DC}{\"U} + \DeclareUnicodeCharacter{00DD}{\'Y} + \DeclareUnicodeCharacter{00DE}{\TH} + \DeclareUnicodeCharacter{00DF}{\ss} + + \DeclareUnicodeCharacter{00E0}{\`a} + \DeclareUnicodeCharacter{00E1}{\'a} + \DeclareUnicodeCharacter{00E2}{\^a} + \DeclareUnicodeCharacter{00E3}{\~a} + \DeclareUnicodeCharacter{00E4}{\"a} + \DeclareUnicodeCharacter{00E5}{\aa} + \DeclareUnicodeCharacter{00E6}{\ae} + \DeclareUnicodeCharacter{00E7}{\cedilla{c}} + \DeclareUnicodeCharacter{00E8}{\`e} + \DeclareUnicodeCharacter{00E9}{\'e} + \DeclareUnicodeCharacter{00EA}{\^e} + \DeclareUnicodeCharacter{00EB}{\"e} + \DeclareUnicodeCharacter{00EC}{\`{\dotless{i}}} + \DeclareUnicodeCharacter{00ED}{\'{\dotless{i}}} + \DeclareUnicodeCharacter{00EE}{\^{\dotless{i}}} + \DeclareUnicodeCharacter{00EF}{\"{\dotless{i}}} + + \DeclareUnicodeCharacter{00F0}{\dh} + \DeclareUnicodeCharacter{00F1}{\~n} + \DeclareUnicodeCharacter{00F2}{\`o} + \DeclareUnicodeCharacter{00F3}{\'o} + \DeclareUnicodeCharacter{00F4}{\^o} + \DeclareUnicodeCharacter{00F5}{\~o} + \DeclareUnicodeCharacter{00F6}{\"o} + \DeclareUnicodeCharacter{00F8}{\o} + \DeclareUnicodeCharacter{00F9}{\`u} + \DeclareUnicodeCharacter{00FA}{\'u} + \DeclareUnicodeCharacter{00FB}{\^u} + \DeclareUnicodeCharacter{00FC}{\"u} + \DeclareUnicodeCharacter{00FD}{\'y} + \DeclareUnicodeCharacter{00FE}{\th} + \DeclareUnicodeCharacter{00FF}{\"y} + + \DeclareUnicodeCharacter{0100}{\=A} + \DeclareUnicodeCharacter{0101}{\=a} + \DeclareUnicodeCharacter{0102}{\u{A}} + \DeclareUnicodeCharacter{0103}{\u{a}} + \DeclareUnicodeCharacter{0104}{\ogonek{A}} + \DeclareUnicodeCharacter{0105}{\ogonek{a}} + \DeclareUnicodeCharacter{0106}{\'C} + \DeclareUnicodeCharacter{0107}{\'c} + \DeclareUnicodeCharacter{0108}{\^C} + \DeclareUnicodeCharacter{0109}{\^c} + \DeclareUnicodeCharacter{0118}{\ogonek{E}} + \DeclareUnicodeCharacter{0119}{\ogonek{e}} + \DeclareUnicodeCharacter{010A}{\dotaccent{C}} + \DeclareUnicodeCharacter{010B}{\dotaccent{c}} + \DeclareUnicodeCharacter{010C}{\v{C}} + \DeclareUnicodeCharacter{010D}{\v{c}} + \DeclareUnicodeCharacter{010E}{\v{D}} + + \DeclareUnicodeCharacter{0112}{\=E} + \DeclareUnicodeCharacter{0113}{\=e} + \DeclareUnicodeCharacter{0114}{\u{E}} + \DeclareUnicodeCharacter{0115}{\u{e}} + \DeclareUnicodeCharacter{0116}{\dotaccent{E}} + \DeclareUnicodeCharacter{0117}{\dotaccent{e}} + \DeclareUnicodeCharacter{011A}{\v{E}} + \DeclareUnicodeCharacter{011B}{\v{e}} + \DeclareUnicodeCharacter{011C}{\^G} + \DeclareUnicodeCharacter{011D}{\^g} + \DeclareUnicodeCharacter{011E}{\u{G}} + \DeclareUnicodeCharacter{011F}{\u{g}} + + \DeclareUnicodeCharacter{0120}{\dotaccent{G}} + \DeclareUnicodeCharacter{0121}{\dotaccent{g}} + \DeclareUnicodeCharacter{0124}{\^H} + \DeclareUnicodeCharacter{0125}{\^h} + \DeclareUnicodeCharacter{0128}{\~I} + \DeclareUnicodeCharacter{0129}{\~{\dotless{i}}} + \DeclareUnicodeCharacter{012A}{\=I} + \DeclareUnicodeCharacter{012B}{\={\dotless{i}}} + \DeclareUnicodeCharacter{012C}{\u{I}} + \DeclareUnicodeCharacter{012D}{\u{\dotless{i}}} + + \DeclareUnicodeCharacter{0130}{\dotaccent{I}} + \DeclareUnicodeCharacter{0131}{\dotless{i}} + \DeclareUnicodeCharacter{0132}{IJ} + \DeclareUnicodeCharacter{0133}{ij} + \DeclareUnicodeCharacter{0134}{\^J} + \DeclareUnicodeCharacter{0135}{\^{\dotless{j}}} + \DeclareUnicodeCharacter{0139}{\'L} + \DeclareUnicodeCharacter{013A}{\'l} + + \DeclareUnicodeCharacter{0141}{\L} + \DeclareUnicodeCharacter{0142}{\l} + \DeclareUnicodeCharacter{0143}{\'N} + \DeclareUnicodeCharacter{0144}{\'n} + \DeclareUnicodeCharacter{0147}{\v{N}} + \DeclareUnicodeCharacter{0148}{\v{n}} + \DeclareUnicodeCharacter{014C}{\=O} + \DeclareUnicodeCharacter{014D}{\=o} + \DeclareUnicodeCharacter{014E}{\u{O}} + \DeclareUnicodeCharacter{014F}{\u{o}} + + \DeclareUnicodeCharacter{0150}{\H{O}} + \DeclareUnicodeCharacter{0151}{\H{o}} + \DeclareUnicodeCharacter{0152}{\OE} + \DeclareUnicodeCharacter{0153}{\oe} + \DeclareUnicodeCharacter{0154}{\'R} + \DeclareUnicodeCharacter{0155}{\'r} + \DeclareUnicodeCharacter{0158}{\v{R}} + \DeclareUnicodeCharacter{0159}{\v{r}} + \DeclareUnicodeCharacter{015A}{\'S} + \DeclareUnicodeCharacter{015B}{\'s} + \DeclareUnicodeCharacter{015C}{\^S} + \DeclareUnicodeCharacter{015D}{\^s} + \DeclareUnicodeCharacter{015E}{\cedilla{S}} + \DeclareUnicodeCharacter{015F}{\cedilla{s}} + + \DeclareUnicodeCharacter{0160}{\v{S}} + \DeclareUnicodeCharacter{0161}{\v{s}} + \DeclareUnicodeCharacter{0162}{\cedilla{t}} + \DeclareUnicodeCharacter{0163}{\cedilla{T}} + \DeclareUnicodeCharacter{0164}{\v{T}} + + \DeclareUnicodeCharacter{0168}{\~U} + \DeclareUnicodeCharacter{0169}{\~u} + \DeclareUnicodeCharacter{016A}{\=U} + \DeclareUnicodeCharacter{016B}{\=u} + \DeclareUnicodeCharacter{016C}{\u{U}} + \DeclareUnicodeCharacter{016D}{\u{u}} + \DeclareUnicodeCharacter{016E}{\ringaccent{U}} + \DeclareUnicodeCharacter{016F}{\ringaccent{u}} + + \DeclareUnicodeCharacter{0170}{\H{U}} + \DeclareUnicodeCharacter{0171}{\H{u}} + \DeclareUnicodeCharacter{0174}{\^W} + \DeclareUnicodeCharacter{0175}{\^w} + \DeclareUnicodeCharacter{0176}{\^Y} + \DeclareUnicodeCharacter{0177}{\^y} + \DeclareUnicodeCharacter{0178}{\"Y} + \DeclareUnicodeCharacter{0179}{\'Z} + \DeclareUnicodeCharacter{017A}{\'z} + \DeclareUnicodeCharacter{017B}{\dotaccent{Z}} + \DeclareUnicodeCharacter{017C}{\dotaccent{z}} + \DeclareUnicodeCharacter{017D}{\v{Z}} + \DeclareUnicodeCharacter{017E}{\v{z}} + + \DeclareUnicodeCharacter{01C4}{D\v{Z}} + \DeclareUnicodeCharacter{01C5}{D\v{z}} + \DeclareUnicodeCharacter{01C6}{d\v{z}} + \DeclareUnicodeCharacter{01C7}{LJ} + \DeclareUnicodeCharacter{01C8}{Lj} + \DeclareUnicodeCharacter{01C9}{lj} + \DeclareUnicodeCharacter{01CA}{NJ} + \DeclareUnicodeCharacter{01CB}{Nj} + \DeclareUnicodeCharacter{01CC}{nj} + \DeclareUnicodeCharacter{01CD}{\v{A}} + \DeclareUnicodeCharacter{01CE}{\v{a}} + \DeclareUnicodeCharacter{01CF}{\v{I}} + + \DeclareUnicodeCharacter{01D0}{\v{\dotless{i}}} + \DeclareUnicodeCharacter{01D1}{\v{O}} + \DeclareUnicodeCharacter{01D2}{\v{o}} + \DeclareUnicodeCharacter{01D3}{\v{U}} + \DeclareUnicodeCharacter{01D4}{\v{u}} + + \DeclareUnicodeCharacter{01E2}{\={\AE}} + \DeclareUnicodeCharacter{01E3}{\={\ae}} + \DeclareUnicodeCharacter{01E6}{\v{G}} + \DeclareUnicodeCharacter{01E7}{\v{g}} + \DeclareUnicodeCharacter{01E8}{\v{K}} + \DeclareUnicodeCharacter{01E9}{\v{k}} + + \DeclareUnicodeCharacter{01F0}{\v{\dotless{j}}} + \DeclareUnicodeCharacter{01F1}{DZ} + \DeclareUnicodeCharacter{01F2}{Dz} + \DeclareUnicodeCharacter{01F3}{dz} + \DeclareUnicodeCharacter{01F4}{\'G} + \DeclareUnicodeCharacter{01F5}{\'g} + \DeclareUnicodeCharacter{01F8}{\`N} + \DeclareUnicodeCharacter{01F9}{\`n} + \DeclareUnicodeCharacter{01FC}{\'{\AE}} + \DeclareUnicodeCharacter{01FD}{\'{\ae}} + \DeclareUnicodeCharacter{01FE}{\'{\O}} + \DeclareUnicodeCharacter{01FF}{\'{\o}} + + \DeclareUnicodeCharacter{021E}{\v{H}} + \DeclareUnicodeCharacter{021F}{\v{h}} + + \DeclareUnicodeCharacter{0226}{\dotaccent{A}} + \DeclareUnicodeCharacter{0227}{\dotaccent{a}} + \DeclareUnicodeCharacter{0228}{\cedilla{E}} + \DeclareUnicodeCharacter{0229}{\cedilla{e}} + \DeclareUnicodeCharacter{022E}{\dotaccent{O}} + \DeclareUnicodeCharacter{022F}{\dotaccent{o}} + + \DeclareUnicodeCharacter{0232}{\=Y} + \DeclareUnicodeCharacter{0233}{\=y} + \DeclareUnicodeCharacter{0237}{\dotless{j}} + + \DeclareUnicodeCharacter{02DB}{\ogonek{ }} + + \DeclareUnicodeCharacter{1E02}{\dotaccent{B}} + \DeclareUnicodeCharacter{1E03}{\dotaccent{b}} + \DeclareUnicodeCharacter{1E04}{\udotaccent{B}} + \DeclareUnicodeCharacter{1E05}{\udotaccent{b}} + \DeclareUnicodeCharacter{1E06}{\ubaraccent{B}} + \DeclareUnicodeCharacter{1E07}{\ubaraccent{b}} + \DeclareUnicodeCharacter{1E0A}{\dotaccent{D}} + \DeclareUnicodeCharacter{1E0B}{\dotaccent{d}} + \DeclareUnicodeCharacter{1E0C}{\udotaccent{D}} + \DeclareUnicodeCharacter{1E0D}{\udotaccent{d}} + \DeclareUnicodeCharacter{1E0E}{\ubaraccent{D}} + \DeclareUnicodeCharacter{1E0F}{\ubaraccent{d}} + + \DeclareUnicodeCharacter{1E1E}{\dotaccent{F}} + \DeclareUnicodeCharacter{1E1F}{\dotaccent{f}} + + \DeclareUnicodeCharacter{1E20}{\=G} + \DeclareUnicodeCharacter{1E21}{\=g} + \DeclareUnicodeCharacter{1E22}{\dotaccent{H}} + \DeclareUnicodeCharacter{1E23}{\dotaccent{h}} + \DeclareUnicodeCharacter{1E24}{\udotaccent{H}} + \DeclareUnicodeCharacter{1E25}{\udotaccent{h}} + \DeclareUnicodeCharacter{1E26}{\"H} + \DeclareUnicodeCharacter{1E27}{\"h} + + \DeclareUnicodeCharacter{1E30}{\'K} + \DeclareUnicodeCharacter{1E31}{\'k} + \DeclareUnicodeCharacter{1E32}{\udotaccent{K}} + \DeclareUnicodeCharacter{1E33}{\udotaccent{k}} + \DeclareUnicodeCharacter{1E34}{\ubaraccent{K}} + \DeclareUnicodeCharacter{1E35}{\ubaraccent{k}} + \DeclareUnicodeCharacter{1E36}{\udotaccent{L}} + \DeclareUnicodeCharacter{1E37}{\udotaccent{l}} + \DeclareUnicodeCharacter{1E3A}{\ubaraccent{L}} + \DeclareUnicodeCharacter{1E3B}{\ubaraccent{l}} + \DeclareUnicodeCharacter{1E3E}{\'M} + \DeclareUnicodeCharacter{1E3F}{\'m} + + \DeclareUnicodeCharacter{1E40}{\dotaccent{M}} + \DeclareUnicodeCharacter{1E41}{\dotaccent{m}} + \DeclareUnicodeCharacter{1E42}{\udotaccent{M}} + \DeclareUnicodeCharacter{1E43}{\udotaccent{m}} + \DeclareUnicodeCharacter{1E44}{\dotaccent{N}} + \DeclareUnicodeCharacter{1E45}{\dotaccent{n}} + \DeclareUnicodeCharacter{1E46}{\udotaccent{N}} + \DeclareUnicodeCharacter{1E47}{\udotaccent{n}} + \DeclareUnicodeCharacter{1E48}{\ubaraccent{N}} + \DeclareUnicodeCharacter{1E49}{\ubaraccent{n}} + + \DeclareUnicodeCharacter{1E54}{\'P} + \DeclareUnicodeCharacter{1E55}{\'p} + \DeclareUnicodeCharacter{1E56}{\dotaccent{P}} + \DeclareUnicodeCharacter{1E57}{\dotaccent{p}} + \DeclareUnicodeCharacter{1E58}{\dotaccent{R}} + \DeclareUnicodeCharacter{1E59}{\dotaccent{r}} + \DeclareUnicodeCharacter{1E5A}{\udotaccent{R}} + \DeclareUnicodeCharacter{1E5B}{\udotaccent{r}} + \DeclareUnicodeCharacter{1E5E}{\ubaraccent{R}} + \DeclareUnicodeCharacter{1E5F}{\ubaraccent{r}} + + \DeclareUnicodeCharacter{1E60}{\dotaccent{S}} + \DeclareUnicodeCharacter{1E61}{\dotaccent{s}} + \DeclareUnicodeCharacter{1E62}{\udotaccent{S}} + \DeclareUnicodeCharacter{1E63}{\udotaccent{s}} + \DeclareUnicodeCharacter{1E6A}{\dotaccent{T}} + \DeclareUnicodeCharacter{1E6B}{\dotaccent{t}} + \DeclareUnicodeCharacter{1E6C}{\udotaccent{T}} + \DeclareUnicodeCharacter{1E6D}{\udotaccent{t}} + \DeclareUnicodeCharacter{1E6E}{\ubaraccent{T}} + \DeclareUnicodeCharacter{1E6F}{\ubaraccent{t}} + + \DeclareUnicodeCharacter{1E7C}{\~V} + \DeclareUnicodeCharacter{1E7D}{\~v} + \DeclareUnicodeCharacter{1E7E}{\udotaccent{V}} + \DeclareUnicodeCharacter{1E7F}{\udotaccent{v}} + + \DeclareUnicodeCharacter{1E80}{\`W} + \DeclareUnicodeCharacter{1E81}{\`w} + \DeclareUnicodeCharacter{1E82}{\'W} + \DeclareUnicodeCharacter{1E83}{\'w} + \DeclareUnicodeCharacter{1E84}{\"W} + \DeclareUnicodeCharacter{1E85}{\"w} + \DeclareUnicodeCharacter{1E86}{\dotaccent{W}} + \DeclareUnicodeCharacter{1E87}{\dotaccent{w}} + \DeclareUnicodeCharacter{1E88}{\udotaccent{W}} + \DeclareUnicodeCharacter{1E89}{\udotaccent{w}} + \DeclareUnicodeCharacter{1E8A}{\dotaccent{X}} + \DeclareUnicodeCharacter{1E8B}{\dotaccent{x}} + \DeclareUnicodeCharacter{1E8C}{\"X} + \DeclareUnicodeCharacter{1E8D}{\"x} + \DeclareUnicodeCharacter{1E8E}{\dotaccent{Y}} + \DeclareUnicodeCharacter{1E8F}{\dotaccent{y}} + + \DeclareUnicodeCharacter{1E90}{\^Z} + \DeclareUnicodeCharacter{1E91}{\^z} + \DeclareUnicodeCharacter{1E92}{\udotaccent{Z}} + \DeclareUnicodeCharacter{1E93}{\udotaccent{z}} + \DeclareUnicodeCharacter{1E94}{\ubaraccent{Z}} + \DeclareUnicodeCharacter{1E95}{\ubaraccent{z}} + \DeclareUnicodeCharacter{1E96}{\ubaraccent{h}} + \DeclareUnicodeCharacter{1E97}{\"t} + \DeclareUnicodeCharacter{1E98}{\ringaccent{w}} + \DeclareUnicodeCharacter{1E99}{\ringaccent{y}} + + \DeclareUnicodeCharacter{1EA0}{\udotaccent{A}} + \DeclareUnicodeCharacter{1EA1}{\udotaccent{a}} + + \DeclareUnicodeCharacter{1EB8}{\udotaccent{E}} + \DeclareUnicodeCharacter{1EB9}{\udotaccent{e}} + \DeclareUnicodeCharacter{1EBC}{\~E} + \DeclareUnicodeCharacter{1EBD}{\~e} + + \DeclareUnicodeCharacter{1ECA}{\udotaccent{I}} + \DeclareUnicodeCharacter{1ECB}{\udotaccent{i}} + \DeclareUnicodeCharacter{1ECC}{\udotaccent{O}} + \DeclareUnicodeCharacter{1ECD}{\udotaccent{o}} + + \DeclareUnicodeCharacter{1EE4}{\udotaccent{U}} + \DeclareUnicodeCharacter{1EE5}{\udotaccent{u}} + + \DeclareUnicodeCharacter{1EF2}{\`Y} + \DeclareUnicodeCharacter{1EF3}{\`y} + \DeclareUnicodeCharacter{1EF4}{\udotaccent{Y}} + + \DeclareUnicodeCharacter{1EF8}{\~Y} + \DeclareUnicodeCharacter{1EF9}{\~y} + + \DeclareUnicodeCharacter{2013}{--} + \DeclareUnicodeCharacter{2014}{---} + \DeclareUnicodeCharacter{2018}{\quoteleft} + \DeclareUnicodeCharacter{2019}{\quoteright} + \DeclareUnicodeCharacter{201A}{\quotesinglbase} + \DeclareUnicodeCharacter{201C}{\quotedblleft} + \DeclareUnicodeCharacter{201D}{\quotedblright} + \DeclareUnicodeCharacter{201E}{\quotedblbase} + \DeclareUnicodeCharacter{2022}{\bullet} + \DeclareUnicodeCharacter{2026}{\dots} + \DeclareUnicodeCharacter{2039}{\guilsinglleft} + \DeclareUnicodeCharacter{203A}{\guilsinglright} + \DeclareUnicodeCharacter{20AC}{\euro} + + \DeclareUnicodeCharacter{2192}{\expansion} + \DeclareUnicodeCharacter{21D2}{\result} + + \DeclareUnicodeCharacter{2212}{\minus} + \DeclareUnicodeCharacter{2217}{\point} + \DeclareUnicodeCharacter{2261}{\equiv} +}% end of \utfeightchardefs + + +% US-ASCII character definitions. +\def\asciichardefs{% nothing need be done + \relax +} + +% Make non-ASCII characters printable again for compatibility with +% existing Texinfo documents that may use them, even without declaring a +% document encoding. +% +\setnonasciicharscatcode \other + + +\message{formatting,} + +\newdimen\defaultparindent \defaultparindent = 15pt + +\chapheadingskip = 15pt plus 4pt minus 2pt +\secheadingskip = 12pt plus 3pt minus 2pt +\subsecheadingskip = 9pt plus 2pt minus 2pt + +% Prevent underfull vbox error messages. +\vbadness = 10000 + +% Don't be very finicky about underfull hboxes, either. +\hbadness = 6666 + +% Following George Bush, get rid of widows and orphans. +\widowpenalty=10000 +\clubpenalty=10000 + +% Use TeX 3.0's \emergencystretch to help line breaking, but if we're +% using an old version of TeX, don't do anything. We want the amount of +% stretch added to depend on the line length, hence the dependence on +% \hsize. We call this whenever the paper size is set. +% +\def\setemergencystretch{% + \ifx\emergencystretch\thisisundefined + % Allow us to assign to \emergencystretch anyway. + \def\emergencystretch{\dimen0}% + \else + \emergencystretch = .15\hsize + \fi +} + +% Parameters in order: 1) textheight; 2) textwidth; +% 3) voffset; 4) hoffset; 5) binding offset; 6) topskip; +% 7) physical page height; 8) physical page width. +% +% We also call \setleading{\textleading}, so the caller should define +% \textleading. The caller should also set \parskip. +% +\def\internalpagesizes#1#2#3#4#5#6#7#8{% + \voffset = #3\relax + \topskip = #6\relax + \splittopskip = \topskip + % + \vsize = #1\relax + \advance\vsize by \topskip + \outervsize = \vsize + \advance\outervsize by 2\topandbottommargin + \pageheight = \vsize + % + \hsize = #2\relax + \outerhsize = \hsize + \advance\outerhsize by 0.5in + \pagewidth = \hsize + % + \normaloffset = #4\relax + \bindingoffset = #5\relax + % + \ifpdf + \pdfpageheight #7\relax + \pdfpagewidth #8\relax + % if we don't reset these, they will remain at "1 true in" of + % whatever layout pdftex was dumped with. + \pdfhorigin = 1 true in + \pdfvorigin = 1 true in + \fi + % + \setleading{\textleading} + % + \parindent = \defaultparindent + \setemergencystretch +} + +% @letterpaper (the default). +\def\letterpaper{{\globaldefs = 1 + \parskip = 3pt plus 2pt minus 1pt + \textleading = 13.2pt + % + % If page is nothing but text, make it come out even. + \internalpagesizes{607.2pt}{6in}% that's 46 lines + {\voffset}{.25in}% + {\bindingoffset}{36pt}% + {11in}{8.5in}% +}} + +% Use @smallbook to reset parameters for 7x9.25 trim size. +\def\smallbook{{\globaldefs = 1 + \parskip = 2pt plus 1pt + \textleading = 12pt + % + \internalpagesizes{7.5in}{5in}% + {-.2in}{0in}% + {\bindingoffset}{16pt}% + {9.25in}{7in}% + % + \lispnarrowing = 0.3in + \tolerance = 700 + \hfuzz = 1pt + \contentsrightmargin = 0pt + \defbodyindent = .5cm +}} + +% Use @smallerbook to reset parameters for 6x9 trim size. +% (Just testing, parameters still in flux.) +\def\smallerbook{{\globaldefs = 1 + \parskip = 1.5pt plus 1pt + \textleading = 12pt + % + \internalpagesizes{7.4in}{4.8in}% + {-.2in}{-.4in}% + {0pt}{14pt}% + {9in}{6in}% + % + \lispnarrowing = 0.25in + \tolerance = 700 + \hfuzz = 1pt + \contentsrightmargin = 0pt + \defbodyindent = .4cm +}} + +% Use @afourpaper to print on European A4 paper. +\def\afourpaper{{\globaldefs = 1 + \parskip = 3pt plus 2pt minus 1pt + \textleading = 13.2pt + % + % Double-side printing via postscript on Laserjet 4050 + % prints double-sided nicely when \bindingoffset=10mm and \hoffset=-6mm. + % To change the settings for a different printer or situation, adjust + % \normaloffset until the front-side and back-side texts align. Then + % do the same for \bindingoffset. You can set these for testing in + % your texinfo source file like this: + % @tex + % \global\normaloffset = -6mm + % \global\bindingoffset = 10mm + % @end tex + \internalpagesizes{673.2pt}{160mm}% that's 51 lines + {\voffset}{\hoffset}% + {\bindingoffset}{44pt}% + {297mm}{210mm}% + % + \tolerance = 700 + \hfuzz = 1pt + \contentsrightmargin = 0pt + \defbodyindent = 5mm +}} + +% Use @afivepaper to print on European A5 paper. +% From romildo@urano.iceb.ufop.br, 2 July 2000. +% He also recommends making @example and @lisp be small. +\def\afivepaper{{\globaldefs = 1 + \parskip = 2pt plus 1pt minus 0.1pt + \textleading = 12.5pt + % + \internalpagesizes{160mm}{120mm}% + {\voffset}{\hoffset}% + {\bindingoffset}{8pt}% + {210mm}{148mm}% + % + \lispnarrowing = 0.2in + \tolerance = 800 + \hfuzz = 1.2pt + \contentsrightmargin = 0pt + \defbodyindent = 2mm + \tableindent = 12mm +}} + +% A specific text layout, 24x15cm overall, intended for A4 paper. +\def\afourlatex{{\globaldefs = 1 + \afourpaper + \internalpagesizes{237mm}{150mm}% + {\voffset}{4.6mm}% + {\bindingoffset}{7mm}% + {297mm}{210mm}% + % + % Must explicitly reset to 0 because we call \afourpaper. + \globaldefs = 0 +}} + +% Use @afourwide to print on A4 paper in landscape format. +\def\afourwide{{\globaldefs = 1 + \afourpaper + \internalpagesizes{241mm}{165mm}% + {\voffset}{-2.95mm}% + {\bindingoffset}{7mm}% + {297mm}{210mm}% + \globaldefs = 0 +}} + +% @pagesizes TEXTHEIGHT[,TEXTWIDTH] +% Perhaps we should allow setting the margins, \topskip, \parskip, +% and/or leading, also. Or perhaps we should compute them somehow. +% +\parseargdef\pagesizes{\pagesizesyyy #1,,\finish} +\def\pagesizesyyy#1,#2,#3\finish{{% + \setbox0 = \hbox{\ignorespaces #2}\ifdim\wd0 > 0pt \hsize=#2\relax \fi + \globaldefs = 1 + % + \parskip = 3pt plus 2pt minus 1pt + \setleading{\textleading}% + % + \dimen0 = #1\relax + \advance\dimen0 by \voffset + % + \dimen2 = \hsize + \advance\dimen2 by \normaloffset + % + \internalpagesizes{#1}{\hsize}% + {\voffset}{\normaloffset}% + {\bindingoffset}{44pt}% + {\dimen0}{\dimen2}% +}} + +% Set default to letter. +% +\letterpaper + + +\message{and turning on texinfo input format.} + +\def^^L{\par} % remove \outer, so ^L can appear in an @comment + +% DEL is a comment character, in case @c does not suffice. +\catcode`\^^? = 14 + +% Define macros to output various characters with catcode for normal text. +\catcode`\"=\other \def\normaldoublequote{"} +\catcode`\$=\other \def\normaldollar{$}%$ font-lock fix +\catcode`\+=\other \def\normalplus{+} +\catcode`\<=\other \def\normalless{<} +\catcode`\>=\other \def\normalgreater{>} +\catcode`\^=\other \def\normalcaret{^} +\catcode`\_=\other \def\normalunderscore{_} +\catcode`\|=\other \def\normalverticalbar{|} +\catcode`\~=\other \def\normaltilde{~} + +% This macro is used to make a character print one way in \tt +% (where it can probably be output as-is), and another way in other fonts, +% where something hairier probably needs to be done. +% +% #1 is what to print if we are indeed using \tt; #2 is what to print +% otherwise. Since all the Computer Modern typewriter fonts have zero +% interword stretch (and shrink), and it is reasonable to expect all +% typewriter fonts to have this, we can check that font parameter. +% +\def\ifusingtt#1#2{\ifdim \fontdimen3\font=0pt #1\else #2\fi} + +% Same as above, but check for italic font. Actually this also catches +% non-italic slanted fonts since it is impossible to distinguish them from +% italic fonts. But since this is only used by $ and it uses \sl anyway +% this is not a problem. +\def\ifusingit#1#2{\ifdim \fontdimen1\font>0pt #1\else #2\fi} + +% Turn off all special characters except @ +% (and those which the user can use as if they were ordinary). +% Most of these we simply print from the \tt font, but for some, we can +% use math or other variants that look better in normal text. + +\catcode`\"=\active +\def\activedoublequote{{\tt\char34}} +\let"=\activedoublequote +\catcode`\~=\active +\def~{{\tt\char126}} +\chardef\hat=`\^ +\catcode`\^=\active +\def^{{\tt \hat}} + +\catcode`\_=\active +\def_{\ifusingtt\normalunderscore\_} +\let\realunder=_ +% Subroutine for the previous macro. +\def\_{\leavevmode \kern.07em \vbox{\hrule width.3em height.1ex}\kern .07em } + +\catcode`\|=\active +\def|{{\tt\char124}} +\chardef \less=`\< +\catcode`\<=\active +\def<{{\tt \less}} +\chardef \gtr=`\> +\catcode`\>=\active +\def>{{\tt \gtr}} +\catcode`\+=\active +\def+{{\tt \char 43}} +\catcode`\$=\active +\def${\ifusingit{{\sl\$}}\normaldollar}%$ font-lock fix + +% If a .fmt file is being used, characters that might appear in a file +% name cannot be active until we have parsed the command line. +% So turn them off again, and have \everyjob (or @setfilename) turn them on. +% \otherifyactive is called near the end of this file. +\def\otherifyactive{\catcode`+=\other \catcode`\_=\other} + +% Used sometimes to turn off (effectively) the active characters even after +% parsing them. +\def\turnoffactive{% + \normalturnoffactive + \otherbackslash +} + +\catcode`\@=0 + +% \backslashcurfont outputs one backslash character in current font, +% as in \char`\\. +\global\chardef\backslashcurfont=`\\ +\global\let\rawbackslashxx=\backslashcurfont % let existing .??s files work + +% \realbackslash is an actual character `\' with catcode other, and +% \doublebackslash is two of them (for the pdf outlines). +{\catcode`\\=\other @gdef@realbackslash{\} @gdef@doublebackslash{\\}} + +% In texinfo, backslash is an active character; it prints the backslash +% in fixed width font. +\catcode`\\=\active % @ for escape char from now on. + +% The story here is that in math mode, the \char of \backslashcurfont +% ends up printing the roman \ from the math symbol font (because \char +% in math mode uses the \mathcode, and plain.tex sets +% \mathcode`\\="026E). It seems better for @backslashchar{} to always +% print a typewriter backslash, hence we use an explicit \mathchar, +% which is the decimal equivalent of "715c (class 7, e.g., use \fam; +% ignored family value; char position "5C). We can't use " for the +% usual hex value because it has already been made active. +@def@normalbackslash{{@tt @ifmmode @mathchar29020 @else @backslashcurfont @fi}} +@let@backslashchar = @normalbackslash % @backslashchar{} is for user documents. + +% On startup, @fixbackslash assigns: +% @let \ = @normalbackslash +% \rawbackslash defines an active \ to do \backslashcurfont. +% \otherbackslash defines an active \ to be a literal `\' character with +% catcode other. We switch back and forth between these. +@gdef@rawbackslash{@let\=@backslashcurfont} +@gdef@otherbackslash{@let\=@realbackslash} + +% Same as @turnoffactive except outputs \ as {\tt\char`\\} instead of +% the literal character `\'. +% +@def@normalturnoffactive{% + @let"=@normaldoublequote + @let$=@normaldollar %$ font-lock fix + @let+=@normalplus + @let<=@normalless + @let>=@normalgreater + @let\=@normalbackslash + @let^=@normalcaret + @let_=@normalunderscore + @let|=@normalverticalbar + @let~=@normaltilde + @markupsetuplqdefault + @markupsetuprqdefault + @unsepspaces +} + +% Make _ and + \other characters, temporarily. +% This is canceled by @fixbackslash. +@otherifyactive + +% If a .fmt file is being used, we don't want the `\input texinfo' to show up. +% That is what \eatinput is for; after that, the `\' should revert to printing +% a backslash. +% +@gdef@eatinput input texinfo{@fixbackslash} +@global@let\ = @eatinput + +% On the other hand, perhaps the file did not have a `\input texinfo'. Then +% the first `\' in the file would cause an error. This macro tries to fix +% that, assuming it is called before the first `\' could plausibly occur. +% Also turn back on active characters that might appear in the input +% file name, in case not using a pre-dumped format. +% +@gdef@fixbackslash{% + @ifx\@eatinput @let\ = @normalbackslash @fi + @catcode`+=@active + @catcode`@_=@active +} + +% Say @foo, not \foo, in error messages. +@escapechar = `@@ + +% These (along with & and #) are made active for url-breaking, so need +% active definitions as the normal characters. +@def@normaldot{.} +@def@normalquest{?} +@def@normalslash{/} + +% These look ok in all fonts, so just make them not special. +% @hashchar{} gets its own user-level command, because of #line. +@catcode`@& = @other @def@normalamp{&} +@catcode`@# = @other @def@normalhash{#} +@catcode`@% = @other @def@normalpercent{%} + +@let @hashchar = @normalhash + +@c Finally, make ` and ' active, so that txicodequoteundirected and +@c txicodequotebacktick work right in, e.g., @w{@code{`foo'}}. If we +@c don't make ` and ' active, @code will not get them as active chars. +@c Do this last of all since we use ` in the previous @catcode assignments. +@catcode`@'=@active +@catcode`@`=@active +@markupsetuplqdefault +@markupsetuprqdefault + +@c Local variables: +@c eval: (add-hook 'write-file-hooks 'time-stamp) +@c page-delimiter: "^\\\\message" +@c time-stamp-start: "def\\\\texinfoversion{" +@c time-stamp-format: "%:y-%02m-%02d.%02H" +@c time-stamp-end: "}" +@c End: + +@c vim:sw=2: + +@ignore + arch-tag: e1b36e32-c96e-4135-a41a-0b2efa2ea115 +@end ignore From 8dec88b6a6acabf4c2ca4e04bfd5e6164df14e8e Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sat, 13 Apr 2013 22:06:55 +0200 Subject: [PATCH 003/623] Created first example of documentation --- doc/libhttpserver.texi | 309 +++++++++++++++++++++++++++++++++++++++++ doc/version.texi | 4 + 2 files changed, 313 insertions(+) create mode 100644 doc/libhttpserver.texi create mode 100644 doc/version.texi diff --git a/doc/libhttpserver.texi b/doc/libhttpserver.texi new file mode 100644 index 00000000..d5f8b9e4 --- /dev/null +++ b/doc/libhttpserver.texi @@ -0,0 +1,309 @@ +\input texinfo +@setfilename libhttpserver.info +@include version.texi +@settitle The libhttpserver Reference Manual +@c Unify all the indices into concept index. +@syncodeindex fn cp +@syncodeindex vr cp +@syncodeindex ky cp +@syncodeindex pg cp +@syncodeindex tp cp +@copying +This manual is for libhttpserver +(version @value{VERSION}, @value{UPDATED}), C++ library for creating an +embedded Rest HTTP server (and more). + +Copyright @copyright{} 2011--2013 Sebastiano Merlino + +@quotation +Permission is granted to copy, distribute and/or modify this document +under the terms of the GNU Free Documentation License, Version 1.3 +or any later version published by the Free Software Foundation; +with no Invariant Sections, no Front-Cover Texts, and no Back-Cover +Texts. A copy of the license is included in the section entitled "GNU +Free Documentation License". +@end quotation +@end copying + +@dircategory Software libraries +@direntry +* libhttpserver: (libhttpserver). C++ library for creating an +embedded Rest HTTP server (and more). +@end direntry + +@c +@c Titlepage +@c +@titlepage +@title The libhttpserver Reference Manual +@subtitle Version @value{VERSION} +@subtitle @value{UPDATED} +@author Sebastiano Merlino (@email{electrictwister2000@@gmail.com}) +@page +@vskip 0pt plus 1filll +@insertcopying +@end titlepage + +@ifnottex +@node Top +@top The libhttpserver Library +@insertcopying +@end ifnottex + +@c @summarycontents +@c @contents + +@c ------------------------------------------------------------ +@menu +* httpserver-intro:: Introduction. +* httpserver-const:: Constants. +* httpserver-struct:: Structures and classes type definition. +* httpserver-cb:: Callback functions definition. +* httpserver-init:: Create and work with server. +* httpserver-resources:: Registering resources. +* httpserver-responses:: Building responses to requests. +* httpserver-bans:: Whitelists and Blacklists. +* httpserver-comet:: Simple comet semantics. +* httpserver-dauth:: Utilizing Authentication. +* httpserver-info:: Obtaining and modifying status information. + +Appendices + +* GNU-LGPL:: The GNU Lesser General Public License says how you + can copy and share almost all of `libhttpserver'. +* GNU-FDL:: The GNU Free Documentation License says how you + can copy and share the documentation of `libhttpserver'. + +Indices + +* Concept Index:: Index of concepts and programs. +* Function and Data Index:: Index of functions, variables and data types. +* Type Index:: Index of data types. +@end menu + +@c ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +@c ------------------------------------------------------------ +@node httpserver-intro +@chapter Introduction + + +@noindent +libhttpserver is meant to constitute an easy system to build HTTP +servers with REST fashion. +libhttpserver is based on libhttpserver and, like this, it is a +daemon library. +The mission of this library is to support all possible HTTP features +directly and with a simple semantic allowing then the user to concentrate +only on his application and not in HTTP handling details. + +The library is supposed to work transparently for the client Implementing +the business logic and using the library itself to realize an interface. +If the user wants it must be able to change every behavior of the library +itself through the registration of callbacks. + +Like the api is based on (libhttpserver), libhttpserver is able to decode +certain body format a and automatically format them in object oriented +fashion. This is true for query arguments and for @code{POST} and @code{PUT} +requests bodies if @code{application/x-www-form-urlencoded} or +@code{multipart/form-data} header are passed. + +The header reproduce all the constants defined by libhttpserver. +These maps various constant used by the HTTP protocol that are exported +as a convenience for users of the library. Is is possible for the user +to define their own extensions of the HTTP standard and use those with +libhttpserver. + +All functions are guaranteed to be completely reentrant and +thread-safe. +Additionally, clients can specify resource limits on the overall +number of connections, number of connections per IP address and memory +used per connection to avoid resource exhaustion. + +@section Compiling libhttpserver +@cindex compilation +@cindex embedded systems +@cindex portability + +libhttpserver uses the standard system where the usual build process +involves running +@verbatim +$ ./configure +$ make +$ make install +@end verbatim + +@c ------------------------------------------------------------ +@node httpserver-const +@chapter Constants + +@c ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +@c ------------------------------------------------------------ +@node httpserver-struct +@chapter Structures and classes type definition + + +@deftp {CPP Class} http_resource +Represents the resource corresponding to a specific endpoint. +@end deftp + +@deftp {CPP Class} http_request +Represents the request received by the resource that process it. +@end deftp + +@deftp {CPP Class} http_response +Represents the response sent by the server once the resource +finished its work. +@end deftp + +@deftp {CPP Class} webserver +Represents the daemon listening on a socket for HTTP traffic. +@end deftp + + +@c ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +@c ------------------------------------------------------------ +@node httpserver-cb +@chapter Callback functions definition + +Callbacks are functions defined by the client and then used by the library. This way +it is possible to extend the library itself. + +@deftypefn {Function Pointer} void {*log_access_ptr} (const std::string& uri) +Invoked by the library when a new connection is opened by the client. + +@table @var +@item uri +A string containing the uri called by the client and the method it used. +@end table +@end deftypefn + + +@deftypefn {Function Pointer} void {*log_error_ptr} (const std::string& message) +Invoked by the library to send error messages. + +@table @var +@item message +A string containing the message to log. +@end table +@end deftypefn + +@deftypefn {Function Pointer} void {*unescaper_ptr} (char* uri) +Invoked by the library to unescape the uri requested by the client. +If no unescaper_ptr is passed to the webserver it uses the internal defined +unescaper function. + +@table @var +@item uri +A c-style string containing the uri to unescape. +@end table +@end deftypefn + + + +@c ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +@c ------------------------------------------------------------ +@node httpserver-init +@chapter Create and work with server + +@deftypemethod {webserver}{} webserver (int port, const http_utils::start_method_T& start_method, int max_threads, int max_connections, int memory_limit, int connection_timeout, int per_IP_connection_limit, log_access_ptr log_access, log_error_ptr log_error, validator_ptr validator, unescaper_ptr unescaper, const struct sockaddr* bind_address, int bind_socket, int max_thread_stack_size, bool use_ssl, bool use_ipv6, bool debug, bool pedantic, const std::string& https_mem_key, const std::string& https_mem_cert, const std::string& https_mem_trust, const std::string& https_mem_priorities, const http_utils::cred_type_T& cred_type, const std::string digest_auth_random, int nonce_nc_size, const http_utils::policy_T& default_policy, bool basic_auth_enabled, bool digest_auth_enabled, bool regex_checking, bool ban_system_enabled, bool post_process_enabled, render_ptr single_resource, render_ptr not_found_resource, render_ptr method_not_allowed_resource, render_ptr method_not_acceptable_resource, render_ptr internal_error_resource) + +@end deftypemethod + +@c ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +@c ----------------------------------------------------------- +@node httpserver-resources +@chapter Registering resources + + +@c ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +@c ------------------------------------------------------------ +@node httpserver-responses +@chapter Building responses to requests + + +@c ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +@c ------------------------------------------------------------ +@c @node httpserver-response create +@c @section Creating a response object + + + +@c ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +@c ------------------------------------------------------------ +@c @node httpserver-response headers +@c @section Adding headers to a response + +@c ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +@c ------------------------------------------------------------ +@c @node httpserver-response inspect +@c @section Inspecting a response object + +@c ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +@c ------------------------------------------------------------ +@node httpserver-dauth +@chapter Utilizing Authentication + +@c ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +@c ------------------------------------------------------------ +@node httpserver-info +@chapter Obtaining and modifying status information. + +@c ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +@c ------------------------------------------------------------ +@node httpserver-bans +@chapter AAAA + + +@c ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +@c ------------------------------------------------------------ +@node httpserver-comet +@chapter AAAA + + + +@c ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + + +@c ********************************************************** +@c ******************* Appendices ************************* +@c ********************************************************** + +@node GNU-LGPL +@unnumbered GNU-LGPL +@cindex license +@include lgpl.texi + +@node GNU-FDL +@unnumbered GNU-FDL +@cindex license +@include fdl-1.3.texi + +@node Concept Index +@unnumbered Concept Index + +@printindex cp + +@node Function and Data Index +@unnumbered Function and Data Index + +@printindex fn + +@node Type Index +@unnumbered Type Index + +@printindex tp + +@bye diff --git a/doc/version.texi b/doc/version.texi new file mode 100644 index 00000000..f0360c0f --- /dev/null +++ b/doc/version.texi @@ -0,0 +1,4 @@ +@set UPDATED 13 April 2013 +@set UPDATED-MONTH April 2013 +@set EDITION 0.6.3 +@set VERSION 0.6.3 From 5cf765602aeff0ed2c4bef34f188f0024314a18d Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sat, 13 Apr 2013 22:30:14 +0200 Subject: [PATCH 004/623] Solved travis problems --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index e0dd6c4a..3a88a5c3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,6 +2,7 @@ language: cpp compiler: gcc # Change this to your needs before_install: + - sudo apt-get install makeinfo - wget http://199.231.187.83/resources/libmicrohttpd-0.9.20.tar.gz - tar -xvzf libmicrohttpd-0.9.20.tar.gz - cd libmicrohttpd-0.9.20 From 261948900c0f7a222d8704bf27a52ddcdbe3f7cf Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sat, 13 Apr 2013 22:34:46 +0200 Subject: [PATCH 005/623] Changed makeinfo to texinfo --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 3a88a5c3..7974dcaa 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,7 +2,7 @@ language: cpp compiler: gcc # Change this to your needs before_install: - - sudo apt-get install makeinfo + - sudo apt-get install texinfo - wget http://199.231.187.83/resources/libmicrohttpd-0.9.20.tar.gz - tar -xvzf libmicrohttpd-0.9.20.tar.gz - cd libmicrohttpd-0.9.20 From 7b200dc94fdf4abb26bb49e4825d0c53c05dfdda Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Tue, 8 Oct 2013 23:04:29 +0200 Subject: [PATCH 006/623] Removed extraction of the url at each loop The url was recalculated at each loop. This was unuseful and really expensive especially in presence of a big quantity of data to send or receive. --- examples/Test.cpp | 2 +- src/httpserver/webserver.hpp | 91 ++++++++++++++-------------- src/webserver.cpp | 112 +++++++++++++++++------------------ 3 files changed, 99 insertions(+), 106 deletions(-) diff --git a/examples/Test.cpp b/examples/Test.cpp index a204c517..dbcafe06 100644 --- a/examples/Test.cpp +++ b/examples/Test.cpp @@ -96,7 +96,7 @@ void Test::render_PUT(const http_request& r, http_response** res) int main() { // signal(SIGINT, &signal_callback_handler); - webserver ws = create_webserver(8080).max_threads(5); + webserver ws = create_webserver(8080)/*.max_threads(5)*/; ws_ptr = &ws; Test dt = Test(); Test2 dt2 = Test2(); diff --git a/src/httpserver/webserver.hpp b/src/httpserver/webserver.hpp index bf767455..efcc58e9 100644 --- a/src/httpserver/webserver.hpp +++ b/src/httpserver/webserver.hpp @@ -14,7 +14,7 @@ You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -140,12 +140,12 @@ namespace details template http_resource_mirror(http_resource* res): render( - HAS_METHOD(render, T, void, + HAS_METHOD(render, T, void, const http_request&, http_response** ) ? functor(res, &T::render) : functor(&empty_render) ), render_GET( - HAS_METHOD(render_GET, T, void, + HAS_METHOD(render_GET, T, void, const http_request&, http_response** ) ? functor(res, &T::render_GET) : ( @@ -155,7 +155,7 @@ namespace details ) ), render_POST( - HAS_METHOD(render_POST, T, void, + HAS_METHOD(render_POST, T, void, const http_request&, http_response** ) ? functor(res, &T::render_POST) : ( @@ -165,7 +165,7 @@ namespace details ) ), render_PUT( - HAS_METHOD(render_PUT, T, void, + HAS_METHOD(render_PUT, T, void, const http_request&, http_response** ) ? functor(res, &T::render_PUT) : ( @@ -175,7 +175,7 @@ namespace details ) ), render_HEAD( - HAS_METHOD(render_HEAD, T, void, + HAS_METHOD(render_HEAD, T, void, const http_request&, http_response** ) ? functor(res, &T::render_HEAD) : ( @@ -185,7 +185,7 @@ namespace details ) ), render_DELETE( - HAS_METHOD(render_DELETE, T, void, + HAS_METHOD(render_DELETE, T, void, const http_request&, http_response** ) ? functor(res, &T::render_DELETE) : ( @@ -195,7 +195,7 @@ namespace details ) ), render_TRACE( - HAS_METHOD(render_TRACE, T, void, + HAS_METHOD(render_TRACE, T, void, const http_request&, http_response** ) ? functor(res, &T::render_TRACE) : ( @@ -205,7 +205,7 @@ namespace details ) ), render_OPTIONS( - HAS_METHOD(render_OPTIONS, T, void, + HAS_METHOD(render_OPTIONS, T, void, const http_request&, http_response** ) ? functor(res, &T::render_OPTIONS) : ( @@ -215,7 +215,7 @@ namespace details ) ), render_CONNECT( - HAS_METHOD(render_CONNECT, T, void, + HAS_METHOD(render_CONNECT, T, void, const http_request&, http_response** ) ? functor(res, &T::render_CONNECT) : ( @@ -235,9 +235,9 @@ namespace details { private: typedef void(*supply_events_ptr)( - fd_set*, - fd_set*, - fd_set*, + fd_set*, + fd_set*, + fd_set*, int* ); @@ -248,9 +248,9 @@ namespace details dispatch_events_ptr dispatch_events; event_tuple(); - + friend class ::httpserver::webserver; - public: + public: template event_tuple(event_supplier* es): supply_events(std::bind1st(std::mem_fun(&T::supply_events),es)), @@ -282,9 +282,9 @@ class event_supplier } void supply_events( - fd_set* read_fdset, - fd_set* write_fdset, - fd_set* exc_fdset, + fd_set* read_fdset, + fd_set* write_fdset, + fd_set* exc_fdset, int* max ) const { @@ -313,7 +313,7 @@ typedef void(*log_error_ptr)(const std::string&); /** * Class representing the webserver. Main class of the apis. **/ -class webserver +class webserver { public: /** @@ -338,9 +338,9 @@ class webserver **/ explicit webserver ( - int port = DEFAULT_WS_PORT, + int port = DEFAULT_WS_PORT, const http_utils::start_method_T& start_method = http_utils::INTERNAL_SELECT, - int max_threads = 0, + int max_threads = 0, int max_connections = 0, int memory_limit = 0, int connection_timeout = DEFAULT_WS_TIMEOUT, @@ -425,8 +425,8 @@ class webserver void send_message_to_consumer(const httpserver_ska& connection_id, const std::string& message, bool to_lock = true ); - void register_to_topics(const std::vector& topics, - const httpserver_ska& connection_id, int keepalive_secs = -1, + void register_to_topics(const std::vector& topics, + const httpserver_ska& connection_id, int keepalive_secs = -1, std::string keepalive_msg = "" ); size_t read_message(const httpserver_ska& connection_id, @@ -462,7 +462,7 @@ class webserver { return this->log_error; } - + void set_access_logger(log_access_ptr log_access) { this->log_access = log_access; @@ -602,16 +602,16 @@ class webserver ); void not_found_page(http_response** dhrs, details::modded_request* mr); - static int method_not_acceptable_page + static int method_not_acceptable_page ( const void *cls, struct MHD_Connection *connection ); - static void request_completed(void *cls, - struct MHD_Connection *connection, void **con_cls, + static void request_completed(void *cls, + struct MHD_Connection *connection, void **con_cls, enum MHD_RequestTerminationCode toe ); - static int build_request_header (void *cls, enum MHD_ValueKind kind, + static int build_request_header (void *cls, enum MHD_ValueKind kind, const char *key, const char *value ); static int build_request_footer (void *cls, enum MHD_ValueKind kind, @@ -630,7 +630,7 @@ class webserver const char* version, const char* upload_data, size_t* upload_data_size, void** con_cls ); - static int post_iterator + static int post_iterator ( void *cls, enum MHD_ValueKind kind, @@ -640,9 +640,9 @@ class webserver const char *transfer_encoding, const char *data, uint64_t off, size_t size ); - static void upgrade_handler + static void upgrade_handler ( - void *cls, + void *cls, struct MHD_Connection* connection, void **con_cls, int upgrade_socket ); @@ -652,8 +652,8 @@ class webserver static void get_response(cache_entry*, http_response** res); int bodyless_requests_answer(MHD_Connection* connection, - const char* url, const char* method, - const char* version, struct details::modded_request* mr + const char* method, const char* version, + struct details::modded_request* mr ); int bodyfull_requests_answer_first_step(MHD_Connection* connection, @@ -661,25 +661,22 @@ class webserver ); int bodyfull_requests_answer_second_step(MHD_Connection* connection, - const char* url, const char* method, - const char* version, const char* upload_data, + const char* method, const char* version, const char* upload_data, size_t* upload_data_size, struct details::modded_request* mr ); - void end_request_construction(MHD_Connection* connection, - struct details::modded_request* mr, const char* version, - const char* st_url, const char* method, - char* user, char* pass, char* digested_user + void end_request_construction(MHD_Connection* connection, + struct details::modded_request* mr, const char* version, + const char* method, char* user, char* pass, char* digested_user ); - int finalize_answer(MHD_Connection* connection, - struct details::modded_request* mr, const char* st_url, - const char* method + int finalize_answer(MHD_Connection* connection, + struct details::modded_request* mr, const char* method ); - int complete_request(MHD_Connection* connection, - struct details::modded_request* mr, const char* version, - const char* st_url, const char* method + int complete_request(MHD_Connection* connection, + struct details::modded_request* mr, + const char* version, const char* method ); bool use_internal_select() @@ -687,7 +684,7 @@ class webserver return this->start_method == http_utils::INTERNAL_SELECT; } - friend int policy_callback (void *cls, + friend int policy_callback (void *cls, const struct sockaddr* addr, socklen_t addrlen ); friend void error_log(void* cls, const char* fmt, va_list ap); @@ -954,7 +951,7 @@ class create_webserver render_ptr internal_error_resource ) { - _internal_error_resource = internal_error_resource; return *this; + _internal_error_resource = internal_error_resource; return *this; } private: diff --git a/src/webserver.cpp b/src/webserver.cpp index 8a677e98..dbfc0046 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -14,7 +14,7 @@ You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -153,6 +153,7 @@ struct modded_request { struct MHD_PostProcessor *pp; std::string* complete_uri; + std::string* standardized_url; webserver* ws; const binders::functor_two< @@ -166,6 +167,7 @@ struct modded_request modded_request(): pp(0x0), complete_uri(0x0), + standardized_url(0x0), ws(0x0), dhr(0x0), dhrs(0x0), @@ -174,13 +176,14 @@ struct modded_request } ~modded_request() { - if (NULL != pp) + if (NULL != pp) { MHD_destroy_post_processor (pp); } if(second) delete dhr; //TODO: verify. It could be an error delete complete_uri; + delete standardized_url; } }; @@ -264,7 +267,7 @@ struct cache_entry pthread_rwlock_init(&elem_guard, NULL); pthread_mutex_init(&lock_guard, NULL); } - + void lock(bool write = false) { pthread_mutex_lock(&lock_guard); @@ -377,11 +380,11 @@ create_webserver& create_webserver::https_mem_trust( } //WEBSERVER -webserver::webserver +webserver::webserver ( - int port, + int port, const http_utils::start_method_T& start_method, - int max_threads, + int max_threads, int max_connections, int memory_limit, int connection_timeout, @@ -417,9 +420,9 @@ webserver::webserver render_ptr internal_error_resource ) : - port(port), + port(port), start_method(start_method), - max_threads(max_threads), + max_threads(max_threads), max_connections(max_connections), memory_limit(memory_limit), connection_timeout(connection_timeout), @@ -555,7 +558,7 @@ void webserver::request_completed ( } pthread_rwlock_unlock(&mr->ws->comet_guard); - if (0x0 == mr) + if (0x0 == mr) { if(mr->dhrs.res != 0x0 && mr->dhrs->ca != 0x0) mr->dhrs->ca(mr->dhrs->closure_data); @@ -570,7 +573,7 @@ void webserver::register_resource( ) { if(method_not_acceptable_resource) - hrm.method_not_acceptable_resource = method_not_acceptable_resource; + hrm.method_not_acceptable_resource = method_not_acceptable_resource; details::http_endpoint idx(resource, family, true, regex_checking); @@ -630,8 +633,8 @@ void* webserver::select(void* self) max = local_max; struct timeval t = (*it).second.get_timeout(); - if((unsigned MHD_LONG_LONG) t.tv_sec < timeout_secs || - ((unsigned MHD_LONG_LONG) t.tv_sec == timeout_secs + if((unsigned MHD_LONG_LONG) t.tv_sec < timeout_secs || + ((unsigned MHD_LONG_LONG) t.tv_sec == timeout_secs && (unsigned MHD_LONG_LONG) t.tv_usec < timeout_microsecs ) ) @@ -660,7 +663,7 @@ void* webserver::select(void* self) ); else { - unsigned MHD_LONG_LONG to_wait_time = + unsigned MHD_LONG_LONG to_wait_time = di->ws->q_keepalives_mem[(*it).first].first - waited_time; if(to_wait_time < timeout_secs) @@ -728,8 +731,8 @@ bool webserver::start(bool blocking) vector iov; iov.push_back(gen(MHD_OPTION_NOTIFY_COMPLETED, - (intptr_t) &request_completed, - NULL + (intptr_t) &request_completed, + NULL )); iov.push_back(gen(MHD_OPTION_URI_LOG_CALLBACK, (intptr_t) &uri_log, this)); iov.push_back(gen(MHD_OPTION_EXTERNAL_LOGGER, (intptr_t) &error_log, this)); @@ -834,7 +837,7 @@ bool webserver::start(bool blocking) if (use_ipv6) bind_socket = create_socket (PF_INET6, SOCK_STREAM, 0); - else + else bind_socket = create_socket (PF_INET, SOCK_STREAM, 0); setsockopt (bind_socket, @@ -846,8 +849,8 @@ bool webserver::start(bool blocking) { #ifdef IPPROTO_IPV6 #ifdef IPV6_V6ONLY - setsockopt (bind_socket, - IPPROTO_IPV6, IPV6_V6ONLY, + setsockopt (bind_socket, + IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof (on) ); #endif @@ -924,12 +927,12 @@ bool webserver::start(bool blocking) struct MHD_Daemon* daemon = MHD_start_daemon ( start_conf, this->port, &policy_callback, this, - &answer_to_connection, this, MHD_OPTION_ARRAY, + &answer_to_connection, this, MHD_OPTION_ARRAY, ops, MHD_OPTION_END ); if(NULL == daemon) { - cout << gettext("Unable to connect daemon to port: ") << + cout << gettext("Unable to connect daemon to port: ") << this->port << endl; abort(); } @@ -1090,8 +1093,8 @@ int policy_callback (void *cls, const struct sockaddr* addr, socklen_t addrlen) { if((static_cast(cls))->ban_system_enabled) { - if((((static_cast(cls))->default_policy == http_utils::ACCEPT) && - ((static_cast(cls))->bans.count(addr)) && + if((((static_cast(cls))->default_policy == http_utils::ACCEPT) && + ((static_cast(cls))->bans.count(addr)) && (!(static_cast(cls))->allowances.count(addr)) ) || (((static_cast(cls))->default_policy == http_utils::REJECT) @@ -1174,7 +1177,7 @@ void webserver::not_found_page( not_found_resource(*mr->dhr, dhrs); else *dhrs = new http_string_response( - NOT_FOUND_ERROR, + NOT_FOUND_ERROR, http_utils::http_not_found ); } @@ -1189,8 +1192,8 @@ int webserver::method_not_acceptable_page (const void *cls, response = MHD_create_response_from_buffer (strlen (NOT_METHOD_ERROR), (void *) NOT_METHOD_ERROR, MHD_RESPMEM_PERSISTENT); - ret = MHD_queue_response (connection, - MHD_HTTP_METHOD_NOT_ACCEPTABLE, + ret = MHD_queue_response (connection, + MHD_HTTP_METHOD_NOT_ACCEPTABLE, response); MHD_add_response_header (response, MHD_HTTP_HEADER_CONTENT_ENCODING, @@ -1229,18 +1232,14 @@ void webserver::internal_error_page( ); } -int webserver::bodyless_requests_answer(MHD_Connection* connection, - const char* url, const char* method, +int webserver::bodyless_requests_answer( + MHD_Connection* connection, const char* method, const char* version, struct details::modded_request* mr ) { - string st_url; - internal_unescaper((void*) this, (char*) url); - http_utils::standardize_url(url, st_url); http_request req; mr->dhr = &(req); - - return complete_request(connection, mr, version, st_url.c_str(), method); + return complete_request(connection, mr, version, method); } int webserver::bodyfull_requests_answer_first_step( @@ -1258,16 +1257,16 @@ int webserver::bodyfull_requests_answer_first_step( if(encoding != 0x0) mr->dhr->set_header(http_utils::http_header_content_type, encoding); if ( post_process_enabled && - ( - 0x0 != encoding && + ( + 0x0 != encoding && ((0 == strncasecmp ( - MHD_HTTP_POST_ENCODING_FORM_URLENCODED, - encoding, + MHD_HTTP_POST_ENCODING_FORM_URLENCODED, + encoding, strlen (MHD_HTTP_POST_ENCODING_FORM_URLENCODED) ) )) ) - ) + ) { mr->pp = MHD_create_post_processor ( connection, @@ -1275,23 +1274,20 @@ int webserver::bodyfull_requests_answer_first_step( &post_iterator, mr ); - } - else + } + else { mr->pp = NULL; } return MHD_YES; } -int webserver::bodyfull_requests_answer_second_step(MHD_Connection* connection, - const char* url, const char* method, +int webserver::bodyfull_requests_answer_second_step( + MHD_Connection* connection, const char* method, const char* version, const char* upload_data, size_t* upload_data_size, struct details::modded_request* mr ) { - string st_url; - internal_unescaper((void*) this, (char*) url); - http_utils::standardize_url(url, st_url); if ( 0 != *upload_data_size) { #ifdef DEBUG @@ -1306,14 +1302,13 @@ int webserver::bodyfull_requests_answer_second_step(MHD_Connection* connection, return MHD_YES; } - return complete_request(connection, mr, version, st_url.c_str(), method); + return complete_request(connection, mr, version, method); } void webserver::end_request_construction( MHD_Connection* connection, struct details::modded_request* mr, const char* version, - const char* st_url, const char* method, char* user, char* pass, @@ -1346,7 +1341,7 @@ void webserver::end_request_construction( (void*) mr->dhr ); - mr->dhr->set_path(st_url); + mr->dhr->set_path(mr->standardized_url->c_str()); mr->dhr->set_method(method); if(basic_auth_enabled) @@ -1378,7 +1373,6 @@ void webserver::end_request_construction( int webserver::finalize_answer( MHD_Connection* connection, struct details::modded_request* mr, - const char* st_url, const char* method ) { @@ -1393,6 +1387,7 @@ int webserver::finalize_answer( struct MHD_Response* raw_response; if(!single_resource) { + const char* st_url = mr->standardized_url->c_str(); fe = registered_resources_str.find(st_url); if(fe == registered_resources_str.end()) { @@ -1422,7 +1417,7 @@ int webserver::finalize_answer( { int endpoint_pieces_len = (*it).first.get_url_pieces_num(); int endpoint_tot_len = (*it).first.get_url_complete_size(); - if(tot_len == -1 || + if(tot_len == -1 || len == -1 || endpoint_pieces_len > len || ( @@ -1440,7 +1435,7 @@ int webserver::finalize_answer( } } } - if(found) + if(found) { vector url_pars; @@ -1451,7 +1446,7 @@ int webserver::finalize_answer( endpoint.get_url_pieces(url_pieces); vector chunkes; found_endpoint->first.get_chunk_positions(chunkes); - for(unsigned int i = 0; i < pars_size; i++) + for(unsigned int i = 0; i < pars_size; i++) { mr->dhr->set_arg(url_pars[i], url_pieces[chunkes[i]]); } @@ -1536,7 +1531,6 @@ int webserver::complete_request( MHD_Connection* connection, struct details::modded_request* mr, const char* version, - const char* st_url, const char* method ) { @@ -1548,14 +1542,13 @@ int webserver::complete_request( connection, mr, version, - st_url, method, pass, user, digested_user ); - int to_ret = finalize_answer(connection, mr, st_url, method); + int to_ret = finalize_answer(connection, mr, method); if (user != 0x0) free (user); @@ -1573,11 +1566,15 @@ int webserver::answer_to_connection(void* cls, MHD_Connection* connection, size_t* upload_data_size, void** con_cls ) { - struct details::modded_request* mr = + struct details::modded_request* mr = static_cast(*con_cls); if(mr->second == false) { + mr->standardized_url = new string(); + internal_unescaper((void*) static_cast(cls), (char*) url); + http_utils::standardize_url(url, *mr->standardized_url); + bool body = false; access_log( @@ -1637,14 +1634,13 @@ int webserver::answer_to_connection(void* cls, MHD_Connection* connection, bodyfull_requests_answer_first_step(connection, mr); else return static_cast(cls)-> - bodyless_requests_answer(connection, url, method, version, mr); + bodyless_requests_answer(connection, method, version, mr); } else { return static_cast(cls)-> bodyfull_requests_answer_second_step( connection, - url, method, version, upload_data, @@ -1827,7 +1823,7 @@ bool webserver::pop_signaled(const httpserver_ska& consumer) if(rslt == ETIMEDOUT) { pthread_rwlock_wrlock(&comet_guard); - send_message_to_consumer(consumer, + send_message_to_consumer(consumer, q_keepalives_mem[consumer].second, false ); pthread_rwlock_unlock(&comet_guard); From fb7e8c41500aaa1879d2e116cf5181cffa47b3dd Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Tue, 8 Oct 2013 23:54:21 +0200 Subject: [PATCH 007/623] Update README Added travis status to the readme --- README | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README b/README index 62929a6a..fe183050 100644 --- a/README +++ b/README @@ -47,3 +47,5 @@ For further information: visit our website www.zencoders.org Author: Sebastiano Merlino + +[![Build Status](https://travis-ci.org/etr/libhttpserver.png?branch=master)](https://travis-ci.org/etr/libhttpserver) From 50072ba7474f4437ff06923c2e30bee699902cc2 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Tue, 8 Oct 2013 23:54:59 +0200 Subject: [PATCH 008/623] Rename README to README.md moved readme to markdown --- README => README.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename README => README.md (100%) diff --git a/README b/README.md similarity index 100% rename from README rename to README.md From 51ac8cfa56cefe16f04e953c32a406761033f6cd Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Tue, 8 Oct 2013 23:58:01 +0200 Subject: [PATCH 009/623] Update README.md Moved to verbatim to solve layout problems on zencoders tree in readme.md --- README.md | 84 +++++++++++++++++++++++++++---------------------------- 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/README.md b/README.md index fe183050..2de44477 100644 --- a/README.md +++ b/README.md @@ -1,47 +1,47 @@ ================================= libhttpserver =================================== - When you see this tree, know that you've came across ZenCoders.org - - with open('ZenCoders. - `num` in numbers synchronized - datetime d glob. sys.argv[2] . - def myclass `..` @@oscla org. . class { - displ hooks( public static void ma functor: - $myclass->method( impport sys, os.pipe ` @param name` - fcl if(system(cmd) myc. /de ` $card( array("a" srand - format lists: ++: conc ++ "my an WHERE for( == myi - `sys: myvalue(myvalue) sys.t Console.W try{ rais using - connec SELECT * FROM table mycnf acco desc and or selector::clas at - openldap string sys. print "zenc der " { 'a': `ls -l` > appe &firs - import Tkinter paste( $obh &a or it myval bro roll: :: [] require a - case `` super. +y expr say " %rooms 1 --account fb- yy - proc meth Animate => send(D, open) putd EndIf 10 whi myc` cont - and main (--) import loop $$ or end onload UNION WITH tab timer 150 *2 - end. begin True GtkLabel *label doto partition te let auto i<- (i + d ); -.mushup ``/. ^/zenc/ myclass->her flv op <> element >> 71 or -QFileDi : and .. with myc toA channel::bo myc isEmpty a not bodt; -class T public pol str mycalc d pt &&a *i fc add ^ac -::ZenCoders::core::namespac boost::function st f = std: ;; int assert -cout << endl public genera #include "b ost ::ac myna const cast mys -ac size_t return ran int (*getNextValue)(void) ff double sa_family_t famil -pu a do puts(" ac int main(int argc, char* "%5d struct nam -cs float for typedef enum puts getchar() -if( else #define fp FILE* f char* s - i++ strcat( %s int - 31] total+= do - }do while(1) sle - getc strcpy( a for - prin scanf(%d, & get - int void myfunc(int pa retu - BEQ BNEQZ R1 10 ANDI R1 R2 SYS - XOR SYSCALL 5 SLTIU MFLO 15 SW JAL - BNE BLTZAL R1 1 LUI 001 NOOP MULTU SLLV - MOV R1 ADD R1 R2 JUMP 10 1001 BEQ R1 R2 1 ANDI - 1101 1010001100 111 001 01 1010 101100 1001 100 - 110110 100 0 01 101 01100 100 100 1000100011 - 11101001001 00 11 100 11 10100010 - 000101001001 10 1001 101000101 - 010010010010110101001010 + When you see this tree, know that you've came across ZenCoders.org + + with open('ZenCoders. + `num` in numbers synchronized + datetime d glob. sys.argv[2] . + def myclass `..` @@oscla org. . class { + displ hooks( public static void ma functor: + $myclass->method( impport sys, os.pipe ` @param name` + fcl if(system(cmd) myc. /de ` $card( array("a" srand + format lists: ++: conc ++ "my an WHERE for( == myi + `sys: myvalue(myvalue) sys.t Console.W try{ rais using + connec SELECT * FROM table mycnf acco desc and or selector::clas at + openldap string sys. print "zenc der " { 'a': `ls -l` > appe &firs + import Tkinter paste( $obh &a or it myval bro roll: :: [] require a + case `` super. +y expr say " %rooms 1 --account fb- yy + proc meth Animate => send(D, open) putd EndIf 10 whi myc` cont + and main (--) import loop $$ or end onload UNION WITH tab timer 150 *2 + end. begin True GtkLabel *label doto partition te let auto i<- (i + d ); + .mushup ``/. ^/zenc/ myclass->her flv op <> element >> 71 or + QFileDi : and .. with myc toA channel::bo myc isEmpty a not bodt; + class T public pol str mycalc d pt &&a *i fc add ^ac + ::ZenCoders::core::namespac boost::function st f = std: ;; int assert + cout << endl public genera #include "b ost ::ac myna const cast mys + ac size_t return ran int (*getNextValue)(void) ff double sa_family_t famil + pu a do puts(" ac int main(int argc, char* "%5d struct nam + cs float for typedef enum puts getchar() + if( else #define fp FILE* f char* s + i++ strcat( %s int + 31] total+= do + }do while(1) sle + getc strcpy( a for + prin scanf(%d, & get + int void myfunc(int pa retu + BEQ BNEQZ R1 10 ANDI R1 R2 SYS + XOR SYSCALL 5 SLTIU MFLO 15 SW JAL + BNE BLTZAL R1 1 LUI 001 NOOP MULTU SLLV + MOV R1 ADD R1 R2 JUMP 10 1001 BEQ R1 R2 1 ANDI + 1101 1010001100 111 001 01 1010 101100 1001 100 + 110110 100 0 01 101 01100 100 100 1000100011 + 11101001001 00 11 100 11 10100010 + 000101001001 10 1001 101000101 + 010010010010110101001010 For further information: visit our website www.zencoders.org From c9db9b3580aa56ee30ec9a05a4ab6405594508b8 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Fri, 25 Oct 2013 00:19:55 +0200 Subject: [PATCH 010/623] Enhanced the level of detail of hello_world example --- examples/hello_world.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/examples/hello_world.cpp b/examples/hello_world.cpp index bbe88282..5afac6e1 100755 --- a/examples/hello_world.cpp +++ b/examples/hello_world.cpp @@ -1,15 +1,25 @@ #include +#include using namespace httpserver; class hello_world_resource : public http_resource { public: void render(const http_request&, http_response**); + void set_some_data(const std::string &s) {data = s;} + std::string data; }; //using the render method you are able to catch each type of request you receive void hello_world_resource::render(const http_request& req, http_response** res) { + //it is possible to store data inside the resource object that can be altered + //through the requests + std::cout << "Data was: " << data << std::endl; + std::string datapar = req.get_arg("data"); + set_some_data(datapar == "" ? "no data passed!!!" : datapar); + std::cout << "Now data is:" << data << std::endl; + //it is possible to send a response initializing an http_string_response //that reads the content to send in response from a string. *res = new http_string_response("Hello World!!!", 200); From f9b76910643580f5f740a44be08dd6cac0175b87 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Fri, 29 Nov 2013 01:53:37 +0100 Subject: [PATCH 011/623] Use MHD_create_response_from_fd for files Now the library makes use of MHD_create_response_from_fd for files. This improves the performances because allow to use sendfile on the socket. --- src/http_response.cpp | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/http_response.cpp b/src/http_response.cpp index 7b019531..115d6996 100644 --- a/src/http_response.cpp +++ b/src/http_response.cpp @@ -14,7 +14,7 @@ You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -22,6 +22,8 @@ #include #include #include +#include +#include #include "http_utils.hpp" #include "webserver.hpp" #include "http_response.hpp" @@ -155,20 +157,20 @@ void http_response::get_raw_response_file( webserver* ws ) { - char* page = NULL; - size_t size = http::load_file(filename.c_str(), &page); + int fd = open(filename.c_str(), O_RDONLY); + size_t size = lseek(fd, 0, SEEK_END); if(size) - *response = MHD_create_response_from_buffer( - size, - page, - MHD_RESPMEM_MUST_FREE - ); + { + *response = MHD_create_response_from_fd(size, fd); + } else + { *response = MHD_create_response_from_buffer( - size, + 0, (void*) "", MHD_RESPMEM_PERSISTENT ); + } } void http_response::get_raw_response_cache( @@ -184,7 +186,7 @@ void http_response::get_raw_response_cache( webserver::get_response(ce, &r); r->get_raw_response(response, ws); r->decorate_response(*response); //It is done here to avoid to search two times for the same element - + //TODO: Check if element is not in cache and throw exception } @@ -253,7 +255,7 @@ ssize_t long_polling_receive_response::data_generator( size_t max ) { - long_polling_receive_response* _this = + long_polling_receive_response* _this = static_cast(cls); if(_this->ws->pop_signaled(_this->connection_id)) From 8197d6c2d5629bea4212e58f48d296e73bf61306 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Fri, 29 Nov 2013 02:01:16 +0100 Subject: [PATCH 012/623] Use AC_SYS_LARGEFILE in configure.ac This allow to avoid problems when working with FDs on 64bit systems --- configure.ac | 1 + 1 file changed, 1 insertion(+) diff --git a/configure.ac b/configure.ac index fbf8eec2..ed2fa5c6 100644 --- a/configure.ac +++ b/configure.ac @@ -37,6 +37,7 @@ AC_PROG_CXX AC_PROG_LN_S CXXFLAGS=$OLD_CXXFLAGS AC_LANG([C++]) +AC_SYS_LARGEFILE if test "`cd $srcdir; /bin/pwd`" = "`/bin/pwd`"; then AC_MSG_ERROR("you must configure in a separate build directory") From 9f15463077b715cacc8dc148eca36cbccc396004 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Fri, 29 Nov 2013 02:03:02 +0100 Subject: [PATCH 013/623] Avoid memory leak Avoid a memory leak due to a typo (this is embarassing) in response completion. --- src/webserver.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/webserver.cpp b/src/webserver.cpp index dbfc0046..df96c1f2 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -558,11 +558,12 @@ void webserver::request_completed ( } pthread_rwlock_unlock(&mr->ws->comet_guard); - if (0x0 == mr) + if (0x0 != mr) { if(mr->dhrs.res != 0x0 && mr->dhrs->ca != 0x0) mr->dhrs->ca(mr->dhrs->closure_data); delete mr; + mr = 0x0; } } From 2cfc155427a44d8326ecae9e42f17544aa4f71ef Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 12 Jan 2014 18:32:02 +0100 Subject: [PATCH 014/623] updated errors in doc introduction --- doc/libhttpserver.texi | 9 ++++++--- doc/version.texi | 4 ++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/doc/libhttpserver.texi b/doc/libhttpserver.texi index d5f8b9e4..65930e87 100644 --- a/doc/libhttpserver.texi +++ b/doc/libhttpserver.texi @@ -91,18 +91,18 @@ Indices @noindent libhttpserver is meant to constitute an easy system to build HTTP servers with REST fashion. -libhttpserver is based on libhttpserver and, like this, it is a +libhttpserver is based on libmicrohttpd and, like this, it is a daemon library. The mission of this library is to support all possible HTTP features directly and with a simple semantic allowing then the user to concentrate -only on his application and not in HTTP handling details. +only on his application and not on HTTP request handling details. The library is supposed to work transparently for the client Implementing the business logic and using the library itself to realize an interface. If the user wants it must be able to change every behavior of the library itself through the registration of callbacks. -Like the api is based on (libhttpserver), libhttpserver is able to decode +Like the api is based on (libmicrohttpd), libhttpserver is able to decode certain body format a and automatically format them in object oriented fashion. This is true for query arguments and for @code{POST} and @code{PUT} requests bodies if @code{application/x-www-form-urlencoded} or @@ -128,6 +128,9 @@ used per connection to avoid resource exhaustion. libhttpserver uses the standard system where the usual build process involves running @verbatim +$ ./bootstrap +$ mkdir build +$ cd build $ ./configure $ make $ make install diff --git a/doc/version.texi b/doc/version.texi index f0360c0f..4417f941 100644 --- a/doc/version.texi +++ b/doc/version.texi @@ -1,4 +1,4 @@ -@set UPDATED 13 April 2013 -@set UPDATED-MONTH April 2013 +@set UPDATED 12 January 2014 +@set UPDATED-MONTH January 2014 @set EDITION 0.6.3 @set VERSION 0.6.3 From 8feead74c86ae290ea0e791c1127b45b4c5ced3d Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sat, 18 Jan 2014 18:11:45 +0000 Subject: [PATCH 015/623] Removed useless typedef --- src/httpserver/binders.hpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/httpserver/binders.hpp b/src/httpserver/binders.hpp index 0724a1cc..f94d8017 100644 --- a/src/httpserver/binders.hpp +++ b/src/httpserver/binders.hpp @@ -14,7 +14,7 @@ You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -45,7 +45,6 @@ namespace details generic_mem_func_type &bound ) { - typedef char ERROR_member_pointer_not_supported[N-100]; return 0; } }; @@ -54,7 +53,7 @@ namespace details struct converter { template inline static generic_class* convert( X* pmem, @@ -168,7 +167,7 @@ namespace details typedef RET_TYPE (*static_function)(PAR1 p1, PAR2 p2); typedef RET_TYPE (*void_static_function)(PAR1 p1, PAR2 p2); - typedef RET_TYPE + typedef RET_TYPE (generic_class::*generic_mem)(PAR1 p1, PAR2 p2); typedef binder< From 4b4380c04540f518016f6c182b100aad7f7f45bb Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sat, 18 Jan 2014 18:12:05 +0000 Subject: [PATCH 016/623] Removed useless forward declaration --- src/httpserver/http_response.hpp | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/src/httpserver/http_response.hpp b/src/httpserver/http_response.hpp index 91adfd1f..04279cd0 100644 --- a/src/httpserver/http_response.hpp +++ b/src/httpserver/http_response.hpp @@ -14,7 +14,7 @@ You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -35,7 +35,6 @@ namespace httpserver { class webserver; -class http_response; struct cache_entry; namespace http @@ -63,7 +62,7 @@ class bad_caching_attempt: public std::exception /** * Class representing an abstraction for an Http Response. It is used from classes using these apis to send information through http protocol. **/ -class http_response +class http_response { public: /** @@ -76,7 +75,7 @@ class http_response http_response ( const T* response_type = 0x0, - const std::string& content = "", + const std::string& content = "", int response_code = 200, const std::string& content_type = "text/plain", bool autodelete = true, @@ -352,7 +351,7 @@ class http_response this->closure_data = closure_data; } protected: - typedef details::binders::functor_two get_raw_response_t; typedef details::binders::functor_one Date: Sat, 18 Jan 2014 19:18:43 +0000 Subject: [PATCH 017/623] http_response uses overload instead of template specialization Changed approach of http_response constructor for strategy implementation. Now method overload is used instead - it consists of a strong simplification in term of code/binary and should make easier to compile the library with old compilers. --- src/httpserver/http_response.hpp | 128 +++++++++++++++---------------- 1 file changed, 64 insertions(+), 64 deletions(-) diff --git a/src/httpserver/http_response.hpp b/src/httpserver/http_response.hpp index 04279cd0..ed86cd18 100644 --- a/src/httpserver/http_response.hpp +++ b/src/httpserver/http_response.hpp @@ -59,6 +59,58 @@ class bad_caching_attempt: public std::exception } }; +class http_file_response; +class http_basic_auth_fail_response; +class http_digest_auth_fail_response; +class switch_protocol_response; +class long_polling_receive_response; +class long_polling_send_response; +class cache_response; +class deferred_response; + +#define SPECIALIZE_RESPONSE_FOR(TYPE, S1, S2, S3) \ +http_response \ +( \ + const TYPE* response_type, \ + const std::string& content, \ + int response_code,\ + const std::string& content_type,\ + bool autodelete,\ + const std::string& realm,\ + const std::string& opaque,\ + bool reload_nonce,\ + const std::vector& topics,\ + int keepalive_secs,\ + const std::string keepalive_msg,\ + const std::string send_topic,\ + cache_entry* ce\ +):\ + content(content),\ + response_code(response_code),\ + autodelete(autodelete),\ + realm(realm),\ + opaque(opaque),\ + reload_nonce(reload_nonce),\ + fp(-1),\ + filename(content),\ + topics(topics),\ + keepalive_secs(keepalive_secs),\ + keepalive_msg(keepalive_msg),\ + send_topic(send_topic),\ + underlying_connection(0x0),\ + ca(0x0),\ + closure_data(0x0),\ + ce(ce),\ + get_raw_response(this, &http_response::get_raw_response_## S1),\ + decorate_response(this, &http_response::decorate_response_## S2),\ + enqueue_response(this, &http_response::enqueue_response_## S3),\ + completed(false),\ + ws(0x0),\ + connection_id(0x0)\ +{\ + set_header(http_utils::http_header_content_type, content_type);\ +} + /** * Class representing an abstraction for an Http Response. It is used from classes using these apis to send information through http protocol. **/ @@ -71,10 +123,10 @@ class http_response * @param response_code The response code to set for the request. * @param response_type param indicating if the content have to be read from a string or from a file **/ - template + template http_response ( - const T* response_type = 0x0, + const TYPE* t, const std::string& content = "", int response_code = 200, const std::string& content_type = "text/plain", @@ -113,6 +165,16 @@ class http_response { set_header(http_utils::http_header_content_type, content_type); } + + SPECIALIZE_RESPONSE_FOR(http_file_response, file, str, str); + SPECIALIZE_RESPONSE_FOR(http_basic_auth_fail_response, str, str, basic); + SPECIALIZE_RESPONSE_FOR(http_digest_auth_fail_response, str, str, digest); + SPECIALIZE_RESPONSE_FOR(switch_protocol_response, switch_r, str, str); + SPECIALIZE_RESPONSE_FOR(long_polling_receive_response, lp_receive, str, str); + SPECIALIZE_RESPONSE_FOR(long_polling_send_response, lp_send, str, str); + SPECIALIZE_RESPONSE_FOR(cache_response, cache, cache, str); + SPECIALIZE_RESPONSE_FOR(deferred_response, deferred, deferred, str); + /** * Copy constructor * @param b The http_response object to copy attributes value from. @@ -425,68 +487,6 @@ class http_response http_response& operator=(const http_response& b); }; -class http_file_response; -class http_basic_auth_fail_response; -class http_digest_auth_fail_response; -class switch_protocol_response; -class long_polling_receive_response; -class long_polling_send_response; -class cache_response; -class deferred_response; - -#define SPECIALIZE_RESPONSE_FOR(TYPE, S1, S2, S3) \ -template <> \ -inline http_response::http_response \ -( \ - const TYPE* response_type, \ - const std::string& content, \ - int response_code,\ - const std::string& content_type,\ - bool autodelete,\ - const std::string& realm,\ - const std::string& opaque,\ - bool reload_nonce,\ - const std::vector& topics,\ - int keepalive_secs,\ - const std::string keepalive_msg,\ - const std::string send_topic,\ - cache_entry* ce\ -):\ - content(content),\ - response_code(response_code),\ - autodelete(autodelete),\ - realm(realm),\ - opaque(opaque),\ - reload_nonce(reload_nonce),\ - fp(-1),\ - filename(content),\ - topics(topics),\ - keepalive_secs(keepalive_secs),\ - keepalive_msg(keepalive_msg),\ - send_topic(send_topic),\ - underlying_connection(0x0),\ - ca(0x0),\ - closure_data(0x0),\ - ce(ce),\ - get_raw_response(this, &http_response::get_raw_response_## S1),\ - decorate_response(this, &http_response::decorate_response_## S2),\ - enqueue_response(this, &http_response::enqueue_response_## S3),\ - completed(false),\ - ws(0x0),\ - connection_id(0x0)\ -{\ - set_header(http_utils::http_header_content_type, content_type);\ -} - -SPECIALIZE_RESPONSE_FOR(http_file_response, file, str, str); -SPECIALIZE_RESPONSE_FOR(http_basic_auth_fail_response, str, str, basic); -SPECIALIZE_RESPONSE_FOR(http_digest_auth_fail_response, str, str, digest); -SPECIALIZE_RESPONSE_FOR(switch_protocol_response, switch_r, str, str); -SPECIALIZE_RESPONSE_FOR(long_polling_receive_response, lp_receive, str, str); -SPECIALIZE_RESPONSE_FOR(long_polling_send_response, lp_send, str, str); -SPECIALIZE_RESPONSE_FOR(cache_response, cache, cache, str); -SPECIALIZE_RESPONSE_FOR(deferred_response, deferred, deferred, str); - class http_string_response : public http_response { public: From 779b92d400ff825b5682100f99e33aea73d3577b Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sat, 18 Jan 2014 19:27:10 +0000 Subject: [PATCH 018/623] Avoid errors on SOCK_CLOEXEC undefined in kernel < 2.7 --- src/webserver.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/webserver.cpp b/src/webserver.cpp index df96c1f2..9db604fe 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -45,6 +45,10 @@ #define _REENTRANT 1 +#ifndef SOCK_CLOEXEC +#define SOCK_CLOEXEC 02000000 +#endif + using namespace std; namespace httpserver From 87f16a13a8c9b422f38489990fbe70dea0acf818 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 19 Jan 2014 16:56:21 +0000 Subject: [PATCH 019/623] Extracted minor classes from webserver header Extracted create_webserver, event_supplier, event_tuple, http_resource_mirror from webserver.hpp file. This increases readability of the code. --- src/Makefile.am | 4 +- src/httpserver.hpp | 1 + src/httpserver/http_response.hpp | 3 +- src/httpserver/webserver.hpp | 559 +------------------------------ src/webserver.cpp | 31 -- 5 files changed, 11 insertions(+), 587 deletions(-) diff --git a/src/Makefile.am b/src/Makefile.am index 19e99fa0..1a3a2dea 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -20,8 +20,8 @@ INCLUDES = -I../ -I$(srcdir)/httpserver/ METASOURCES = AUTO lib_LTLIBRARIES = libhttpserver.la libhttpserver_la_SOURCES = string_utilities.cpp webserver.cpp http_utils.cpp http_endpoint.cpp http_request.cpp http_response.cpp http_resource.cpp -noinst_HEADERS = httpserver/string_utilities.hpp gettext.h -nobase_include_HEADERS = httpserver.hpp httpserver/webserver.hpp httpserver/http_utils.hpp httpserver/http_endpoint.hpp httpserver/http_request.hpp httpserver/http_response.hpp httpserver/http_resource.hpp httpserver/binders.hpp +noinst_HEADERS = httpserver/details/event_tuple.hpp httpserver/details/http_resource_mirror.hpp httpserver/string_utilities.hpp gettext.h +nobase_include_HEADERS = httpserver.hpp httpserver/create_webserver.hpp httpserver/webserver.hpp httpserver/http_utils.hpp httpserver/http_endpoint.hpp httpserver/http_request.hpp httpserver/http_response.hpp httpserver/http_resource.hpp httpserver/binders.hpp httpserver/event_supplier.hpp AM_CXXFLAGS += -fPIC -Wall libhttpserver_la_LIBADD = -lmicrohttpd libhttpserver_la_LDFLAGS = diff --git a/src/httpserver.hpp b/src/httpserver.hpp index f7c7429d..08d69ffd 100644 --- a/src/httpserver.hpp +++ b/src/httpserver.hpp @@ -8,6 +8,7 @@ #include "httpserver/http_resource.hpp" #include "httpserver/http_response.hpp" #include "httpserver/http_request.hpp" +#include "httpserver/event_supplier.hpp" #include "httpserver/webserver.hpp" #endif diff --git a/src/httpserver/http_response.hpp b/src/httpserver/http_response.hpp index ed86cd18..0e850001 100644 --- a/src/httpserver/http_response.hpp +++ b/src/httpserver/http_response.hpp @@ -27,7 +27,8 @@ #include #include #include -#include "httpserver/binders.hpp" + +#include "httpserver/details/http_resource_mirror.hpp" struct MHD_Connection; diff --git a/src/httpserver/webserver.hpp b/src/httpserver/webserver.hpp index efcc58e9..abb1b63f 100644 --- a/src/httpserver/webserver.hpp +++ b/src/httpserver/webserver.hpp @@ -32,20 +32,6 @@ #define DEFAULT_WS_PORT 9898 #define DEFAULT_WS_TIMEOUT 180 -#define CREATE_METHOD_DETECTOR(X) \ - template \ - class has_##X \ - { \ - template struct Check; \ - template static char func(Check *); \ - template static int func(...); \ - public: \ - enum { value = sizeof(func(0)) == sizeof(char) }; \ - }; - -#define HAS_METHOD(X, T, RESULT, ARG1, ARG2) \ - has_##X::value - #include #include #include @@ -60,256 +46,22 @@ #include #include -#include "httpserver/binders.hpp" + +#include "httpserver/details/http_resource_mirror.hpp" +#include "httpserver/details/event_tuple.hpp" +#include "httpserver/create_webserver.hpp" namespace httpserver { -template -class http_resource; +template class http_resource; class http_response; -class cache_response; -class http_request; -class long_polling_receive_response; -class long_polling_send_response; struct cache_entry; -class webserver; -template -class event_supplier; - -typedef void(*render_ptr)(const http_request&, http_response**); - -namespace details -{ - class http_endpoint; - struct modded_request; - struct daemon_item; - typedef bool(*is_allowed_ptr)(const std::string&); - - CREATE_METHOD_DETECTOR(render); - CREATE_METHOD_DETECTOR(render_GET); - CREATE_METHOD_DETECTOR(render_POST); - CREATE_METHOD_DETECTOR(render_PUT); - CREATE_METHOD_DETECTOR(render_HEAD); - CREATE_METHOD_DETECTOR(render_DELETE); - CREATE_METHOD_DETECTOR(render_TRACE); - CREATE_METHOD_DETECTOR(render_OPTIONS); - CREATE_METHOD_DETECTOR(render_CONNECT); - CREATE_METHOD_DETECTOR(render_not_acceptable); - - void empty_render(const http_request& r, http_response** res); - void empty_not_acceptable_render( - const http_request& r, http_response** res - ); - bool empty_is_allowed(const std::string& method); - - class http_resource_mirror - { - public: - http_resource_mirror() - { - } - - ~http_resource_mirror() - { - } - private: - typedef binders::functor_two functor; - - typedef binders::functor_one functor_allowed; - - const functor render; - const functor render_GET; - const functor render_POST; - const functor render_PUT; - const functor render_HEAD; - const functor render_DELETE; - const functor render_TRACE; - const functor render_OPTIONS; - const functor render_CONNECT; - const functor_allowed is_allowed; - - functor method_not_acceptable_resource; - - http_resource_mirror& operator= (const http_resource_mirror& o) - { - return *this; - } - - template - http_resource_mirror(http_resource* res): - render( - HAS_METHOD(render, T, void, - const http_request&, http_response** - ) ? functor(res, &T::render) : functor(&empty_render) - ), - render_GET( - HAS_METHOD(render_GET, T, void, - const http_request&, http_response** - ) ? functor(res, &T::render_GET) : - ( - HAS_METHOD(render, T, void, - const http_request&, http_response** - ) ? functor(res, &T::render) : functor(&empty_render) - ) - ), - render_POST( - HAS_METHOD(render_POST, T, void, - const http_request&, http_response** - ) ? functor(res, &T::render_POST) : - ( - HAS_METHOD(render, T, void, - const http_request&, http_response** - ) ? functor(res, &T::render) : functor(&empty_render) - ) - ), - render_PUT( - HAS_METHOD(render_PUT, T, void, - const http_request&, http_response** - ) ? functor(res, &T::render_PUT) : - ( - HAS_METHOD(render, T, void, - const http_request&, http_response** - ) ? functor(res, &T::render) : functor(&empty_render) - ) - ), - render_HEAD( - HAS_METHOD(render_HEAD, T, void, - const http_request&, http_response** - ) ? functor(res, &T::render_HEAD) : - ( - HAS_METHOD(render, T, void, - const http_request&, http_response** - ) ? functor(res, &T::render) : functor(&empty_render) - ) - ), - render_DELETE( - HAS_METHOD(render_DELETE, T, void, - const http_request&, http_response** - ) ? functor(res, &T::render_DELETE) : - ( - HAS_METHOD(render, T, void, - const http_request&, http_response** - ) ? functor(res, &T::render) : functor(&empty_render) - ) - ), - render_TRACE( - HAS_METHOD(render_TRACE, T, void, - const http_request&, http_response** - ) ? functor(res, &T::render_TRACE) : - ( - HAS_METHOD(render, T, void, - const http_request&, http_response** - ) ? functor(res, &T::render) : functor(&empty_render) - ) - ), - render_OPTIONS( - HAS_METHOD(render_OPTIONS, T, void, - const http_request&, http_response** - ) ? functor(res, &T::render_OPTIONS) : - ( - HAS_METHOD(render, T, void, - const http_request&, http_response** - ) ? functor(res, &T::render) : functor(&empty_render) - ) - ), - render_CONNECT( - HAS_METHOD(render_CONNECT, T, void, - const http_request&, http_response** - ) ? functor(res, &T::render_CONNECT) : - ( - HAS_METHOD(render, T, void, - const http_request&, http_response** - ) ? functor(res, &T::render) : functor(&empty_render) - ) - ), - is_allowed(res, &T::is_allowed) - { - } - - friend class ::httpserver::webserver; - }; - - class event_tuple - { - private: - typedef void(*supply_events_ptr)( - fd_set*, - fd_set*, - fd_set*, - int* - ); - - typedef struct timeval(*get_timeout_ptr)(); - typedef void(*dispatch_events_ptr)(); - supply_events_ptr supply_events; - get_timeout_ptr get_timeout; - dispatch_events_ptr dispatch_events; - - event_tuple(); - - friend class ::httpserver::webserver; - public: - template - event_tuple(event_supplier* es): - supply_events(std::bind1st(std::mem_fun(&T::supply_events),es)), - get_timeout(std::bind1st(std::mem_fun(&T::get_timeout), es)), - dispatch_events(std::bind1st( - std::mem_fun(&T::dispatch_events), es) - ) - { - } - }; -} - -using namespace http; +template class event_supplier; namespace http { struct ip_representation; }; -template -class event_supplier -{ - public: - event_supplier() - { - } - - ~event_supplier() - { - } - - void supply_events( - fd_set* read_fdset, - fd_set* write_fdset, - fd_set* exc_fdset, - int* max - ) const - { - static_cast(this)->supply_events( - read_fdset, write_fdset, exc_fdset, max - ); - } - - struct timeval get_timeout() const - { - return static_cast(this)->get_timeout(); - } - - void dispatch_events() const - { - static_cast(this)->dispatch_events(); - } -}; - -class create_webserver; -typedef bool(*validator_ptr)(const std::string&); -typedef void(*unescaper_ptr)(char*); -typedef void(*log_access_ptr)(const std::string&); -typedef void(*log_error_ptr)(const std::string&); - /** * Class representing the webserver. Main class of the apis. **/ @@ -507,7 +259,6 @@ class webserver pthread_rwlock_unlock(&runguard); } - void remove_event_supplier(const std::string& id); /** @@ -697,303 +448,5 @@ class webserver friend class http_response; }; -class create_webserver -{ - public: - create_webserver(): - _port(DEFAULT_WS_PORT), - _start_method(http_utils::INTERNAL_SELECT), - _max_threads(0), - _max_connections(0), - _memory_limit(0), - _connection_timeout(DEFAULT_WS_TIMEOUT), - _per_IP_connection_limit(0), - _log_access(0x0), - _log_error(0x0), - _validator(0x0), - _unescaper(0x0), - _bind_address(0x0), - _bind_socket(0), - _max_thread_stack_size(0), - _use_ssl(false), - _use_ipv6(false), - _debug(false), - _pedantic(false), - _https_mem_key(""), - _https_mem_cert(""), - _https_mem_trust(""), - _https_priorities(""), - _cred_type(http_utils::NONE), - _digest_auth_random(""), - _nonce_nc_size(0), - _default_policy(http_utils::ACCEPT), - _basic_auth_enabled(true), - _digest_auth_enabled(true), - _regex_checking(true), - _ban_system_enabled(true), - _post_process_enabled(true), - _single_resource(0x0), - _not_found_resource(0x0), - _method_not_allowed_resource(0x0), - _method_not_acceptable_resource(0x0), - _internal_error_resource(0x0) - { - } - - explicit create_webserver(int port): - _port(port), - _start_method(http_utils::INTERNAL_SELECT), - _max_threads(0), - _max_connections(0), - _memory_limit(0), - _connection_timeout(DEFAULT_WS_TIMEOUT), - _per_IP_connection_limit(0), - _log_access(0x0), - _log_error(0x0), - _validator(0x0), - _unescaper(0x0), - _bind_address(0x0), - _bind_socket(0), - _max_thread_stack_size(0), - _use_ssl(false), - _use_ipv6(false), - _debug(false), - _pedantic(false), - _https_mem_key(""), - _https_mem_cert(""), - _https_mem_trust(""), - _https_priorities(""), - _cred_type(http_utils::NONE), - _digest_auth_random(""), - _nonce_nc_size(0), - _default_policy(http_utils::ACCEPT), - _basic_auth_enabled(true), - _digest_auth_enabled(true), - _regex_checking(true), - _ban_system_enabled(true), - _post_process_enabled(true), - _single_resource(0x0), - _not_found_resource(0x0), - _method_not_allowed_resource(0x0), - _method_not_acceptable_resource(0x0), - _internal_error_resource(0x0) - { - } - - create_webserver& port(int port) { _port = port; return *this; } - create_webserver& start_method( - const http_utils::start_method_T& start_method - ) - { - _start_method = start_method; return *this; - } - create_webserver& max_threads(int max_threads) - { - _max_threads = max_threads; return *this; - } - create_webserver& max_connections(int max_connections) - { - _max_connections = max_connections; return *this; - } - create_webserver& memory_limit(int memory_limit) - { - _memory_limit = memory_limit; return *this; - } - create_webserver& connection_timeout(int connection_timeout) - { - _connection_timeout = connection_timeout; return *this; - } - create_webserver& per_IP_connection_limit(int per_IP_connection_limit) - { - _per_IP_connection_limit = per_IP_connection_limit; return *this; - } - create_webserver& log_access(log_access_ptr log_access) - { - _log_access = log_access; return *this; - } - create_webserver& log_error(log_error_ptr log_error) - { - _log_error = log_error; return *this; - } - create_webserver& validator(validator_ptr validator) - { - _validator = validator; return *this; - } - create_webserver& unescaper(unescaper_ptr unescaper) - { - _unescaper = unescaper; return *this; - } - create_webserver& bind_address(const struct sockaddr* bind_address) - { - _bind_address = bind_address; return *this; - } - create_webserver& bind_socket(int bind_socket) - { - _bind_socket = bind_socket; return *this; - } - create_webserver& max_thread_stack_size(int max_thread_stack_size) - { - _max_thread_stack_size = max_thread_stack_size; return *this; - } - create_webserver& use_ssl() { _use_ssl = true; return *this; } - create_webserver& no_ssl() { _use_ssl = false; return *this; } - create_webserver& use_ipv6() { _use_ipv6 = true; return *this; } - create_webserver& no_ipv6() { _use_ipv6 = false; return *this; } - create_webserver& debug() { _debug = true; return *this; } - create_webserver& no_debug() { _debug = false; return *this; } - create_webserver& pedantic() { _pedantic = true; return *this; } - create_webserver& no_pedantic() { _pedantic = false; return *this; } - create_webserver& https_mem_key(const std::string& https_mem_key); - create_webserver& https_mem_cert(const std::string& https_mem_cert); - create_webserver& https_mem_trust(const std::string& https_mem_trust); - create_webserver& raw_https_mem_key(const std::string& https_mem_key) - { - _https_mem_key = https_mem_key; return *this; - } - create_webserver& raw_https_mem_cert(const std::string& https_mem_cert) - { - _https_mem_cert = https_mem_cert; return *this; - } - create_webserver& raw_https_mem_trust( - const std::string& https_mem_trust - ) - { - _https_mem_trust = https_mem_trust; return *this; - } - create_webserver& https_priorities(const std::string& https_priorities) - { - _https_priorities = https_priorities; return *this; - } - create_webserver& cred_type(const http_utils::cred_type_T& cred_type) - { - _cred_type = cred_type; return *this; - } - create_webserver& digest_auth_random( - const std::string& digest_auth_random - ) - { - _digest_auth_random = digest_auth_random; return *this; - } - create_webserver& nonce_nc_size(int nonce_nc_size) - { - _nonce_nc_size = nonce_nc_size; return *this; - } - create_webserver& default_policy( - const http_utils::policy_T& default_policy - ) - { - _default_policy = default_policy; return *this; - } - create_webserver& basic_auth() - { - _basic_auth_enabled = true; return *this; - } - create_webserver& no_basic_auth() - { - _basic_auth_enabled = false; return *this; - } - create_webserver& digest_auth() - { - _digest_auth_enabled = true; return *this; - } - create_webserver& no_digest_auth() - { - _digest_auth_enabled = false; return *this; - } - create_webserver& regex_checking() - { - _regex_checking = true; return *this; - } - create_webserver& no_regex_checking() - { - _regex_checking = false; return *this; - } - create_webserver& ban_system() - { - _ban_system_enabled = true; return *this; - } - create_webserver& no_ban_system() - { - _ban_system_enabled = false; return *this; - } - create_webserver& post_process() - { - _post_process_enabled = true; return *this; - } - create_webserver& no_post_process() - { - _post_process_enabled = false; return *this; - } - - create_webserver& single_resource(render_ptr single_resource) - { - _single_resource = single_resource; return *this; - } - create_webserver& not_found_resource(render_ptr not_found_resource) - { - _not_found_resource = not_found_resource; return *this; - } - create_webserver& method_not_allowed_resource( - render_ptr method_not_allowed_resource - ) - { - _method_not_allowed_resource = method_not_allowed_resource; - return *this; - } - create_webserver& method_not_acceptable_resource( - render_ptr method_not_acceptable_resource - ) - { - _method_not_acceptable_resource = method_not_acceptable_resource; - return *this; - } - create_webserver& internal_error_resource( - render_ptr internal_error_resource - ) - { - _internal_error_resource = internal_error_resource; return *this; - } - - private: - int _port; - http_utils::start_method_T _start_method; - int _max_threads; - int _max_connections; - int _memory_limit; - int _connection_timeout; - int _per_IP_connection_limit; - log_access_ptr _log_access; - log_error_ptr _log_error; - validator_ptr _validator; - unescaper_ptr _unescaper; - const struct sockaddr* _bind_address; - int _bind_socket; - int _max_thread_stack_size; - bool _use_ssl; - bool _use_ipv6; - bool _debug; - bool _pedantic; - std::string _https_mem_key; - std::string _https_mem_cert; - std::string _https_mem_trust; - std::string _https_priorities; - http_utils::cred_type_T _cred_type; - std::string _digest_auth_random; - int _nonce_nc_size; - http_utils::policy_T _default_policy; - bool _basic_auth_enabled; - bool _digest_auth_enabled; - bool _regex_checking; - bool _ban_system_enabled; - bool _post_process_enabled; - render_ptr _single_resource; - render_ptr _not_found_resource; - render_ptr _method_not_allowed_resource; - render_ptr _method_not_acceptable_resource; - render_ptr _internal_error_resource; - - friend class webserver; -}; - }; #endif //_FRAMEWORK_WEBSERVER_HPP__ diff --git a/src/webserver.cpp b/src/webserver.cpp index 9db604fe..80aa115f 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -352,37 +352,6 @@ static void ignore_sigpipe () ); } -//WEBSERVER CREATOR -create_webserver& create_webserver::https_mem_key( - const std::string& https_mem_key -) -{ - char* _https_mem_key_pt = http::load_file(https_mem_key.c_str()); - _https_mem_key = _https_mem_key_pt; - free(_https_mem_key_pt); - return *this; -} - -create_webserver& create_webserver::https_mem_cert( - const std::string& https_mem_cert -) -{ - char* _https_mem_cert_pt = http::load_file(https_mem_cert.c_str()); - _https_mem_cert = _https_mem_cert_pt; - free(_https_mem_cert_pt); - return *this; -} - -create_webserver& create_webserver::https_mem_trust( - const std::string& https_mem_trust -) -{ - char* _https_mem_trust_pt = http::load_file(https_mem_trust.c_str()); - _https_mem_trust = _https_mem_trust_pt; - free(_https_mem_trust_pt); - return *this; -} - //WEBSERVER webserver::webserver ( From 35dc94faae2f562878c24f05ac62c3af6c7a5f47 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 19 Jan 2014 16:59:56 +0000 Subject: [PATCH 020/623] Added missing hpp files --- src/httpserver/create_webserver.hpp | 361 ++++++++++++++++++ src/httpserver/details/event_tuple.hpp | 71 ++++ .../details/http_resource_mirror.hpp | 206 ++++++++++ src/httpserver/event_supplier.hpp | 67 ++++ 4 files changed, 705 insertions(+) create mode 100644 src/httpserver/create_webserver.hpp create mode 100644 src/httpserver/details/event_tuple.hpp create mode 100644 src/httpserver/details/http_resource_mirror.hpp create mode 100644 src/httpserver/event_supplier.hpp diff --git a/src/httpserver/create_webserver.hpp b/src/httpserver/create_webserver.hpp new file mode 100644 index 00000000..1a587df9 --- /dev/null +++ b/src/httpserver/create_webserver.hpp @@ -0,0 +1,361 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#if !defined (_HTTPSERVER_HPP_INSIDE_) && !defined (HTTPSERVER_COMPILATION) +#error "Only or can be included directly." +#endif + +#ifndef _CREATE_WEBSERVER_HPP_ +#define _CREATE_WEBSERVER_HPP_ + +#include "httpserver/http_utils.hpp" + +namespace httpserver { + +class webserver; +class http_request; +class http_response; + +using namespace http; + +typedef void(*render_ptr)(const http_request&, http_response**); +typedef bool(*validator_ptr)(const std::string&); +typedef void(*unescaper_ptr)(char*); +typedef void(*log_access_ptr)(const std::string&); +typedef void(*log_error_ptr)(const std::string&); + +class create_webserver +{ + public: + create_webserver(): + _port(DEFAULT_WS_PORT), + _start_method(http_utils::INTERNAL_SELECT), + _max_threads(0), + _max_connections(0), + _memory_limit(0), + _connection_timeout(DEFAULT_WS_TIMEOUT), + _per_IP_connection_limit(0), + _log_access(0x0), + _log_error(0x0), + _validator(0x0), + _unescaper(0x0), + _bind_address(0x0), + _bind_socket(0), + _max_thread_stack_size(0), + _use_ssl(false), + _use_ipv6(false), + _debug(false), + _pedantic(false), + _https_mem_key(""), + _https_mem_cert(""), + _https_mem_trust(""), + _https_priorities(""), + _cred_type(http_utils::NONE), + _digest_auth_random(""), + _nonce_nc_size(0), + _default_policy(http_utils::ACCEPT), + _basic_auth_enabled(true), + _digest_auth_enabled(true), + _regex_checking(true), + _ban_system_enabled(true), + _post_process_enabled(true), + _single_resource(0x0), + _not_found_resource(0x0), + _method_not_allowed_resource(0x0), + _method_not_acceptable_resource(0x0), + _internal_error_resource(0x0) + { + } + + explicit create_webserver(int port): + _port(port), + _start_method(http_utils::INTERNAL_SELECT), + _max_threads(0), + _max_connections(0), + _memory_limit(0), + _connection_timeout(DEFAULT_WS_TIMEOUT), + _per_IP_connection_limit(0), + _log_access(0x0), + _log_error(0x0), + _validator(0x0), + _unescaper(0x0), + _bind_address(0x0), + _bind_socket(0), + _max_thread_stack_size(0), + _use_ssl(false), + _use_ipv6(false), + _debug(false), + _pedantic(false), + _https_mem_key(""), + _https_mem_cert(""), + _https_mem_trust(""), + _https_priorities(""), + _cred_type(http_utils::NONE), + _digest_auth_random(""), + _nonce_nc_size(0), + _default_policy(http_utils::ACCEPT), + _basic_auth_enabled(true), + _digest_auth_enabled(true), + _regex_checking(true), + _ban_system_enabled(true), + _post_process_enabled(true), + _single_resource(0x0), + _not_found_resource(0x0), + _method_not_allowed_resource(0x0), + _method_not_acceptable_resource(0x0), + _internal_error_resource(0x0) + { + } + + create_webserver& port(int port) { _port = port; return *this; } + create_webserver& start_method( + const http_utils::start_method_T& start_method + ) + { + _start_method = start_method; return *this; + } + create_webserver& max_threads(int max_threads) + { + _max_threads = max_threads; return *this; + } + create_webserver& max_connections(int max_connections) + { + _max_connections = max_connections; return *this; + } + create_webserver& memory_limit(int memory_limit) + { + _memory_limit = memory_limit; return *this; + } + create_webserver& connection_timeout(int connection_timeout) + { + _connection_timeout = connection_timeout; return *this; + } + create_webserver& per_IP_connection_limit(int per_IP_connection_limit) + { + _per_IP_connection_limit = per_IP_connection_limit; return *this; + } + create_webserver& log_access(log_access_ptr log_access) + { + _log_access = log_access; return *this; + } + create_webserver& log_error(log_error_ptr log_error) + { + _log_error = log_error; return *this; + } + create_webserver& validator(validator_ptr validator) + { + _validator = validator; return *this; + } + create_webserver& unescaper(unescaper_ptr unescaper) + { + _unescaper = unescaper; return *this; + } + create_webserver& bind_address(const struct sockaddr* bind_address) + { + _bind_address = bind_address; return *this; + } + create_webserver& bind_socket(int bind_socket) + { + _bind_socket = bind_socket; return *this; + } + create_webserver& max_thread_stack_size(int max_thread_stack_size) + { + _max_thread_stack_size = max_thread_stack_size; return *this; + } + create_webserver& use_ssl() { _use_ssl = true; return *this; } + create_webserver& no_ssl() { _use_ssl = false; return *this; } + create_webserver& use_ipv6() { _use_ipv6 = true; return *this; } + create_webserver& no_ipv6() { _use_ipv6 = false; return *this; } + create_webserver& debug() { _debug = true; return *this; } + create_webserver& no_debug() { _debug = false; return *this; } + create_webserver& pedantic() { _pedantic = true; return *this; } + create_webserver& no_pedantic() { _pedantic = false; return *this; } + create_webserver& https_mem_key(const std::string& https_mem_key) + { + char* _https_mem_key_pt = http::load_file(https_mem_key.c_str()); + _https_mem_key = _https_mem_key_pt; + free(_https_mem_key_pt); + return *this; + } + create_webserver& https_mem_cert(const std::string& https_mem_cert) + { + char* _https_mem_cert_pt = http::load_file(https_mem_cert.c_str()); + _https_mem_cert = _https_mem_cert_pt; + free(_https_mem_cert_pt); + return *this; + } + create_webserver& https_mem_trust(const std::string& https_mem_trust) + { + char* _https_mem_trust_pt = http::load_file(https_mem_trust.c_str()); + _https_mem_trust = _https_mem_trust_pt; + free(_https_mem_trust_pt); + return *this; + } + create_webserver& raw_https_mem_key(const std::string& https_mem_key) + { + _https_mem_key = https_mem_key; return *this; + } + create_webserver& raw_https_mem_cert(const std::string& https_mem_cert) + { + _https_mem_cert = https_mem_cert; return *this; + } + create_webserver& raw_https_mem_trust( + const std::string& https_mem_trust + ) + { + _https_mem_trust = https_mem_trust; return *this; + } + create_webserver& https_priorities(const std::string& https_priorities) + { + _https_priorities = https_priorities; return *this; + } + create_webserver& cred_type(const http_utils::cred_type_T& cred_type) + { + _cred_type = cred_type; return *this; + } + create_webserver& digest_auth_random( + const std::string& digest_auth_random + ) + { + _digest_auth_random = digest_auth_random; return *this; + } + create_webserver& nonce_nc_size(int nonce_nc_size) + { + _nonce_nc_size = nonce_nc_size; return *this; + } + create_webserver& default_policy( + const http_utils::policy_T& default_policy + ) + { + _default_policy = default_policy; return *this; + } + create_webserver& basic_auth() + { + _basic_auth_enabled = true; return *this; + } + create_webserver& no_basic_auth() + { + _basic_auth_enabled = false; return *this; + } + create_webserver& digest_auth() + { + _digest_auth_enabled = true; return *this; + } + create_webserver& no_digest_auth() + { + _digest_auth_enabled = false; return *this; + } + create_webserver& regex_checking() + { + _regex_checking = true; return *this; + } + create_webserver& no_regex_checking() + { + _regex_checking = false; return *this; + } + create_webserver& ban_system() + { + _ban_system_enabled = true; return *this; + } + create_webserver& no_ban_system() + { + _ban_system_enabled = false; return *this; + } + create_webserver& post_process() + { + _post_process_enabled = true; return *this; + } + create_webserver& no_post_process() + { + _post_process_enabled = false; return *this; + } + create_webserver& single_resource(render_ptr single_resource) + { + _single_resource = single_resource; return *this; + } + create_webserver& not_found_resource(render_ptr not_found_resource) + { + _not_found_resource = not_found_resource; return *this; + } + create_webserver& method_not_allowed_resource( + render_ptr method_not_allowed_resource + ) + { + _method_not_allowed_resource = method_not_allowed_resource; + return *this; + } + create_webserver& method_not_acceptable_resource( + render_ptr method_not_acceptable_resource + ) + { + _method_not_acceptable_resource = method_not_acceptable_resource; + return *this; + } + create_webserver& internal_error_resource( + render_ptr internal_error_resource + ) + { + _internal_error_resource = internal_error_resource; return *this; + } + + private: + int _port; + http_utils::start_method_T _start_method; + int _max_threads; + int _max_connections; + int _memory_limit; + int _connection_timeout; + int _per_IP_connection_limit; + log_access_ptr _log_access; + log_error_ptr _log_error; + validator_ptr _validator; + unescaper_ptr _unescaper; + const struct sockaddr* _bind_address; + int _bind_socket; + int _max_thread_stack_size; + bool _use_ssl; + bool _use_ipv6; + bool _debug; + bool _pedantic; + std::string _https_mem_key; + std::string _https_mem_cert; + std::string _https_mem_trust; + std::string _https_priorities; + http_utils::cred_type_T _cred_type; + std::string _digest_auth_random; + int _nonce_nc_size; + http_utils::policy_T _default_policy; + bool _basic_auth_enabled; + bool _digest_auth_enabled; + bool _regex_checking; + bool _ban_system_enabled; + bool _post_process_enabled; + render_ptr _single_resource; + render_ptr _not_found_resource; + render_ptr _method_not_allowed_resource; + render_ptr _method_not_acceptable_resource; + render_ptr _internal_error_resource; + + friend class webserver; +}; + +} //httpserver + +#endif //_CREATE_WEBSERVER_HPP_ diff --git a/src/httpserver/details/event_tuple.hpp b/src/httpserver/details/event_tuple.hpp new file mode 100644 index 00000000..e036d761 --- /dev/null +++ b/src/httpserver/details/event_tuple.hpp @@ -0,0 +1,71 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#if !defined (_HTTPSERVER_HPP_INSIDE_) && !defined (HTTPSERVER_COMPILATION) +#error "Only or can be included directly." +#endif + +#ifndef _EVENT_TUPLE_HPP_ +#define _EVENT_TUPLE_HPP_ + +#include "httpserver/event_supplier.hpp" + +namespace httpserver { +namespace details +{ + class http_endpoint; + struct modded_request; + struct daemon_item; + + class event_tuple + { + private: + typedef void(*supply_events_ptr)( + fd_set*, + fd_set*, + fd_set*, + int* + ); + + typedef struct timeval(*get_timeout_ptr)(); + typedef void(*dispatch_events_ptr)(); + supply_events_ptr supply_events; + get_timeout_ptr get_timeout; + dispatch_events_ptr dispatch_events; + + event_tuple(); + + friend class ::httpserver::webserver; + public: + template + event_tuple(event_supplier* es): + supply_events(std::bind1st(std::mem_fun(&T::supply_events),es)), + get_timeout(std::bind1st(std::mem_fun(&T::get_timeout), es)), + dispatch_events(std::bind1st( + std::mem_fun(&T::dispatch_events), es) + ) + { + } + }; +} //details + +} //httpserver + +#endif //_EVENT_TUPLE_HPP_ diff --git a/src/httpserver/details/http_resource_mirror.hpp b/src/httpserver/details/http_resource_mirror.hpp new file mode 100644 index 00000000..7c4e4168 --- /dev/null +++ b/src/httpserver/details/http_resource_mirror.hpp @@ -0,0 +1,206 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#if !defined (_HTTPSERVER_HPP_INSIDE_) && !defined (HTTPSERVER_COMPILATION) +#error "Only or can be included directly." +#endif + +#ifndef _HTTP_RESOURCE_MIRROR_HPP_ +#define _HTTP_RESOURCE_MIRROR_HPP_ + +#include "httpserver/binders.hpp" + +#define CREATE_METHOD_DETECTOR(X) \ + template \ + class has_##X \ + { \ + template struct Check; \ + template static char func(Check *); \ + template static int func(...); \ + public: \ + enum { value = sizeof(func(0)) == sizeof(char) }; \ + }; + +#define HAS_METHOD(X, T, RESULT, ARG1, ARG2) \ + has_##X::value + +namespace httpserver { + +template class http_resource; +class http_request; +class http_response; +class webserver; + +namespace details +{ + class http_endpoint; + struct modded_request; + struct daemon_item; + typedef bool(*is_allowed_ptr)(const std::string&); + + CREATE_METHOD_DETECTOR(render); + CREATE_METHOD_DETECTOR(render_GET); + CREATE_METHOD_DETECTOR(render_POST); + CREATE_METHOD_DETECTOR(render_PUT); + CREATE_METHOD_DETECTOR(render_HEAD); + CREATE_METHOD_DETECTOR(render_DELETE); + CREATE_METHOD_DETECTOR(render_TRACE); + CREATE_METHOD_DETECTOR(render_OPTIONS); + CREATE_METHOD_DETECTOR(render_CONNECT); + CREATE_METHOD_DETECTOR(render_not_acceptable); + + void empty_render(const http_request& r, http_response** res); + void empty_not_acceptable_render( + const http_request& r, http_response** res + ); + bool empty_is_allowed(const std::string& method); + + class http_resource_mirror + { + public: + http_resource_mirror() + { + } + + ~http_resource_mirror() + { + } + private: + typedef binders::functor_two functor; + + typedef binders::functor_one functor_allowed; + + const functor render; + const functor render_GET; + const functor render_POST; + const functor render_PUT; + const functor render_HEAD; + const functor render_DELETE; + const functor render_TRACE; + const functor render_OPTIONS; + const functor render_CONNECT; + const functor_allowed is_allowed; + + functor method_not_acceptable_resource; + + http_resource_mirror& operator= (const http_resource_mirror& o) + { + return *this; + } + + template + http_resource_mirror(http_resource* res): + render( + HAS_METHOD(render, T, void, + const http_request&, http_response** + ) ? functor(res, &T::render) : functor(&empty_render) + ), + render_GET( + HAS_METHOD(render_GET, T, void, + const http_request&, http_response** + ) ? functor(res, &T::render_GET) : + ( + HAS_METHOD(render, T, void, + const http_request&, http_response** + ) ? functor(res, &T::render) : functor(&empty_render) + ) + ), + render_POST( + HAS_METHOD(render_POST, T, void, + const http_request&, http_response** + ) ? functor(res, &T::render_POST) : + ( + HAS_METHOD(render, T, void, + const http_request&, http_response** + ) ? functor(res, &T::render) : functor(&empty_render) + ) + ), + render_PUT( + HAS_METHOD(render_PUT, T, void, + const http_request&, http_response** + ) ? functor(res, &T::render_PUT) : + ( + HAS_METHOD(render, T, void, + const http_request&, http_response** + ) ? functor(res, &T::render) : functor(&empty_render) + ) + ), + render_HEAD( + HAS_METHOD(render_HEAD, T, void, + const http_request&, http_response** + ) ? functor(res, &T::render_HEAD) : + ( + HAS_METHOD(render, T, void, + const http_request&, http_response** + ) ? functor(res, &T::render) : functor(&empty_render) + ) + ), + render_DELETE( + HAS_METHOD(render_DELETE, T, void, + const http_request&, http_response** + ) ? functor(res, &T::render_DELETE) : + ( + HAS_METHOD(render, T, void, + const http_request&, http_response** + ) ? functor(res, &T::render) : functor(&empty_render) + ) + ), + render_TRACE( + HAS_METHOD(render_TRACE, T, void, + const http_request&, http_response** + ) ? functor(res, &T::render_TRACE) : + ( + HAS_METHOD(render, T, void, + const http_request&, http_response** + ) ? functor(res, &T::render) : functor(&empty_render) + ) + ), + render_OPTIONS( + HAS_METHOD(render_OPTIONS, T, void, + const http_request&, http_response** + ) ? functor(res, &T::render_OPTIONS) : + ( + HAS_METHOD(render, T, void, + const http_request&, http_response** + ) ? functor(res, &T::render) : functor(&empty_render) + ) + ), + render_CONNECT( + HAS_METHOD(render_CONNECT, T, void, + const http_request&, http_response** + ) ? functor(res, &T::render_CONNECT) : + ( + HAS_METHOD(render, T, void, + const http_request&, http_response** + ) ? functor(res, &T::render) : functor(&empty_render) + ) + ), + is_allowed(res, &T::is_allowed) + { + } + + friend class ::httpserver::webserver; + }; +} //details +} //httpserver + +#endif //_HTTP_RESOURCE_MIRROR_HPP_ diff --git a/src/httpserver/event_supplier.hpp b/src/httpserver/event_supplier.hpp new file mode 100644 index 00000000..fd39400c --- /dev/null +++ b/src/httpserver/event_supplier.hpp @@ -0,0 +1,67 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#if !defined (_HTTPSERVER_HPP_INSIDE_) && !defined (HTTPSERVER_COMPILATION) +#error "Only or can be included directly." +#endif + +#ifndef _EVENT_SUPPLIER_HPP_ +#define _EVENT_SUPPLIER_HPP_ + +namespace httpserver { + +template +class event_supplier +{ + public: + event_supplier() + { + } + + ~event_supplier() + { + } + + void supply_events( + fd_set* read_fdset, + fd_set* write_fdset, + fd_set* exc_fdset, + int* max + ) const + { + static_cast(this)->supply_events( + read_fdset, write_fdset, exc_fdset, max + ); + } + + struct timeval get_timeout() const + { + return static_cast(this)->get_timeout(); + } + + void dispatch_events() const + { + static_cast(this)->dispatch_events(); + } +}; + +} //event_supplier + +#endif //_EVENT_SUPPLIER_HPP_ From dfe3664487c6e8c090d4de9542fb14c25330bb5a Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Mon, 20 Jan 2014 01:53:31 +0000 Subject: [PATCH 021/623] Avoid to expose dependencies toward detail classes --- src/httpserver/create_webserver.hpp | 29 +++---- src/httpserver/webserver.hpp | 120 +++++++--------------------- src/webserver.cpp | 89 +-------------------- 3 files changed, 48 insertions(+), 190 deletions(-) diff --git a/src/httpserver/create_webserver.hpp b/src/httpserver/create_webserver.hpp index 1a587df9..805d7f9c 100644 --- a/src/httpserver/create_webserver.hpp +++ b/src/httpserver/create_webserver.hpp @@ -27,14 +27,15 @@ #include "httpserver/http_utils.hpp" +#define DEFAULT_WS_TIMEOUT 180 +#define DEFAULT_WS_PORT 9898 + namespace httpserver { class webserver; class http_request; class http_response; -using namespace http; - typedef void(*render_ptr)(const http_request&, http_response**); typedef bool(*validator_ptr)(const std::string&); typedef void(*unescaper_ptr)(char*); @@ -46,7 +47,7 @@ class create_webserver public: create_webserver(): _port(DEFAULT_WS_PORT), - _start_method(http_utils::INTERNAL_SELECT), + _start_method(http::http_utils::INTERNAL_SELECT), _max_threads(0), _max_connections(0), _memory_limit(0), @@ -67,10 +68,10 @@ class create_webserver _https_mem_cert(""), _https_mem_trust(""), _https_priorities(""), - _cred_type(http_utils::NONE), + _cred_type(http::http_utils::NONE), _digest_auth_random(""), _nonce_nc_size(0), - _default_policy(http_utils::ACCEPT), + _default_policy(http::http_utils::ACCEPT), _basic_auth_enabled(true), _digest_auth_enabled(true), _regex_checking(true), @@ -86,7 +87,7 @@ class create_webserver explicit create_webserver(int port): _port(port), - _start_method(http_utils::INTERNAL_SELECT), + _start_method(http::http_utils::INTERNAL_SELECT), _max_threads(0), _max_connections(0), _memory_limit(0), @@ -107,10 +108,10 @@ class create_webserver _https_mem_cert(""), _https_mem_trust(""), _https_priorities(""), - _cred_type(http_utils::NONE), + _cred_type(http::http_utils::NONE), _digest_auth_random(""), _nonce_nc_size(0), - _default_policy(http_utils::ACCEPT), + _default_policy(http::http_utils::ACCEPT), _basic_auth_enabled(true), _digest_auth_enabled(true), _regex_checking(true), @@ -126,7 +127,7 @@ class create_webserver create_webserver& port(int port) { _port = port; return *this; } create_webserver& start_method( - const http_utils::start_method_T& start_method + const http::http_utils::start_method_T& start_method ) { _start_method = start_method; return *this; @@ -226,7 +227,7 @@ class create_webserver { _https_priorities = https_priorities; return *this; } - create_webserver& cred_type(const http_utils::cred_type_T& cred_type) + create_webserver& cred_type(const http::http_utils::cred_type_T& cred_type) { _cred_type = cred_type; return *this; } @@ -241,7 +242,7 @@ class create_webserver _nonce_nc_size = nonce_nc_size; return *this; } create_webserver& default_policy( - const http_utils::policy_T& default_policy + const http::http_utils::policy_T& default_policy ) { _default_policy = default_policy; return *this; @@ -317,7 +318,7 @@ class create_webserver private: int _port; - http_utils::start_method_T _start_method; + http::http_utils::start_method_T _start_method; int _max_threads; int _max_connections; int _memory_limit; @@ -338,10 +339,10 @@ class create_webserver std::string _https_mem_cert; std::string _https_mem_trust; std::string _https_priorities; - http_utils::cred_type_T _cred_type; + http::http_utils::cred_type_T _cred_type; std::string _digest_auth_random; int _nonce_nc_size; - http_utils::policy_T _default_policy; + http::http_utils::policy_T _default_policy; bool _basic_auth_enabled; bool _digest_auth_enabled; bool _regex_checking; diff --git a/src/httpserver/webserver.hpp b/src/httpserver/webserver.hpp index abb1b63f..e5ffd6b8 100644 --- a/src/httpserver/webserver.hpp +++ b/src/httpserver/webserver.hpp @@ -29,8 +29,6 @@ #define METHOD_ERROR "Method not Allowed" #define NOT_METHOD_ERROR "Method not Acceptable" #define GENERIC_ERROR "Internal Error" -#define DEFAULT_WS_PORT 9898 -#define DEFAULT_WS_TIMEOUT 180 #include #include @@ -47,8 +45,6 @@ #include -#include "httpserver/details/http_resource_mirror.hpp" -#include "httpserver/details/event_tuple.hpp" #include "httpserver/create_webserver.hpp" namespace httpserver { @@ -57,76 +53,27 @@ template class http_resource; class http_response; struct cache_entry; template class event_supplier; +class create_webserver; namespace http { struct ip_representation; +struct httpserver_ska; }; +namespace details { + class http_resource_mirror; + class event_tuple; + class http_endpoint; + class daemon_item; + class modded_request; +} + /** * Class representing the webserver. Main class of the apis. **/ class webserver { public: - /** - * Constructor of the class. - * @param port Integer representing the port the webserver is listening on. - * @param start_method - * @param max_threads max number of serving threads (0 -> infty) - * @param max_connections max number of allowed connections (0 -> infty). - * @param memory_limit max memory allocated to serve requests (0 -> infty). - * @param per_IP_connection_limit max number of connections allowed for a single IP (0 -> infty). - * @param log_delegate logging_delegate object used to log - * @param validator request_validator object used to validate requestors - * @param unescaper unescaper object used to unescape urls. - * @param max_thread_stack_size max dimesion of request stack - * @param https_mem_key used with https. Private key used for requests. - * @param https_mem_cert used with https. Certificate used for requests. - * @param https_mem_trust used with https. CA Certificate used to trust request certificates. - * @param https_priorities used with https. Determinates the SSL/TLS version to be used. - * @param cred_type used with https. Daemon credential type. Can be NONE, certificate or anonymous. - * @param digest_auth_random used with https. Digest authentication nonce's seed. - * @param nonce_nc_size used with https. Size of an array of nonce and nonce counter map. - **/ - explicit webserver - ( - int port = DEFAULT_WS_PORT, - const http_utils::start_method_T& start_method = http_utils::INTERNAL_SELECT, - int max_threads = 0, - int max_connections = 0, - int memory_limit = 0, - int connection_timeout = DEFAULT_WS_TIMEOUT, - int per_IP_connection_limit = 0, - log_access_ptr log_access = 0x0, - log_error_ptr log_error = 0x0, - validator_ptr validator = 0x0, - unescaper_ptr unescaper = 0x0, - const struct sockaddr* bind_address = 0x0, - int bind_socket = 0, - int max_thread_stack_size = 0, - bool use_ssl = false, - bool use_ipv6 = false, - bool debug = false, - bool pedantic = false, - const std::string& https_mem_key = "", - const std::string& https_mem_cert = "", - const std::string& https_mem_trust = "", - const std::string& https_priorities = "", - const http_utils::cred_type_T& cred_type= http_utils::NONE, - const std::string digest_auth_random = "", //IT'S CORRECT TO PASS THIS PARAMETER BY VALUE - int nonce_nc_size = 0, - const http_utils::policy_T& default_policy = http_utils::ACCEPT, - bool basic_auth_enabled = true, - bool digest_auth_enabled = true, - bool regex_checking = true, - bool ban_system_enabled = true, - bool post_process_enabled = true, - render_ptr single_resource = 0x0, - render_ptr not_found_resource = 0x0, - render_ptr method_not_allowed_resource = 0x0, - render_ptr method_not_acceptable_resource = 0x0, - render_ptr internal_error_resource = 0x0 - ); webserver(const create_webserver& params); /** * Destructor of the class @@ -174,20 +121,20 @@ class webserver void send_message_to_topic(const std::string& topic, const std::string& message ); - void send_message_to_consumer(const httpserver_ska& connection_id, + void send_message_to_consumer(const http::httpserver_ska& connection_id, const std::string& message, bool to_lock = true ); void register_to_topics(const std::vector& topics, - const httpserver_ska& connection_id, int keepalive_secs = -1, + const http::httpserver_ska& connection_id, int keepalive_secs = -1, std::string keepalive_msg = "" ); - size_t read_message(const httpserver_ska& connection_id, + size_t read_message(const http::httpserver_ska& connection_id, std::string& message ); size_t get_topic_consumers(const std::string& topic, - std::set& consumers + std::set& consumers ); - bool pop_signaled(const httpserver_ska& consumer); + bool pop_signaled(const http::httpserver_ska& consumer); http_response* get_from_cache(const std::string& key, bool* valid, bool lock = false, bool write = false @@ -250,13 +197,7 @@ class webserver event_supplier* ev_supplier ) { - pthread_rwlock_wrlock(&runguard); - std::map::iterator it = - event_suppliers.find(id); - if(it != event_suppliers.end()) - delete it->second; - event_suppliers[id] = details::event_tuple(&ev_supplier); - pthread_rwlock_unlock(&runguard); + //TODO: implement method } void remove_event_supplier(const std::string& id); @@ -267,7 +208,7 @@ class webserver void sweet_kill(); private: const int port; - http_utils::start_method_T start_method; + http::http_utils::start_method_T start_method; const int max_threads; const int max_connections; const int memory_limit; @@ -288,11 +229,11 @@ class webserver const std::string https_mem_cert; const std::string https_mem_trust; const std::string https_priorities; - const http_utils::cred_type_T cred_type; + const http::http_utils::cred_type_T cred_type; const std::string digest_auth_random; const int nonce_nc_size; bool running; - const http_utils::policy_T default_policy; + const http::http_utils::policy_T default_policy; const bool basic_auth_enabled; const bool digest_auth_enabled; const bool regex_checking; @@ -315,19 +256,19 @@ class webserver int next_to_choose; pthread_rwlock_t cache_guard; #ifdef USE_CPP_ZEROX - std::unordered_set bans; - std::unordered_set allowances; + std::unordered_set bans; + std::unordered_set allowances; #else - std::set bans; - std::set allowances; + std::set bans; + std::set allowances; #endif - std::map > q_messages; - std::map > q_waitings; - std::map > q_blocks; - std::set q_signal; - std::map q_keepalives; - std::map > q_keepalives_mem; + std::map > q_messages; + std::map > q_waitings; + std::map > q_blocks; + std::set q_signal; + std::map q_keepalives; + std::map > q_keepalives_mem; pthread_rwlock_t comet_guard; std::vector daemons; @@ -337,7 +278,6 @@ class webserver webserver& operator=(const webserver& b); - void init(render_ptr single_resource); static void* select(void* self); static void* cleaner(void* self); @@ -432,7 +372,7 @@ class webserver bool use_internal_select() { - return this->start_method == http_utils::INTERNAL_SELECT; + return this->start_method == http::http_utils::INTERNAL_SELECT; } friend int policy_callback (void *cls, diff --git a/src/webserver.cpp b/src/webserver.cpp index 80aa115f..d758d2a9 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -41,6 +41,9 @@ #include "http_request.hpp" #include "http_endpoint.hpp" #include "string_utilities.hpp" +#include "details/http_resource_mirror.hpp" +#include "details/event_tuple.hpp" +#include "create_webserver.hpp" #include "webserver.hpp" #define _REENTRANT 1 @@ -353,87 +356,6 @@ static void ignore_sigpipe () } //WEBSERVER -webserver::webserver -( - int port, - const http_utils::start_method_T& start_method, - int max_threads, - int max_connections, - int memory_limit, - int connection_timeout, - int per_IP_connection_limit, - log_access_ptr log_access, - log_error_ptr log_error, - validator_ptr validator, - unescaper_ptr unescaper, - const struct sockaddr* bind_address, - int bind_socket, - int max_thread_stack_size, - bool use_ssl, - bool use_ipv6, - bool debug, - bool pedantic, - const string& https_mem_key, - const string& https_mem_cert, - const string& https_mem_trust, - const string& https_priorities, - const http_utils::cred_type_T& cred_type, - const string digest_auth_random, - int nonce_nc_size, - const http_utils::policy_T& default_policy, - bool basic_auth_enabled, - bool digest_auth_enabled, - bool regex_checking, - bool ban_system_enabled, - bool post_process_enabled, - render_ptr single_resource, - render_ptr not_found_resource, - render_ptr method_not_allowed_resource, - render_ptr method_not_acceptable_resource, - render_ptr internal_error_resource - -) : - port(port), - start_method(start_method), - max_threads(max_threads), - max_connections(max_connections), - memory_limit(memory_limit), - connection_timeout(connection_timeout), - per_IP_connection_limit(per_IP_connection_limit), - log_access(log_access), - log_error(log_error), - validator(validator), - unescaper(unescaper), - bind_address(bind_address), - bind_socket(bind_socket), - max_thread_stack_size(max_thread_stack_size), - use_ssl(use_ssl), - use_ipv6(use_ipv6), - debug(debug), - pedantic(pedantic), - https_mem_key(https_mem_key), - https_mem_cert(https_mem_cert), - https_mem_trust(https_mem_trust), - https_priorities(https_priorities), - cred_type(cred_type), - digest_auth_random(digest_auth_random), - nonce_nc_size(nonce_nc_size), - running(false), - default_policy(default_policy), - basic_auth_enabled(basic_auth_enabled), - digest_auth_enabled(digest_auth_enabled), - regex_checking(regex_checking), - ban_system_enabled(ban_system_enabled), - post_process_enabled(post_process_enabled), - not_found_resource(not_found_resource), - method_not_allowed_resource(method_not_allowed_resource), - method_not_acceptable_resource(method_not_acceptable_resource), - internal_error_resource(internal_error_resource), - next_to_choose(0) -{ - init(single_resource); -} - webserver::webserver(const create_webserver& params): port(params._port), start_method(params._start_method), @@ -472,11 +394,6 @@ webserver::webserver(const create_webserver& params): method_not_acceptable_resource(params._method_not_acceptable_resource), internal_error_resource(params._internal_error_resource), next_to_choose(0) -{ - init(params._single_resource); -} - -void webserver::init(render_ptr single_resource) { if(single_resource != 0x0) this->single_resource = true; From 22e7352c4d529ca9582fe7bb84000e00e5543c0c Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Mon, 20 Jan 2014 02:16:13 +0000 Subject: [PATCH 022/623] re-enabled register_event_supplier method --- src/httpserver/details/event_tuple.hpp | 1 + src/httpserver/webserver.hpp | 4 +++- src/webserver.cpp | 7 +++++++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/httpserver/details/event_tuple.hpp b/src/httpserver/details/event_tuple.hpp index e036d761..4fedafab 100644 --- a/src/httpserver/details/event_tuple.hpp +++ b/src/httpserver/details/event_tuple.hpp @@ -28,6 +28,7 @@ #include "httpserver/event_supplier.hpp" namespace httpserver { + namespace details { class http_endpoint; diff --git a/src/httpserver/webserver.hpp b/src/httpserver/webserver.hpp index e5ffd6b8..8c460e90 100644 --- a/src/httpserver/webserver.hpp +++ b/src/httpserver/webserver.hpp @@ -197,7 +197,7 @@ class webserver event_supplier* ev_supplier ) { - //TODO: implement method + register_event_supplier(id, details::event_tuple(&ev_supplier)); } void remove_event_supplier(const std::string& id); @@ -370,6 +370,8 @@ class webserver const char* version, const char* method ); + void register_event_supplier(const std::string& id, const details::event_tuple& evt); + bool use_internal_select() { return this->start_method == http::http_utils::INTERNAL_SELECT; diff --git a/src/webserver.cpp b/src/webserver.cpp index d758d2a9..ac7c7505 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -1936,4 +1936,11 @@ void webserver::remove_event_supplier(const std::string& id) pthread_rwlock_unlock(&runguard); } +void webserver::register_event_supplier(const std::string& id, const details::event_tuple& evt) +{ + pthread_rwlock_wrlock(&runguard); + event_suppliers.insert(std::pair(id, evt)); + pthread_rwlock_unlock(&runguard); +} + }; From 376f1385f1f61ea20202406ffb647c22e01f41dc Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Thu, 23 Jan 2014 23:41:51 +0000 Subject: [PATCH 023/623] Avoid linking problems due to details hiding --- src/Makefile.am | 4 ++-- src/httpserver.hpp | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Makefile.am b/src/Makefile.am index 1a3a2dea..d7c80795 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -20,8 +20,8 @@ INCLUDES = -I../ -I$(srcdir)/httpserver/ METASOURCES = AUTO lib_LTLIBRARIES = libhttpserver.la libhttpserver_la_SOURCES = string_utilities.cpp webserver.cpp http_utils.cpp http_endpoint.cpp http_request.cpp http_response.cpp http_resource.cpp -noinst_HEADERS = httpserver/details/event_tuple.hpp httpserver/details/http_resource_mirror.hpp httpserver/string_utilities.hpp gettext.h -nobase_include_HEADERS = httpserver.hpp httpserver/create_webserver.hpp httpserver/webserver.hpp httpserver/http_utils.hpp httpserver/http_endpoint.hpp httpserver/http_request.hpp httpserver/http_response.hpp httpserver/http_resource.hpp httpserver/binders.hpp httpserver/event_supplier.hpp +noinst_HEADERS = httpserver/string_utilities.hpp gettext.h +nobase_include_HEADERS = httpserver.hpp httpserver/create_webserver.hpp httpserver/webserver.hpp httpserver/http_utils.hpp httpserver/http_endpoint.hpp httpserver/http_request.hpp httpserver/http_response.hpp httpserver/http_resource.hpp httpserver/binders.hpp httpserver/event_supplier.hpp httpserver/details/event_tuple.hpp httpserver/details/http_resource_mirror.hpp AM_CXXFLAGS += -fPIC -Wall libhttpserver_la_LIBADD = -lmicrohttpd libhttpserver_la_LDFLAGS = diff --git a/src/httpserver.hpp b/src/httpserver.hpp index 08d69ffd..01917e5b 100644 --- a/src/httpserver.hpp +++ b/src/httpserver.hpp @@ -9,6 +9,8 @@ #include "httpserver/http_response.hpp" #include "httpserver/http_request.hpp" #include "httpserver/event_supplier.hpp" +#include "httpserver/details/event_tuple.hpp" +#include "httpserver/details/http_resource_mirror.hpp" #include "httpserver/webserver.hpp" #endif From e40eccfb12c304756143da64de45687e8c33c9cf Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sat, 25 Jan 2014 13:16:11 +0000 Subject: [PATCH 024/623] Moved http_endpoint to subdir details Aligned with its namespace --- src/Makefile.am | 2 +- src/http_endpoint.cpp | 34 +++++++++---------- src/httpserver.hpp | 2 +- .../{ => details}/http_endpoint.hpp | 0 src/webserver.cpp | 2 +- 5 files changed, 20 insertions(+), 20 deletions(-) rename src/httpserver/{ => details}/http_endpoint.hpp (100%) diff --git a/src/Makefile.am b/src/Makefile.am index d7c80795..560205bf 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -21,7 +21,7 @@ METASOURCES = AUTO lib_LTLIBRARIES = libhttpserver.la libhttpserver_la_SOURCES = string_utilities.cpp webserver.cpp http_utils.cpp http_endpoint.cpp http_request.cpp http_response.cpp http_resource.cpp noinst_HEADERS = httpserver/string_utilities.hpp gettext.h -nobase_include_HEADERS = httpserver.hpp httpserver/create_webserver.hpp httpserver/webserver.hpp httpserver/http_utils.hpp httpserver/http_endpoint.hpp httpserver/http_request.hpp httpserver/http_response.hpp httpserver/http_resource.hpp httpserver/binders.hpp httpserver/event_supplier.hpp httpserver/details/event_tuple.hpp httpserver/details/http_resource_mirror.hpp +nobase_include_HEADERS = httpserver.hpp httpserver/create_webserver.hpp httpserver/webserver.hpp httpserver/http_utils.hpp httpserver/details/http_endpoint.hpp httpserver/http_request.hpp httpserver/http_response.hpp httpserver/http_resource.hpp httpserver/binders.hpp httpserver/event_supplier.hpp httpserver/details/event_tuple.hpp httpserver/details/http_resource_mirror.hpp AM_CXXFLAGS += -fPIC -Wall libhttpserver_la_LIBADD = -lmicrohttpd libhttpserver_la_LDFLAGS = diff --git a/src/http_endpoint.cpp b/src/http_endpoint.cpp index a2b0dcb4..2a1c04ec 100644 --- a/src/http_endpoint.cpp +++ b/src/http_endpoint.cpp @@ -14,11 +14,11 @@ You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "http_endpoint.hpp" +#include "details/http_endpoint.hpp" #include "http_utils.hpp" #include "string_utilities.hpp" @@ -67,7 +67,7 @@ http_endpoint::http_endpoint { for(unsigned int i = 0; i< parts.size(); i++) { - if((parts[i] != "") && (parts[i][0] != '{')) + if((parts[i] != "") && (parts[i][0] != '{')) { if(first) { @@ -85,14 +85,14 @@ http_endpoint::http_endpoint { this->url_modded += "/" + parts[i]; } - } - else + } + else { if( - (parts[i].size() >= 3) && - (parts[i][0] == '{') && - (parts[i][parts[i].size() - 1] == '}') - ) + (parts[i].size() >= 3) && + (parts[i][0] == '{') && + (parts[i][parts[i].size() - 1] == '}') + ) { int bar = parts[i].find_first_of('|'); if(bar != (int)string::npos) @@ -128,8 +128,8 @@ http_endpoint::http_endpoint } } this->chunk_positions.push_back(i); - } - else + } + else { throw bad_http_endpoint(); } @@ -156,7 +156,7 @@ http_endpoint::http_endpoint if(use_regex) { this->url_modded += "$"; - regcomp(&(this->re_url_modded), url_modded.c_str(), + regcomp(&(this->re_url_modded), url_modded.c_str(), REG_EXTENDED|REG_ICASE|REG_NOSUB ); reg_compiled = true; @@ -173,7 +173,7 @@ http_endpoint::http_endpoint(const http_endpoint& h): reg_compiled(h.reg_compiled) { if(this->reg_compiled) - regcomp(&(this->re_url_modded), url_modded.c_str(), + regcomp(&(this->re_url_modded), url_modded.c_str(), REG_EXTENDED|REG_ICASE|REG_NOSUB ); } @@ -185,7 +185,7 @@ http_endpoint& http_endpoint::operator =(const http_endpoint& h) this->family_url = h.family_url; this->reg_compiled = h.reg_compiled; if(this->reg_compiled) - regcomp(&(this->re_url_modded), url_modded.c_str(), + regcomp(&(this->re_url_modded), url_modded.c_str(), REG_EXTENDED|REG_ICASE|REG_NOSUB ); this->url_pars = h.url_pars; @@ -194,12 +194,12 @@ http_endpoint& http_endpoint::operator =(const http_endpoint& h) return *this; } -bool http_endpoint::operator <(const http_endpoint& b) const +bool http_endpoint::operator <(const http_endpoint& b) const { COMPARATOR(this->url_modded, b.url_modded, toupper); } -bool http_endpoint::match(const http_endpoint& url) const +bool http_endpoint::match(const http_endpoint& url) const { if(this->family_url && (url.url_pieces.size() >= this->url_pieces.size())) { @@ -220,7 +220,7 @@ bool http_endpoint::match(const http_endpoint& url) const return regexec(&(this->re_url_modded), nn.c_str(), 0, NULL, 0) == 0; } else - return regexec(&(this->re_url_modded), + return regexec(&(this->re_url_modded), url.url_modded.c_str(), 0, NULL, 0) == 0; } diff --git a/src/httpserver.hpp b/src/httpserver.hpp index 01917e5b..07d7edbb 100644 --- a/src/httpserver.hpp +++ b/src/httpserver.hpp @@ -4,7 +4,7 @@ #define _HTTPSERVER_HPP_INSIDE_ #include "httpserver/http_utils.hpp" -#include "httpserver/http_endpoint.hpp" +#include "httpserver/details/http_endpoint.hpp" #include "httpserver/http_resource.hpp" #include "httpserver/http_response.hpp" #include "httpserver/http_request.hpp" diff --git a/src/httpserver/http_endpoint.hpp b/src/httpserver/details/http_endpoint.hpp similarity index 100% rename from src/httpserver/http_endpoint.hpp rename to src/httpserver/details/http_endpoint.hpp diff --git a/src/webserver.cpp b/src/webserver.cpp index ac7c7505..aeff7203 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -39,7 +39,7 @@ #include "http_resource.hpp" #include "http_response.hpp" #include "http_request.hpp" -#include "http_endpoint.hpp" +#include "details/http_endpoint.hpp" #include "string_utilities.hpp" #include "details/http_resource_mirror.hpp" #include "details/event_tuple.hpp" From 453d744182206d08d0c2733cefb3acd9353b6cac Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sat, 25 Jan 2014 13:48:50 +0000 Subject: [PATCH 025/623] Extracted modded_request and http_response_ptr from webserver.cpp This improves readability --- src/Makefile.am | 2 +- src/httpserver/details/http_response_ptr.hpp | 91 ++++++++++++++ src/httpserver/details/modded_request.hpp | 51 ++++++++ src/webserver.cpp | 121 +------------------ 4 files changed, 144 insertions(+), 121 deletions(-) create mode 100644 src/httpserver/details/http_response_ptr.hpp create mode 100644 src/httpserver/details/modded_request.hpp diff --git a/src/Makefile.am b/src/Makefile.am index 560205bf..cb057a7a 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -20,7 +20,7 @@ INCLUDES = -I../ -I$(srcdir)/httpserver/ METASOURCES = AUTO lib_LTLIBRARIES = libhttpserver.la libhttpserver_la_SOURCES = string_utilities.cpp webserver.cpp http_utils.cpp http_endpoint.cpp http_request.cpp http_response.cpp http_resource.cpp -noinst_HEADERS = httpserver/string_utilities.hpp gettext.h +noinst_HEADERS = httpserver/string_utilities.hpp httpserver/details/modded_request.hpp httpserver/details/http_response_ptr.hpp gettext.h nobase_include_HEADERS = httpserver.hpp httpserver/create_webserver.hpp httpserver/webserver.hpp httpserver/http_utils.hpp httpserver/details/http_endpoint.hpp httpserver/http_request.hpp httpserver/http_response.hpp httpserver/http_resource.hpp httpserver/binders.hpp httpserver/event_supplier.hpp httpserver/details/event_tuple.hpp httpserver/details/http_resource_mirror.hpp AM_CXXFLAGS += -fPIC -Wall libhttpserver_la_LIBADD = -lmicrohttpd diff --git a/src/httpserver/details/http_response_ptr.hpp b/src/httpserver/details/http_response_ptr.hpp new file mode 100644 index 00000000..5d7f2a07 --- /dev/null +++ b/src/httpserver/details/http_response_ptr.hpp @@ -0,0 +1,91 @@ +namespace httpserver +{ + +class webserver; + +namespace details +{ + +struct http_response_ptr +{ + public: + http_response_ptr(): + res(0x0), + num_references(0x0) + { + num_references = new int(0); + } + http_response_ptr(http_response* res): + res(res), + num_references(0x0) + { + num_references = new int(0); + } + http_response_ptr(const http_response_ptr& b): + res(b.res), + num_references(b.num_references) + { + (*num_references)++; + } + ~http_response_ptr() + { + if(num_references) + { + if((*num_references) == 0) + { + if(res && res->autodelete) + { + delete res; + res = 0x0; + } + delete num_references; + } + else + (*num_references)--; + } + } + http_response& operator* () + { + return *res; + } + http_response* operator-> () + { + return res; + } + http_response* ptr() + { + return res; + } + http_response_ptr& operator= (const http_response_ptr& b) + { + if( this != &b) + { + if(num_references) + { + if((*num_references) == 0) + { + if(res && res->autodelete) + { + delete res; + res = 0x0; + } + delete num_references; + } + else + (*num_references)--; + } + + res = b.res; + num_references = b.num_references; + (*num_references)++; + } + return *this; + } + private: + http_response* res; + int* num_references; + friend class ::httpserver::webserver; +}; + +} //details +} //httpserver diff --git a/src/httpserver/details/modded_request.hpp b/src/httpserver/details/modded_request.hpp new file mode 100644 index 00000000..0b9b096e --- /dev/null +++ b/src/httpserver/details/modded_request.hpp @@ -0,0 +1,51 @@ +#include "binders.hpp" +#include "details/http_response_ptr.hpp" + +namespace httpserver +{ + +namespace details +{ + +struct modded_request +{ + struct MHD_PostProcessor *pp; + std::string* complete_uri; + std::string* standardized_url; + webserver* ws; + + const binders::functor_two< + const http_request&, http_response**, void + > http_resource_mirror::*callback; + + http_request* dhr; + http_response_ptr dhrs; + bool second; + + modded_request(): + pp(0x0), + complete_uri(0x0), + standardized_url(0x0), + ws(0x0), + dhr(0x0), + dhrs(0x0), + second(false) + { + } + ~modded_request() + { + if (NULL != pp) + { + MHD_destroy_post_processor (pp); + } + if(second) + delete dhr; //TODO: verify. It could be an error + delete complete_uri; + delete standardized_url; + } + +}; + +} //details + +} //httpserver diff --git a/src/webserver.cpp b/src/webserver.cpp index aeff7203..e2962840 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -45,6 +45,7 @@ #include "details/event_tuple.hpp" #include "create_webserver.hpp" #include "webserver.hpp" +#include "details/modded_request.hpp" #define _REENTRANT 1 @@ -75,126 +76,6 @@ struct daemon_item } }; -struct http_response_ptr -{ - public: - http_response_ptr(): - res(0x0), - num_references(0x0) - { - num_references = new int(0); - } - http_response_ptr(http_response* res): - res(res), - num_references(0x0) - { - num_references = new int(0); - } - http_response_ptr(const http_response_ptr& b): - res(b.res), - num_references(b.num_references) - { - (*num_references)++; - } - ~http_response_ptr() - { - if(num_references) - { - if((*num_references) == 0) - { - if(res && res->autodelete) - { - delete res; - res = 0x0; - } - delete num_references; - } - else - (*num_references)--; - } - } - http_response& operator* () - { - return *res; - } - http_response* operator-> () - { - return res; - } - http_response* ptr() - { - return res; - } - http_response_ptr& operator= (const http_response_ptr& b) - { - if( this != &b) - { - if(num_references) - { - if((*num_references) == 0) - { - if(res && res->autodelete) - { - delete res; - res = 0x0; - } - delete num_references; - } - else - (*num_references)--; - } - - res = b.res; - num_references = b.num_references; - (*num_references)++; - } - return *this; - } - private: - http_response* res; - int* num_references; - friend class ::httpserver::webserver; -}; - -struct modded_request -{ - struct MHD_PostProcessor *pp; - std::string* complete_uri; - std::string* standardized_url; - webserver* ws; - - const binders::functor_two< - const http_request&, http_response**, void - > http_resource_mirror::*callback; - - http_request* dhr; - http_response_ptr dhrs; - bool second; - - modded_request(): - pp(0x0), - complete_uri(0x0), - standardized_url(0x0), - ws(0x0), - dhr(0x0), - dhrs(0x0), - second(false) - { - } - ~modded_request() - { - if (NULL != pp) - { - MHD_destroy_post_processor (pp); - } - if(second) - delete dhr; //TODO: verify. It could be an error - delete complete_uri; - delete standardized_url; - } - -}; - void empty_render(const http_request& r, http_response** res) { *res = new http_string_response("", 200); From dcfa5a7238042c4c6b104d43aa9b28c7545d5930 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sat, 25 Jan 2014 15:21:17 +0000 Subject: [PATCH 026/623] Extracted other classes from webserver.cpp --- src/Makefile.am | 2 +- src/httpserver/details/cache_entry.hpp | 137 ++++++++++++++++++ src/httpserver/details/http_response_ptr.hpp | 25 ++++ src/httpserver/details/modded_request.hpp | 25 ++++ src/httpserver/http_response.hpp | 10 +- src/httpserver/webserver.hpp | 18 +-- src/webserver.cpp | 142 +++---------------- 7 files changed, 223 insertions(+), 136 deletions(-) create mode 100644 src/httpserver/details/cache_entry.hpp diff --git a/src/Makefile.am b/src/Makefile.am index cb057a7a..d3dd9f66 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -20,7 +20,7 @@ INCLUDES = -I../ -I$(srcdir)/httpserver/ METASOURCES = AUTO lib_LTLIBRARIES = libhttpserver.la libhttpserver_la_SOURCES = string_utilities.cpp webserver.cpp http_utils.cpp http_endpoint.cpp http_request.cpp http_response.cpp http_resource.cpp -noinst_HEADERS = httpserver/string_utilities.hpp httpserver/details/modded_request.hpp httpserver/details/http_response_ptr.hpp gettext.h +noinst_HEADERS = httpserver/string_utilities.hpp httpserver/details/modded_request.hpp httpserver/details/http_response_ptr.hpp httpserver/details/cache_entry.hpp gettext.h nobase_include_HEADERS = httpserver.hpp httpserver/create_webserver.hpp httpserver/webserver.hpp httpserver/http_utils.hpp httpserver/details/http_endpoint.hpp httpserver/http_request.hpp httpserver/http_response.hpp httpserver/http_resource.hpp httpserver/binders.hpp httpserver/event_supplier.hpp httpserver/details/event_tuple.hpp httpserver/details/http_resource_mirror.hpp AM_CXXFLAGS += -fPIC -Wall libhttpserver_la_LIBADD = -lmicrohttpd diff --git a/src/httpserver/details/cache_entry.hpp b/src/httpserver/details/cache_entry.hpp new file mode 100644 index 00000000..d6678825 --- /dev/null +++ b/src/httpserver/details/cache_entry.hpp @@ -0,0 +1,137 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#ifndef _CACHE_ENTRY_HPP_ +#define _CACHE_ENTRY_HPP_ + +#include +#include +#include "details/http_response_ptr.hpp" + +namespace httpserver +{ +namespace details +{ + +struct pthread_t_comparator +{ + bool operator()(const pthread_t& t1, const pthread_t& t2) const + { + return pthread_equal(t1, t2); + } +}; + +struct cache_entry +{ + long ts; + int validity; + details::http_response_ptr response; + pthread_rwlock_t elem_guard; + pthread_mutex_t lock_guard; + std::set lockers; + + cache_entry(): + ts(-1), + validity(-1) + { + pthread_rwlock_init(&elem_guard, NULL); + pthread_mutex_init(&lock_guard, NULL); + } + + ~cache_entry() + { + pthread_rwlock_destroy(&elem_guard); + pthread_mutex_destroy(&lock_guard); + } + + cache_entry(const cache_entry& b): + ts(b.ts), + validity(b.validity), + response(b.response), + elem_guard(b.elem_guard), + lock_guard(b.lock_guard) + { + } + + void operator= (const cache_entry& b) + { + ts = b.ts; + validity = b.validity; + response = b.response; + pthread_rwlock_destroy(&elem_guard); + pthread_mutex_destroy(&lock_guard); + elem_guard = b.elem_guard; + } + + cache_entry( + details::http_response_ptr response, + long ts = -1, + int validity = -1 + ): + ts(ts), + validity(validity), + response(response) + { + pthread_rwlock_init(&elem_guard, NULL); + pthread_mutex_init(&lock_guard, NULL); + } + + void lock(bool write = false) + { + pthread_mutex_lock(&lock_guard); + pthread_t tid = pthread_self(); + if(!lockers.count(tid)) + { + if(write) + { + lockers.insert(tid); + pthread_mutex_unlock(&lock_guard); + pthread_rwlock_wrlock(&elem_guard); + } + else + { + lockers.insert(tid); + pthread_mutex_unlock(&lock_guard); + pthread_rwlock_rdlock(&elem_guard); + } + } + else + pthread_mutex_unlock(&lock_guard); + } + + void unlock() + { + pthread_mutex_lock(&lock_guard); + { + pthread_t tid = pthread_self(); + if(lockers.count(tid)) + { + lockers.erase(tid); + pthread_rwlock_unlock(&elem_guard); + } + } + pthread_mutex_unlock(&lock_guard); + } +}; + +} //details +} //httpserver + +#endif //_CACHE_ENTRY_HPP_ diff --git a/src/httpserver/details/http_response_ptr.hpp b/src/httpserver/details/http_response_ptr.hpp index 5d7f2a07..3f867e6e 100644 --- a/src/httpserver/details/http_response_ptr.hpp +++ b/src/httpserver/details/http_response_ptr.hpp @@ -1,3 +1,26 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#ifndef _HTTP_RESPONSE_PTR_HPP_ +#define _HTTP_RESPONSE_PTR_HPP_ + namespace httpserver { @@ -89,3 +112,5 @@ struct http_response_ptr } //details } //httpserver + +#endif //_HTTP_RESPONSE_PTR_HPP_ diff --git a/src/httpserver/details/modded_request.hpp b/src/httpserver/details/modded_request.hpp index 0b9b096e..57ccb344 100644 --- a/src/httpserver/details/modded_request.hpp +++ b/src/httpserver/details/modded_request.hpp @@ -1,3 +1,26 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#ifndef _MODDED_REQUEST_HPP_ +#define _MODDED_REQUEST_HPP_ + #include "binders.hpp" #include "details/http_response_ptr.hpp" @@ -49,3 +72,5 @@ struct modded_request } //details } //httpserver + +#endif //_MODDED_REQUEST_HPP_ diff --git a/src/httpserver/http_response.hpp b/src/httpserver/http_response.hpp index 0e850001..213da1e8 100644 --- a/src/httpserver/http_response.hpp +++ b/src/httpserver/http_response.hpp @@ -36,7 +36,6 @@ namespace httpserver { class webserver; -struct cache_entry; namespace http { @@ -48,6 +47,7 @@ namespace details { struct http_response_ptr; ssize_t cb(void*, uint64_t, char*, size_t); + struct cache_entry; }; using namespace http; @@ -84,7 +84,7 @@ http_response \ int keepalive_secs,\ const std::string keepalive_msg,\ const std::string send_topic,\ - cache_entry* ce\ + details::cache_entry* ce\ ):\ content(content),\ response_code(response_code),\ @@ -139,7 +139,7 @@ class http_response int keepalive_secs = -1, const std::string keepalive_msg = "", const std::string send_topic = "", - cache_entry* ce = 0x0 + details::cache_entry* ce = 0x0 ): content(content), response_code(response_code), @@ -441,7 +441,7 @@ class http_response struct MHD_Connection* underlying_connection; void(*ca)(void*); void* closure_data; - cache_entry* ce; + details::cache_entry* ce; const get_raw_response_t get_raw_response; const decorate_response_t decorate_response; @@ -680,7 +680,7 @@ class cache_response : public http_response } cache_response ( - cache_entry* ce + details::cache_entry* ce ) : http_response(this, "", 200, "", true, "", "", false, std::vector(), -1, "", "", ce ) diff --git a/src/httpserver/webserver.hpp b/src/httpserver/webserver.hpp index 8c460e90..8c368d94 100644 --- a/src/httpserver/webserver.hpp +++ b/src/httpserver/webserver.hpp @@ -51,7 +51,6 @@ namespace httpserver { template class http_resource; class http_response; -struct cache_entry; template class event_supplier; class create_webserver; @@ -66,6 +65,7 @@ namespace details { class http_endpoint; class daemon_item; class modded_request; + struct cache_entry; } /** @@ -140,11 +140,11 @@ class webserver bool lock = false, bool write = false ); http_response* get_from_cache(const std::string& key, bool* valid, - cache_entry** ce, bool lock = false, bool write = false + details::cache_entry** ce, bool lock = false, bool write = false ); - void lock_cache_element(cache_entry* ce, bool write = false); - void unlock_cache_element(cache_entry* ce); - cache_entry* put_in_cache(const std::string& key, http_response* value, + void lock_cache_element(details::cache_entry* ce, bool write = false); + void unlock_cache_element(details::cache_entry* ce); + details::cache_entry* put_in_cache(const std::string& key, http_response* value, bool* new_elem, bool lock = false, bool write = false, int validity = -1 ); @@ -252,7 +252,7 @@ class webserver std::map registered_resources; std::map registered_resources_str; - std::map response_cache; + std::map response_cache; int next_to_choose; pthread_rwlock_t cache_guard; #ifdef USE_CPP_ZEROX @@ -338,9 +338,9 @@ class webserver void **con_cls, int upgrade_socket ); - static void unlock_cache_entry(cache_entry*); - static void lock_cache_entry(cache_entry*); - static void get_response(cache_entry*, http_response** res); + static void unlock_cache_entry(details::cache_entry*); + static void lock_cache_entry(details::cache_entry*); + static void get_response(details::cache_entry*, http_response** res); int bodyless_requests_answer(MHD_Connection* connection, const char* method, const char* version, diff --git a/src/webserver.cpp b/src/webserver.cpp index e2962840..dbe42eee 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -46,6 +46,7 @@ #include "create_webserver.hpp" #include "webserver.hpp" #include "details/modded_request.hpp" +#include "details/cache_entry.hpp" #define _REENTRANT 1 @@ -93,107 +94,6 @@ bool empty_is_allowed(const std::string& method) } -struct pthread_t_comparator -{ - bool operator()(const pthread_t& t1, const pthread_t& t2) const - { - return pthread_equal(t1, t2); - } -}; - -struct cache_entry -{ - long ts; - int validity; - details::http_response_ptr response; - pthread_rwlock_t elem_guard; - pthread_mutex_t lock_guard; - set lockers; - - cache_entry(): - ts(-1), - validity(-1) - { - pthread_rwlock_init(&elem_guard, NULL); - pthread_mutex_init(&lock_guard, NULL); - } - - ~cache_entry() - { - pthread_rwlock_destroy(&elem_guard); - pthread_mutex_destroy(&lock_guard); - } - - cache_entry(const cache_entry& b): - ts(b.ts), - validity(b.validity), - response(b.response), - elem_guard(b.elem_guard), - lock_guard(b.lock_guard) - { - } - - void operator= (const cache_entry& b) - { - ts = b.ts; - validity = b.validity; - response = b.response; - pthread_rwlock_destroy(&elem_guard); - pthread_mutex_destroy(&lock_guard); - elem_guard = b.elem_guard; - } - - cache_entry( - details::http_response_ptr response, - long ts = -1, - int validity = -1 - ): - ts(ts), - validity(validity), - response(response) - { - pthread_rwlock_init(&elem_guard, NULL); - pthread_mutex_init(&lock_guard, NULL); - } - - void lock(bool write = false) - { - pthread_mutex_lock(&lock_guard); - pthread_t tid = pthread_self(); - if(!lockers.count(tid)) - { - if(write) - { - lockers.insert(tid); - pthread_mutex_unlock(&lock_guard); - pthread_rwlock_wrlock(&elem_guard); - } - else - { - lockers.insert(tid); - pthread_mutex_unlock(&lock_guard); - pthread_rwlock_rdlock(&elem_guard); - } - } - else - pthread_mutex_unlock(&lock_guard); - } - - void unlock() - { - pthread_mutex_lock(&lock_guard); - { - pthread_t tid = pthread_self(); - if(lockers.count(tid)) - { - lockers.erase(tid); - pthread_rwlock_unlock(&elem_guard); - } - } - pthread_mutex_unlock(&lock_guard); - } -}; - using namespace http; int policy_callback (void *, const struct sockaddr*, socklen_t); @@ -1627,21 +1527,21 @@ http_response* webserver::get_from_cache( bool write ) { - cache_entry* ce = 0x0; + details::cache_entry* ce = 0x0; return get_from_cache(key, valid, &ce, lock, write); } http_response* webserver::get_from_cache( const std::string& key, bool* valid, - cache_entry** ce, + details::cache_entry** ce, bool lock, bool write ) { pthread_rwlock_rdlock(&cache_guard); *valid = true; - map::iterator it(response_cache.find(key)); + map::iterator it(response_cache.find(key)); if(it != response_cache.end()) { if(lock) @@ -1668,7 +1568,7 @@ http_response* webserver::get_from_cache( bool webserver::is_valid(const std::string& key) { pthread_rwlock_rdlock(&cache_guard); - map::iterator it(response_cache.find(key)); + map::iterator it(response_cache.find(key)); if(it != response_cache.end()) { if((*it).second->validity != -1) @@ -1696,19 +1596,19 @@ bool webserver::is_valid(const std::string& key) return false; } -void webserver::lock_cache_element(cache_entry* ce, bool write) +void webserver::lock_cache_element(details::cache_entry* ce, bool write) { if(ce) ce->lock(write); } -void webserver::unlock_cache_element(cache_entry* ce) +void webserver::unlock_cache_element(details::cache_entry* ce) { if(ce) ce->unlock(); } -cache_entry* webserver::put_in_cache( +details::cache_entry* webserver::put_in_cache( const std::string& key, http_response* value, bool* new_elem, @@ -1718,8 +1618,8 @@ cache_entry* webserver::put_in_cache( ) { pthread_rwlock_wrlock(&cache_guard); - map::iterator it(response_cache.find(key)); - cache_entry* to_ret; + map::iterator it(response_cache.find(key)); + details::cache_entry* to_ret; bool already_in = false; if(it != response_cache.end()) { @@ -1736,9 +1636,9 @@ cache_entry* webserver::put_in_cache( } else { - pair::iterator, bool> res = - response_cache.insert(pair( - key, new cache_entry(value)) + pair::iterator, bool> res = + response_cache.insert(pair( + key, new details::cache_entry(value)) ); to_ret = (*res.first).second; @@ -1759,9 +1659,9 @@ cache_entry* webserver::put_in_cache( } else { - pair::iterator, bool> res = - response_cache.insert(pair( - key, new cache_entry(value, now.tv_sec, validity)) + pair::iterator, bool> res = + response_cache.insert(pair( + key, new details::cache_entry(value, now.tv_sec, validity)) ); to_ret = (*res.first).second; *new_elem = res.second; @@ -1778,10 +1678,10 @@ cache_entry* webserver::put_in_cache( void webserver::remove_from_cache(const std::string& key) { pthread_rwlock_wrlock(&cache_guard); - map::iterator it(response_cache.find(key)); + map::iterator it(response_cache.find(key)); if(it != response_cache.end()) { - cache_entry* ce = (*it).second; + details::cache_entry* ce = (*it).second; response_cache.erase(it); delete ce; } @@ -1795,17 +1695,17 @@ void webserver::clean_cache() pthread_rwlock_unlock(&cache_guard); } -void webserver::unlock_cache_entry(cache_entry* ce) +void webserver::unlock_cache_entry(details::cache_entry* ce) { ce->unlock(); } -void webserver::lock_cache_entry(cache_entry* ce) +void webserver::lock_cache_entry(details::cache_entry* ce) { ce->lock(); } -void webserver::get_response(cache_entry* ce, http_response** res) +void webserver::get_response(details::cache_entry* ce, http_response** res) { *res = ce->response.ptr(); } From 71f224cca794989640fb1cd17ef860002a0c878a Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sat, 25 Jan 2014 15:34:24 +0000 Subject: [PATCH 027/623] Enforced immutability to webserver class --- src/httpserver/webserver.hpp | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/src/httpserver/webserver.hpp b/src/httpserver/webserver.hpp index 8c368d94..b55522b6 100644 --- a/src/httpserver/webserver.hpp +++ b/src/httpserver/webserver.hpp @@ -162,36 +162,16 @@ class webserver return this->log_error; } - void set_access_logger(log_access_ptr log_access) - { - this->log_access = log_access; - } - - void set_error_logger(log_error_ptr log_error) - { - this->log_error = log_error; - } - const validator_ptr get_request_validator() const { return this->validator; } - void set_request_validator(validator_ptr validator) - { - this->validator = validator; - } - const unescaper_ptr get_unescaper() const { return this->unescaper; } - void set_unescaper(unescaper_ptr unescaper) - { - this->unescaper = unescaper; - } - template void register_event_supplier(const std::string& id, event_supplier* ev_supplier From 5063054187f3cd6b6dec234cbee8936cc613b1af Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sat, 25 Jan 2014 15:34:43 +0000 Subject: [PATCH 028/623] moved to version 0.7.0 --- ChangeLog | 5 +++++ configure.ac | 4 ++-- debian/changelog.in | 7 +++++++ 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/ChangeLog b/ChangeLog index b9c49b07..c7e6e96c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +Sat Jan 25 16:31:03 2014 +0100 + Cleaned-up webserver.cpp code to extract secondary classes + Enforced immutability of webserver class + Enabled library to compile on g++ 4.1.2 + Wed Oct 31 17:59:40 2012 +0100 Added parameter in http_response to specify if it needs to be deleted by WS - Sebastiano Merlino diff --git a/configure.ac b/configure.ac index ed2fa5c6..befc4ff3 100644 --- a/configure.ac +++ b/configure.ac @@ -21,8 +21,8 @@ AC_PREREQ(2.57) m4_define([libhttpserver_MAJOR_VERSION],[0])dnl -m4_define([libhttpserver_MINOR_VERSION],[6])dnl -m4_define([libhttpserver_REVISION],[3])dnl +m4_define([libhttpserver_MINOR_VERSION],[7])dnl +m4_define([libhttpserver_REVISION],[0])dnl m4_define([libhttpserver_PKG_VERSION],[libhttpserver_MAJOR_VERSION.libhttpserver_MINOR_VERSION.libhttpserver_REVISION])dnl m4_define([libhttpserver_LDF_VERSION],[libhttpserver_MAJOR_VERSION:libhttpserver_MINOR_VERSION:libhttpserver_REVISION])dnl AC_INIT([libhttpserver], libhttpserver_PKG_VERSION, [electrictwister2000@gmail.com]) diff --git a/debian/changelog.in b/debian/changelog.in index f3fb3630..157c7a38 100644 --- a/debian/changelog.in +++ b/debian/changelog.in @@ -1,3 +1,10 @@ +libhttpserver (0.7.0) unstable; urgency=low + * Cleaned-up webserver.cpp code to extract secondary classes + * Enforced immutability of webserver class + * Enabled library to compile on g++ 4.1.2 + + -- Sebastiano Merlino Sat, 25 Jan 2014 16:31:03 +0100 + libhttpserver (0.6.3) unstable; urgency=low * Fixed some bugs in exception management From 36969042c7ff8d602d2a4ed7e9ae37ce00a2edde Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sat, 25 Jan 2014 15:45:32 +0000 Subject: [PATCH 029/623] Updated copyright informations --- Makefile.am | 2 +- Makefile.cvs | 2 +- bootstrap | 2 +- configure.ac | 2 +- src/Makefile.am | 2 +- src/http_endpoint.cpp | 2 +- src/http_request.cpp | 4 +- src/http_resource.cpp | 4 +- src/http_response.cpp | 2 +- src/http_utils.cpp | 96 +++++++++---------- src/httpserver/binders.hpp | 2 +- src/httpserver/create_webserver.hpp | 2 +- src/httpserver/details/cache_entry.hpp | 2 +- src/httpserver/details/event_tuple.hpp | 2 +- src/httpserver/details/http_endpoint.hpp | 6 +- .../details/http_resource_mirror.hpp | 2 +- src/httpserver/details/http_response_ptr.hpp | 2 +- src/httpserver/details/modded_request.hpp | 2 +- src/httpserver/event_supplier.hpp | 2 +- src/httpserver/http_request.hpp | 14 +-- src/httpserver/http_resource.hpp | 6 +- src/httpserver/http_response.hpp | 2 +- src/httpserver/http_utils.hpp | 20 ++-- src/httpserver/string_utilities.hpp | 4 +- src/httpserver/webserver.hpp | 2 +- src/string_utilities.cpp | 26 ++--- src/webserver.cpp | 2 +- test/Makefile.am | 2 +- 28 files changed, 109 insertions(+), 109 deletions(-) diff --git a/Makefile.am b/Makefile.am index 05215d4c..4001ab70 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,6 +1,6 @@ # # This file is part of libhttpserver -# Copyright (C) 2011 Sebastiano Merlino +# Copyright (C) 2011, 2012, 2013, 2014 Sebastiano Merlino # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public diff --git a/Makefile.cvs b/Makefile.cvs index a4cc041b..9d45cc6d 100644 --- a/Makefile.cvs +++ b/Makefile.cvs @@ -1,6 +1,6 @@ # # This file is part of libhttpserver -# Copyright (C) 2011 Sebastiano Merlino +# Copyright (C) 2011, 2012, 2013, 2014 Sebastiano Merlino # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public diff --git a/bootstrap b/bootstrap index 9c0c6963..0b50547f 100755 --- a/bootstrap +++ b/bootstrap @@ -1,7 +1,7 @@ #!/bin/sh # # This file is part of libhttpserver -# Copyright (C) 2011 Sebastiano Merlino +# Copyright (C) 2011, 2012, 2013, 2014 Sebastiano Merlino # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public diff --git a/configure.ac b/configure.ac index befc4ff3..d4f2ae9c 100644 --- a/configure.ac +++ b/configure.ac @@ -1,6 +1,6 @@ # # This file is part of libhttpserver -# Copyright (C) 2011 Sebastiano Merlino +# Copyright (C) 2011, 2012, 2013, 2014 Sebastiano Merlino # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public diff --git a/src/Makefile.am b/src/Makefile.am index d3dd9f66..1233bc0c 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,6 +1,6 @@ # # This file is part of libhttpserver -# Copyright (C) 2011 Sebastiano Merlino +# Copyright (C) 2011, 2012, 2013, 2014 Sebastiano Merlino # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public diff --git a/src/http_endpoint.cpp b/src/http_endpoint.cpp index 2a1c04ec..e066d68a 100644 --- a/src/http_endpoint.cpp +++ b/src/http_endpoint.cpp @@ -1,6 +1,6 @@ /* This file is part of libhttpserver - Copyright (C) 2011 Sebastiano Merlino + Copyright (C) 2011, 2012, 2013, 2014 Sebastiano Merlino This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public diff --git a/src/http_request.cpp b/src/http_request.cpp index 14a3fb68..3d7990ce 100644 --- a/src/http_request.cpp +++ b/src/http_request.cpp @@ -1,6 +1,6 @@ /* This file is part of libhttpserver - Copyright (C) 2011 Sebastiano Merlino + Copyright (C) 2011, 2012, 2013, 2014 Sebastiano Merlino This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public @@ -14,7 +14,7 @@ You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/src/http_resource.cpp b/src/http_resource.cpp index 98c04206..19a59a0f 100644 --- a/src/http_resource.cpp +++ b/src/http_resource.cpp @@ -1,6 +1,6 @@ /* This file is part of libhttpserver - Copyright (C) 2011 Sebastiano Merlino + Copyright (C) 2011, 2012, 2013, 2014 Sebastiano Merlino This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public @@ -26,7 +26,7 @@ using namespace std; -namespace httpserver +namespace httpserver { //RESOURCE void resource_init(map& allowed_methods) diff --git a/src/http_response.cpp b/src/http_response.cpp index 115d6996..11e5e2ae 100644 --- a/src/http_response.cpp +++ b/src/http_response.cpp @@ -1,6 +1,6 @@ /* This file is part of libhttpserver - Copyright (C) 2011 Sebastiano Merlino + Copyright (C) 2011, 2012, 2013, 2014 Sebastiano Merlino This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public diff --git a/src/http_utils.cpp b/src/http_utils.cpp index c3a980a8..74d00629 100644 --- a/src/http_utils.cpp +++ b/src/http_utils.cpp @@ -1,6 +1,6 @@ /* This file is part of libhttpserver - Copyright (C) 2011 Sebastiano Merlino + Copyright (C) 2011, 2012, 2013, 2014 Sebastiano Merlino This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public @@ -14,7 +14,7 @@ You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -47,7 +47,7 @@ const int http_utils::http_processing = MHD_HTTP_PROCESSING; const int http_utils::http_ok = MHD_HTTP_OK; const int http_utils::http_created = MHD_HTTP_CREATED; const int http_utils::http_accepted = MHD_HTTP_ACCEPTED; -const int http_utils::http_non_authoritative_information = +const int http_utils::http_non_authoritative_information = MHD_HTTP_NON_AUTHORITATIVE_INFORMATION; const int http_utils::http_no_content = MHD_HTTP_NO_CONTENT; const int http_utils::http_reset_content = MHD_HTTP_RESET_CONTENT; @@ -69,21 +69,21 @@ const int http_utils::http_payment_required = MHD_HTTP_PAYMENT_REQUIRED; const int http_utils::http_forbidden = MHD_HTTP_FORBIDDEN; const int http_utils::http_not_found = MHD_HTTP_NOT_FOUND; const int http_utils::http_method_not_allowed = MHD_HTTP_METHOD_NOT_ALLOWED; -const int http_utils::http_method_not_acceptable = +const int http_utils::http_method_not_acceptable = MHD_HTTP_METHOD_NOT_ACCEPTABLE; -const int http_utils::http_proxy_authentication_required = +const int http_utils::http_proxy_authentication_required = MHD_HTTP_PROXY_AUTHENTICATION_REQUIRED; const int http_utils::http_request_timeout = MHD_HTTP_REQUEST_TIMEOUT; const int http_utils::http_conflict = MHD_HTTP_CONFLICT; const int http_utils::http_gone = MHD_HTTP_GONE; const int http_utils::http_length_required = MHD_HTTP_LENGTH_REQUIRED; const int http_utils::http_precondition_failed = MHD_HTTP_PRECONDITION_FAILED; -const int http_utils::http_request_entity_too_large = +const int http_utils::http_request_entity_too_large = MHD_HTTP_REQUEST_ENTITY_TOO_LARGE; const int http_utils::http_request_uri_too_long = MHD_HTTP_REQUEST_URI_TOO_LONG; -const int http_utils::http_unsupported_media_type = +const int http_utils::http_unsupported_media_type = MHD_HTTP_UNSUPPORTED_MEDIA_TYPE; -const int http_utils::http_requested_range_not_satisfiable = +const int http_utils::http_requested_range_not_satisfiable = MHD_HTTP_REQUESTED_RANGE_NOT_SATISFIABLE; const int http_utils::http_expectation_failed = MHD_HTTP_EXPECTATION_FAILED; const int http_utils::http_unprocessable_entity = MHD_HTTP_UNPROCESSABLE_ENTITY; @@ -93,53 +93,53 @@ const int http_utils::http_unordered_collection = MHD_HTTP_UNORDERED_COLLECTION; const int http_utils::http_upgrade_required = MHD_HTTP_UPGRADE_REQUIRED; const int http_utils::http_retry_with = MHD_HTTP_RETRY_WITH; -const int http_utils::http_internal_server_error = +const int http_utils::http_internal_server_error = MHD_HTTP_INTERNAL_SERVER_ERROR; const int http_utils::http_not_implemented = MHD_HTTP_NOT_IMPLEMENTED; const int http_utils::http_bad_gateway = MHD_HTTP_BAD_GATEWAY; const int http_utils::http_service_unavailable = MHD_HTTP_SERVICE_UNAVAILABLE; const int http_utils::http_gateway_timeout = MHD_HTTP_GATEWAY_TIMEOUT; -const int http_utils::http_version_not_supported = +const int http_utils::http_version_not_supported = MHD_HTTP_HTTP_VERSION_NOT_SUPPORTED; -const int http_utils::http_variant_also_negotiated = +const int http_utils::http_variant_also_negotiated = MHD_HTTP_VARIANT_ALSO_NEGOTIATES; const int http_utils::http_insufficient_storage = MHD_HTTP_INSUFFICIENT_STORAGE; -const int http_utils::http_bandwidth_limit_exceeded = +const int http_utils::http_bandwidth_limit_exceeded = MHD_HTTP_BANDWIDTH_LIMIT_EXCEEDED; const int http_utils::http_not_extended = MHD_HTTP_NOT_EXTENDED; const int http_utils::shoutcast_response = MHD_ICY_FLAG; const std::string http_utils::http_header_accept = MHD_HTTP_HEADER_ACCEPT; -const std::string http_utils::http_header_accept_charset = +const std::string http_utils::http_header_accept_charset = MHD_HTTP_HEADER_ACCEPT_CHARSET; -const std::string http_utils::http_header_accept_encoding = +const std::string http_utils::http_header_accept_encoding = MHD_HTTP_HEADER_ACCEPT_ENCODING; -const std::string http_utils::http_header_accept_language = +const std::string http_utils::http_header_accept_language = MHD_HTTP_HEADER_ACCEPT_LANGUAGE; -const std::string http_utils::http_header_accept_ranges = +const std::string http_utils::http_header_accept_ranges = MHD_HTTP_HEADER_ACCEPT_RANGES; const std::string http_utils::http_header_age = MHD_HTTP_HEADER_AGE; const std::string http_utils::http_header_allow = MHD_HTTP_HEADER_ALLOW; -const std::string http_utils::http_header_authorization = +const std::string http_utils::http_header_authorization = MHD_HTTP_HEADER_AUTHORIZATION; -const std::string http_utils::http_header_cache_control = +const std::string http_utils::http_header_cache_control = MHD_HTTP_HEADER_CACHE_CONTROL; -const std::string http_utils::http_header_connection = +const std::string http_utils::http_header_connection = MHD_HTTP_HEADER_CONNECTION; -const std::string http_utils::http_header_content_encoding = +const std::string http_utils::http_header_content_encoding = MHD_HTTP_HEADER_CONTENT_ENCODING; -const std::string http_utils::http_header_content_language = +const std::string http_utils::http_header_content_language = MHD_HTTP_HEADER_CONTENT_LANGUAGE; -const std::string http_utils::http_header_content_length = +const std::string http_utils::http_header_content_length = MHD_HTTP_HEADER_CONTENT_LENGTH; -const std::string http_utils::http_header_content_location = +const std::string http_utils::http_header_content_location = MHD_HTTP_HEADER_CONTENT_LOCATION; -const std::string http_utils::http_header_content_md5 = +const std::string http_utils::http_header_content_md5 = MHD_HTTP_HEADER_CONTENT_MD5; -const std::string http_utils::http_header_content_range = +const std::string http_utils::http_header_content_range = MHD_HTTP_HEADER_CONTENT_RANGE; -const std::string http_utils::http_header_content_type = +const std::string http_utils::http_header_content_type = MHD_HTTP_HEADER_CONTENT_TYPE; const std::string http_utils::http_header_date = MHD_HTTP_HEADER_DATE; const std::string http_utils::http_header_etag = MHD_HTTP_HEADER_ETAG; @@ -148,39 +148,39 @@ const std::string http_utils::http_header_expires = MHD_HTTP_HEADER_EXPIRES; const std::string http_utils::http_header_from = MHD_HTTP_HEADER_FROM; const std::string http_utils::http_header_host = MHD_HTTP_HEADER_HOST; const std::string http_utils::http_header_if_match = MHD_HTTP_HEADER_IF_MATCH; -const std::string http_utils::http_header_if_modified_since = +const std::string http_utils::http_header_if_modified_since = MHD_HTTP_HEADER_IF_MODIFIED_SINCE; -const std::string http_utils::http_header_if_none_match = +const std::string http_utils::http_header_if_none_match = MHD_HTTP_HEADER_IF_NONE_MATCH; const std::string http_utils::http_header_if_range = MHD_HTTP_HEADER_IF_RANGE; -const std::string http_utils::http_header_if_unmodified_since = +const std::string http_utils::http_header_if_unmodified_since = MHD_HTTP_HEADER_IF_UNMODIFIED_SINCE; -const std::string http_utils::http_header_last_modified = +const std::string http_utils::http_header_last_modified = MHD_HTTP_HEADER_LAST_MODIFIED; const std::string http_utils::http_header_location = MHD_HTTP_HEADER_LOCATION; -const std::string http_utils::http_header_max_forwards = +const std::string http_utils::http_header_max_forwards = MHD_HTTP_HEADER_MAX_FORWARDS; const std::string http_utils::http_header_pragma = MHD_HTTP_HEADER_PRAGMA; -const std::string http_utils::http_header_proxy_authenticate = +const std::string http_utils::http_header_proxy_authenticate = MHD_HTTP_HEADER_PROXY_AUTHENTICATE; -const std::string http_utils::http_header_proxy_authentication = +const std::string http_utils::http_header_proxy_authentication = MHD_HTTP_HEADER_PROXY_AUTHORIZATION; const std::string http_utils::http_header_range = MHD_HTTP_HEADER_RANGE; const std::string http_utils::http_header_referer = MHD_HTTP_HEADER_REFERER; -const std::string http_utils::http_header_retry_after = +const std::string http_utils::http_header_retry_after = MHD_HTTP_HEADER_RETRY_AFTER; const std::string http_utils::http_header_server = MHD_HTTP_HEADER_SERVER; const std::string http_utils::http_header_te = MHD_HTTP_HEADER_TE; const std::string http_utils::http_header_trailer = MHD_HTTP_HEADER_TRAILER; -const std::string http_utils::http_header_transfer_encoding = +const std::string http_utils::http_header_transfer_encoding = MHD_HTTP_HEADER_TRANSFER_ENCODING; const std::string http_utils::http_header_upgrade = MHD_HTTP_HEADER_UPGRADE; -const std::string http_utils::http_header_user_agent = +const std::string http_utils::http_header_user_agent = MHD_HTTP_HEADER_USER_AGENT; const std::string http_utils::http_header_vary = MHD_HTTP_HEADER_VARY; const std::string http_utils::http_header_via = MHD_HTTP_HEADER_VIA; const std::string http_utils::http_header_warning = MHD_HTTP_HEADER_WARNING; -const std::string http_utils::http_header_www_authenticate = +const std::string http_utils::http_header_www_authenticate = MHD_HTTP_HEADER_WWW_AUTHENTICATE; const std::string http_utils::http_version_1_0 = MHD_HTTP_VERSION_1_0; @@ -195,9 +195,9 @@ const std::string http_utils::http_method_post = MHD_HTTP_METHOD_POST; const std::string http_utils::http_method_put = MHD_HTTP_METHOD_PUT; const std::string http_utils::http_method_trace = MHD_HTTP_METHOD_TRACE; -const std::string http_utils::http_post_encoding_form_urlencoded = +const std::string http_utils::http_post_encoding_form_urlencoded = MHD_HTTP_POST_ENCODING_FORM_URLENCODED; -const std::string http_utils::http_post_encoding_multipart_formdata = +const std::string http_utils::http_post_encoding_multipart_formdata = MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA; @@ -234,7 +234,7 @@ void get_ip_str( if(sa) { char to_ret[INET6_ADDRSTRLEN] = { '\0' }; - switch(sa->sa_family) + switch(sa->sa_family) { case AF_INET: if(maxlen == 0) @@ -315,9 +315,9 @@ size_t http_unescape (char *val) unsigned int num; while ('\0' != *rpos) - { + { switch (*rpos) - { + { case '+': *wpos = ' '; wpos++; @@ -327,20 +327,20 @@ size_t http_unescape (char *val) if ( (1 == sscanf (&rpos[1], "%2x", &num)) || (1 == sscanf (&rpos[1], - "%2X", &num)) + "%2X", &num)) ) - { + { *wpos = (unsigned char) num; wpos++; rpos += 3; break; - } + } /* intentional fall through! */ default: *wpos = *rpos; wpos++; rpos++; - } + } } *wpos = '\0'; /* add 0-terminator */ return wpos - val; /* = strlen(val) */ @@ -362,8 +362,8 @@ ip_representation::ip_representation(const struct sockaddr* ip) ip_version = http_utils::IPV6; for(int i=0;i<32;i+=2) { - pieces[i/2] = - ((u_char*)&(((struct sockaddr_in6 *)ip)->sin6_addr))[i] + + pieces[i/2] = + ((u_char*)&(((struct sockaddr_in6 *)ip)->sin6_addr))[i] + 16 * ((u_char*)&(((struct sockaddr_in6 *)ip)->sin6_addr))[i+1]; } } diff --git a/src/httpserver/binders.hpp b/src/httpserver/binders.hpp index f94d8017..02a76b88 100644 --- a/src/httpserver/binders.hpp +++ b/src/httpserver/binders.hpp @@ -1,6 +1,6 @@ /* This file is part of libhttpserver - Copyright (C) 2011 Sebastiano Merlino + Copyright (C) 2011, 2012, 2013, 2014 Sebastiano Merlino This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public diff --git a/src/httpserver/create_webserver.hpp b/src/httpserver/create_webserver.hpp index 805d7f9c..0c6582b3 100644 --- a/src/httpserver/create_webserver.hpp +++ b/src/httpserver/create_webserver.hpp @@ -1,6 +1,6 @@ /* This file is part of libhttpserver - Copyright (C) 2011 Sebastiano Merlino + Copyright (C) 2011, 2012, 2013, 2014 Sebastiano Merlino This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public diff --git a/src/httpserver/details/cache_entry.hpp b/src/httpserver/details/cache_entry.hpp index d6678825..414293c8 100644 --- a/src/httpserver/details/cache_entry.hpp +++ b/src/httpserver/details/cache_entry.hpp @@ -1,6 +1,6 @@ /* This file is part of libhttpserver - Copyright (C) 2011 Sebastiano Merlino + Copyright (C) 2011, 2012, 2013, 2014 Sebastiano Merlino This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public diff --git a/src/httpserver/details/event_tuple.hpp b/src/httpserver/details/event_tuple.hpp index 4fedafab..ec289120 100644 --- a/src/httpserver/details/event_tuple.hpp +++ b/src/httpserver/details/event_tuple.hpp @@ -1,6 +1,6 @@ /* This file is part of libhttpserver - Copyright (C) 2011 Sebastiano Merlino + Copyright (C) 2011, 2012, 2013, 2014 Sebastiano Merlino This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public diff --git a/src/httpserver/details/http_endpoint.hpp b/src/httpserver/details/http_endpoint.hpp index fa81ccfb..120c6749 100644 --- a/src/httpserver/details/http_endpoint.hpp +++ b/src/httpserver/details/http_endpoint.hpp @@ -1,6 +1,6 @@ /* This file is part of libhttpserver - Copyright (C) 2011 Sebastiano Merlino + Copyright (C) 2011, 2012, 2013, 2014 Sebastiano Merlino This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public @@ -14,7 +14,7 @@ You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -58,7 +58,7 @@ class bad_http_endpoint : public std::exception /** * Class representing an Http Endpoint. It is an abstraction used by the APIs. **/ -class http_endpoint +class http_endpoint { private: /** diff --git a/src/httpserver/details/http_resource_mirror.hpp b/src/httpserver/details/http_resource_mirror.hpp index 7c4e4168..64a394b7 100644 --- a/src/httpserver/details/http_resource_mirror.hpp +++ b/src/httpserver/details/http_resource_mirror.hpp @@ -1,6 +1,6 @@ /* This file is part of libhttpserver - Copyright (C) 2011 Sebastiano Merlino + Copyright (C) 2011, 2012, 2013, 2014 Sebastiano Merlino This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public diff --git a/src/httpserver/details/http_response_ptr.hpp b/src/httpserver/details/http_response_ptr.hpp index 3f867e6e..368aaa50 100644 --- a/src/httpserver/details/http_response_ptr.hpp +++ b/src/httpserver/details/http_response_ptr.hpp @@ -1,6 +1,6 @@ /* This file is part of libhttpserver - Copyright (C) 2011 Sebastiano Merlino + Copyright (C) 2011, 2012, 2013, 2014 Sebastiano Merlino This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public diff --git a/src/httpserver/details/modded_request.hpp b/src/httpserver/details/modded_request.hpp index 57ccb344..998fe386 100644 --- a/src/httpserver/details/modded_request.hpp +++ b/src/httpserver/details/modded_request.hpp @@ -1,6 +1,6 @@ /* This file is part of libhttpserver - Copyright (C) 2011 Sebastiano Merlino + Copyright (C) 2011, 2012, 2013, 2014 Sebastiano Merlino This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public diff --git a/src/httpserver/event_supplier.hpp b/src/httpserver/event_supplier.hpp index fd39400c..6835050e 100644 --- a/src/httpserver/event_supplier.hpp +++ b/src/httpserver/event_supplier.hpp @@ -1,6 +1,6 @@ /* This file is part of libhttpserver - Copyright (C) 2011 Sebastiano Merlino + Copyright (C) 2011, 2012, 2013, 2014 Sebastiano Merlino This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public diff --git a/src/httpserver/http_request.hpp b/src/httpserver/http_request.hpp index 6a0725c3..8b39118c 100644 --- a/src/httpserver/http_request.hpp +++ b/src/httpserver/http_request.hpp @@ -1,6 +1,6 @@ /* This file is part of libhttpserver - Copyright (C) 2011 Sebastiano Merlino + Copyright (C) 2011, 2012, 2013, 2014 Sebastiano Merlino This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public @@ -14,7 +14,7 @@ You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -32,7 +32,7 @@ struct MHD_Connection; -namespace httpserver +namespace httpserver { class webserver; @@ -48,7 +48,7 @@ using namespace http; /** * Class representing an abstraction for an Http Request. It is used from classes using these apis to receive information through http protocol. **/ -class http_request +class http_request { public: @@ -219,7 +219,7 @@ class http_request **/ const std::string get_header(const std::string& key) const { - std::map::const_iterator it = + std::map::const_iterator it = this->headers.find(key); if(it != this->headers.end()) return it->second; @@ -228,7 +228,7 @@ class http_request } void get_header(const std::string& key, std::string& result) const { - std::map::const_iterator it = + std::map::const_iterator it = this->headers.find(key); if(it != this->headers.end()) result = it->second; @@ -479,7 +479,7 @@ class http_request this->path = path; std::vector complete_path; http_utils::tokenize_url(this->path, complete_path); - for(unsigned int i = 0; i < complete_path.size(); i++) + for(unsigned int i = 0; i < complete_path.size(); i++) { this->post_path.push_back(complete_path[i]); } diff --git a/src/httpserver/http_resource.hpp b/src/httpserver/http_resource.hpp index d06fb52c..a0895994 100644 --- a/src/httpserver/http_resource.hpp +++ b/src/httpserver/http_resource.hpp @@ -1,6 +1,6 @@ /* This file is part of libhttpserver - Copyright (C) 2011 Sebastiano Merlino + Copyright (C) 2011, 2012, 2013, 2014 Sebastiano Merlino This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public @@ -45,7 +45,7 @@ class http_response; void resource_init(std::map& res); template -class http_resource +class http_resource { public: /** @@ -142,7 +142,7 @@ class http_resource **/ void set_allowing(const std::string& method, bool allowed) { - if(this->allowed_methods.count(method)) + if(this->allowed_methods.count(method)) { this->allowed_methods[method] = allowed; } diff --git a/src/httpserver/http_response.hpp b/src/httpserver/http_response.hpp index 213da1e8..05790b39 100644 --- a/src/httpserver/http_response.hpp +++ b/src/httpserver/http_response.hpp @@ -1,6 +1,6 @@ /* This file is part of libhttpserver - Copyright (C) 2011 Sebastiano Merlino + Copyright (C) 2011, 2012, 2013, 2014 Sebastiano Merlino This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public diff --git a/src/httpserver/http_utils.hpp b/src/httpserver/http_utils.hpp index 8523c5f2..deccc180 100644 --- a/src/httpserver/http_utils.hpp +++ b/src/httpserver/http_utils.hpp @@ -1,6 +1,6 @@ /* This file is part of libhttpserver - Copyright (C) 2011 Sebastiano Merlino + Copyright (C) 2011, 2012, 2013, 2014 Sebastiano Merlino This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public @@ -14,7 +14,7 @@ You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -54,13 +54,13 @@ class file_access_exception: public std::exception { return "Unable to open file!"; } -}; +}; -class http_utils +class http_utils { public: - enum cred_type_T + enum cred_type_T { NONE = -1 #ifdef HAVE_GNUTLS @@ -223,7 +223,7 @@ class http_utils static const std::string http_post_encoding_form_urlencoded; static const std::string http_post_encoding_multipart_formdata; - static size_t tokenize_url(const std::string&, + static size_t tokenize_url(const std::string&, std::vector& result, const char separator = '/' ); static void standardize_url(const std::string&, std::string& result); @@ -253,8 +253,8 @@ class header_comparator { * @param first string * @param second string **/ - bool operator()(const std::string& x,const std::string& y) const - { + bool operator()(const std::string& x,const std::string& y) const + { COMPARATOR(x, y, toupper); } }; @@ -271,8 +271,8 @@ class arg_comparator { * @param first string * @param second string **/ - bool operator()(const std::string& x,const std::string& y) const - { + bool operator()(const std::string& x,const std::string& y) const + { #ifdef CASE_INSENSITIVE COMPARATOR(x, y, toupper); #else diff --git a/src/httpserver/string_utilities.hpp b/src/httpserver/string_utilities.hpp index a50f4350..92aa9716 100644 --- a/src/httpserver/string_utilities.hpp +++ b/src/httpserver/string_utilities.hpp @@ -1,6 +1,6 @@ /* This file is part of libhttpserver - Copyright (C) 2011 Sebastiano Merlino + Copyright (C) 2011, 2012, 2013, 2014 Sebastiano Merlino This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public @@ -14,7 +14,7 @@ You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ diff --git a/src/httpserver/webserver.hpp b/src/httpserver/webserver.hpp index b55522b6..22471e91 100644 --- a/src/httpserver/webserver.hpp +++ b/src/httpserver/webserver.hpp @@ -1,6 +1,6 @@ /* This file is part of libhttpserver - Copyright (C) 2011 Sebastiano Merlino + Copyright (C) 2011, 2012, 2013, 2014 Sebastiano Merlino This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public diff --git a/src/string_utilities.cpp b/src/string_utilities.cpp index 9ef84d29..1d146275 100644 --- a/src/string_utilities.cpp +++ b/src/string_utilities.cpp @@ -1,6 +1,6 @@ /* This file is part of libhttpserver - Copyright (C) 2011 Sebastiano Merlino + Copyright (C) 2011, 2012, 2013, 2014 Sebastiano Merlino This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public @@ -14,7 +14,7 @@ You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ @@ -28,7 +28,7 @@ #include #include #include "string_utilities.hpp" - + namespace httpserver { namespace string_utilities @@ -37,8 +37,8 @@ namespace string_utilities void to_upper_copy(const std::string& str, std::string& result) { result = str; - std::transform(result.begin(), - result.end(), + std::transform(result.begin(), + result.end(), result.begin(), (int(*)(int)) std::toupper ); @@ -46,18 +46,18 @@ void to_upper_copy(const std::string& str, std::string& result) void to_upper(std::string& str) { - std::transform(str.begin(), - str.end(), + std::transform(str.begin(), + str.end(), str.begin(), (int(*)(int)) std::toupper ); } - + void to_lower_copy(const std::string& str, std::string& result) { result = str; - std::transform(result.begin(), - result.end(), + std::transform(result.begin(), + result.end(), result.begin(), (int(*)(int)) std::tolower ); @@ -90,10 +90,10 @@ void regex_replace(const std::string& str, regcomp(&preg, pattern.c_str(), REG_EXTENDED|REG_ICASE); if ( regexec(&preg, str.c_str(), 1, substmatch, 0) == 0 ) { - char ns[substmatch[0].rm_so + 1 + + char ns[substmatch[0].rm_so + 1 + replace_str.size() + (str.size() - substmatch[0].rm_eo) + 2 ]; - + memcpy(ns, str.c_str(), substmatch[0].rm_so+1); memcpy(&ns[substmatch[0].rm_so], @@ -111,7 +111,7 @@ void regex_replace(const std::string& str, ] = 0; result = std::string((char*)ns); - } + } regfree(&preg); } diff --git a/src/webserver.cpp b/src/webserver.cpp index dbe42eee..2e5f7fcc 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -1,6 +1,6 @@ /* This file is part of libhttpserver - Copyright (C) 2011 Sebastiano Merlino + Copyright (C) 2011, 2012, 2013, 2014 Sebastiano Merlino This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public diff --git a/test/Makefile.am b/test/Makefile.am index e1a6facb..162f6d89 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -1,6 +1,6 @@ # # This file is part of libhttpserver -# Copyright (C) 2011 Sebastiano Merlino +# Copyright (C) 2011, 2012, 2013, 2014 Sebastiano Merlino # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public From 4625f6568bbac0cb9463aa381246cd86cdc97933 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sat, 22 Feb 2014 09:39:21 +0000 Subject: [PATCH 030/623] Enforced constness of headers getters in http_response --- src/http_response.cpp | 6 +++--- src/httpserver/http_response.hpp | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/http_response.cpp b/src/http_response.cpp index 11e5e2ae..6a61b29a 100644 --- a/src/http_response.cpp +++ b/src/http_response.cpp @@ -39,19 +39,19 @@ http_response::~http_response() webserver::unlock_cache_entry(ce); } -size_t http_response::get_headers(std::map& result) +size_t http_response::get_headers(std::map& result) const { result = this->headers; return result.size(); } -size_t http_response::get_footers(std::map& result) +size_t http_response::get_footers(std::map& result) const { result = this->footers; return result.size(); } -size_t http_response::get_cookies(std::map& result) +size_t http_response::get_cookies(std::map& result) const { result = this->cookies; return result.size(); diff --git a/src/httpserver/http_response.hpp b/src/httpserver/http_response.hpp index 05790b39..55821cbc 100644 --- a/src/httpserver/http_response.hpp +++ b/src/httpserver/http_response.hpp @@ -324,17 +324,17 @@ class http_response **/ size_t get_headers( std::map& result - ); + ) const; /** * Method used to get all footers passed with the request. * @return a map containing all footers. **/ size_t get_footers( std::map& result - ); + ) const; size_t get_cookies( std::map& result - ); + ) const; /** * Method used to set all headers of the response. * @param headers The headers key-value map to set for the response. From b78bac657b6fd2b9c77a5045c06c24699af37ec9 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sat, 22 Feb 2014 11:01:31 +0000 Subject: [PATCH 031/623] Changed version and updated authors file --- AUTHORS | 1 + configure.ac | 2 +- debian/changelog.in | 5 +++++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/AUTHORS b/AUTHORS index f44ee5d2..adf0a9aa 100644 --- a/AUTHORS +++ b/AUTHORS @@ -4,3 +4,4 @@ Sebastiano Merlino (maintainer) Code contributions also came from: Dario Mazza Andrea Nicotra +Jeff Waller diff --git a/configure.ac b/configure.ac index d4f2ae9c..ae9f9320 100644 --- a/configure.ac +++ b/configure.ac @@ -22,7 +22,7 @@ AC_PREREQ(2.57) m4_define([libhttpserver_MAJOR_VERSION],[0])dnl m4_define([libhttpserver_MINOR_VERSION],[7])dnl -m4_define([libhttpserver_REVISION],[0])dnl +m4_define([libhttpserver_REVISION],[1])dnl m4_define([libhttpserver_PKG_VERSION],[libhttpserver_MAJOR_VERSION.libhttpserver_MINOR_VERSION.libhttpserver_REVISION])dnl m4_define([libhttpserver_LDF_VERSION],[libhttpserver_MAJOR_VERSION:libhttpserver_MINOR_VERSION:libhttpserver_REVISION])dnl AC_INIT([libhttpserver], libhttpserver_PKG_VERSION, [electrictwister2000@gmail.com]) diff --git a/debian/changelog.in b/debian/changelog.in index 157c7a38..f1eeedbf 100644 --- a/debian/changelog.in +++ b/debian/changelog.in @@ -1,3 +1,8 @@ +libhttpserver (0.7.1) unstable; urgency=low + * Improved methods constness + + -- Sebastiano Merlino Sat, 22 Feb 2014 10:58:02 +0100 + libhttpserver (0.7.0) unstable; urgency=low * Cleaned-up webserver.cpp code to extract secondary classes * Enforced immutability of webserver class From 8585d0ed1cbba4ed0debd2a1ddb3af33f21ae11f Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sat, 22 Feb 2014 11:21:11 +0000 Subject: [PATCH 032/623] Preparing the ground for clang compilation Included .h files needed by clang in http_resource and http_response --- src/http_resource.cpp | 4 ++++ src/http_response.cpp | 2 ++ 2 files changed, 6 insertions(+) diff --git a/src/http_resource.cpp b/src/http_resource.cpp index 19a59a0f..03437315 100644 --- a/src/http_resource.cpp +++ b/src/http_resource.cpp @@ -17,10 +17,14 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include + #include "http_resource.hpp" #include "http_utils.hpp" #include "http_request.hpp" #include "http_response.hpp" +#include "details/event_tuple.hpp" #include "webserver.hpp" #include "string_utilities.hpp" diff --git a/src/http_response.cpp b/src/http_response.cpp index 6a61b29a..5a5a008e 100644 --- a/src/http_response.cpp +++ b/src/http_response.cpp @@ -25,6 +25,8 @@ #include #include #include "http_utils.hpp" +#include "details/http_resource_mirror.hpp" +#include "details/event_tuple.hpp" #include "webserver.hpp" #include "http_response.hpp" From a45ea31d88f9f8009f5d6432ee3f11bfa4787184 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sat, 22 Feb 2014 20:25:17 +0000 Subject: [PATCH 033/623] Fixed problem on OS/X Fixed problem on OS/X; SOCK_CLOEXEC raises EPROTONOSUPPORT instead of EINVAL. --- src/webserver.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/webserver.cpp b/src/webserver.cpp index 2e5f7fcc..7988ce93 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -376,7 +376,9 @@ int create_socket (int domain, int type, int protocol) /* use SOCK_STREAM rather than ai_socktype: some getaddrinfo * implementations do not set ai_socktype, e.g. RHL6.2. */ fd = socket(domain, ctype, protocol); - if ( (-1 == fd) && (EINVAL == errno) && (0 != sock_cloexec) ) + if ((fd == -1) && + (errno == EINVAL || errno == EPROTONOSUPPORT) && (sock_cloexec != 0) + ) { sock_cloexec = 0; fd = socket(domain, type, protocol); From 8db7a3ffbdb45dc9816101fd8abac561bd73e295 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sat, 22 Feb 2014 20:36:13 +0000 Subject: [PATCH 034/623] Avoid warnings during clang compilation Match class/struct in both definition and declaration --- src/httpserver/details/http_endpoint.hpp | 2 +- src/httpserver/webserver.hpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/httpserver/details/http_endpoint.hpp b/src/httpserver/details/http_endpoint.hpp index 120c6749..19d3fc7a 100644 --- a/src/httpserver/details/http_endpoint.hpp +++ b/src/httpserver/details/http_endpoint.hpp @@ -38,7 +38,7 @@ class webserver; namespace details { -struct http_resource_mirror; +class http_resource_mirror; /** * Exception class throwed when a bad formatted http url is used diff --git a/src/httpserver/webserver.hpp b/src/httpserver/webserver.hpp index 22471e91..5b610d09 100644 --- a/src/httpserver/webserver.hpp +++ b/src/httpserver/webserver.hpp @@ -63,8 +63,8 @@ namespace details { class http_resource_mirror; class event_tuple; class http_endpoint; - class daemon_item; - class modded_request; + struct daemon_item; + struct modded_request; struct cache_entry; } From ecd1470e8e9198bc448b9396ad307e942d7a9bfa Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sat, 22 Feb 2014 20:41:59 +0000 Subject: [PATCH 035/623] Avoid to assign member field to itself --- src/httpserver/http_request.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/httpserver/http_request.hpp b/src/httpserver/http_request.hpp index 8b39118c..d702f35f 100644 --- a/src/httpserver/http_request.hpp +++ b/src/httpserver/http_request.hpp @@ -509,7 +509,7 @@ class http_request * Method used to set the requestor port * @param requestor The requestor port to set **/ - void set_requestor_port(short requestor) + void set_requestor_port(short requestor_port) { this->requestor_port = requestor_port; } From 829c8fa1f15a28cd7bb34e4883fc65eb9d56d4dc Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sat, 22 Feb 2014 20:43:13 +0000 Subject: [PATCH 036/623] Added clang to travis compilation --- .travis.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 55fd1808..19f83f27 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,7 @@ language: cpp -compiler: gcc +compiler: + - gcc + - clang # Change this to your needs before_install: - wget http://199.231.187.83/resources/libmicrohttpd-0.9.26.tar.gz From 71d3be3b8b3fe51fca3577409e4ccdf10ab4ec11 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sat, 22 Feb 2014 20:47:58 +0000 Subject: [PATCH 037/623] Avoid warning during tests --- test/littletest.hpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/test/littletest.hpp b/test/littletest.hpp index b6fe8c50..36490ac6 100644 --- a/test/littletest.hpp +++ b/test/littletest.hpp @@ -56,7 +56,7 @@ #define LT_CREATE_RUNNER(__lt_suite_name__, __lt_runner_name__) \ std::cout << "** Initializing Runner \"" << #__lt_runner_name__ << "\" **" << std::endl; \ - littletest::test_runner __lt_runner_name__ + littletest::test_runner __lt_runner_name__ #define LT_RUNNER(__lt_runner_name__) __lt_runner_name__ @@ -283,7 +283,7 @@ struct check_unattended : public std::exception { return message.c_str(); } - + private: std::string message; }; @@ -298,8 +298,8 @@ struct assert_unattended : public std::exception virtual const char* what() const throw() { return message.c_str(); - } - + } + private: std::string message; }; @@ -314,8 +314,8 @@ struct warn_unattended : public std::exception virtual const char* what() const throw() { return message.c_str(); - } - + } + private: std::string message; }; @@ -367,8 +367,8 @@ struct test_runner template test_runner& run(test_impl* t) { - std::cout << "Running test (" << - test_counter << "): " << + std::cout << "Running test (" << + test_counter << "): " << t->__lt_name__ << std::endl; t->run_test(this); @@ -579,7 +579,7 @@ class test : public test_base test() { } test(const test& t) { } - friend class test_runner; + friend struct test_runner; }; }; From eaa553780fe372e82930d356fff5b15b228b5b65 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sat, 22 Feb 2014 20:56:31 +0000 Subject: [PATCH 038/623] Added option to enable same directory build --- configure.ac | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index ae9f9320..52d49494 100644 --- a/configure.ac +++ b/configure.ac @@ -39,8 +39,18 @@ CXXFLAGS=$OLD_CXXFLAGS AC_LANG([C++]) AC_SYS_LARGEFILE -if test "`cd $srcdir; /bin/pwd`" = "`/bin/pwd`"; then - AC_MSG_ERROR("you must configure in a separate build directory") +AC_MSG_CHECKING([whether it is possible to compile in the same directory]) +AC_ARG_ENABLE([same-directory-build], + [AS_HELP_STRING([--enable-same-directory-build], + [enable to compile in the same directory. This is heavily discouraged. (def=no)])], + [samedirectory="$enableval"], + [samedirectory=no]) +AC_MSG_RESULT([$samedirectory]) + +if test x"$samedirectory" = x"no"; then + if test "`cd $srcdir; /bin/pwd`" = "`/bin/pwd`"; then + AC_MSG_ERROR("you must configure in a separate build directory") + fi fi # Checks for header files. From 604143156dce180bec9c9bcc9e2354373665b6e9 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sat, 22 Feb 2014 21:04:17 +0000 Subject: [PATCH 039/623] Ignore file generated by build --- .gitignore | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index bc2d7218..72c98a64 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,11 @@ *.gcov *.gcno *.gcda +*.o +*.lo +*.la +.idea +libhttpserver.iml build/* aclocal.m4 autom4te.cache/ @@ -27,8 +32,28 @@ src/core src/http_request_builder.cpp src/httpserver/core src/httpserver/http_request_builder.hpp -src/php/php_libhttpserver_php.h +src/.deps/ +src/.libs/ test/Test test/core test/err test/test.txt +Makefile +src/Makefile +stamp-h1 +test-driver +test/.deps/ +test/Makefile +compile +config.h +config.log +config.status +debian/changelog +debian/control +debian/copyright +debian/libhttpserver-dev.install +debian/libhttpserver.install +debian/rules +redhat/libhttpserver.SPEC +libhttpserver.pc +libtool From d5e9b216bebbc2dedcd0cbb622907830e207fa94 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 23 Feb 2014 23:15:16 +0000 Subject: [PATCH 040/623] Avoid ambiguities in resource registration --- src/webserver.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/webserver.cpp b/src/webserver.cpp index 7988ce93..4d9df7ba 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -252,6 +252,7 @@ void webserver::register_resource( registered_resources.insert( pair(idx,hrm) ); + registered_resources[idx] = hrm; registered_resources_str[idx.url_complete] = ®istered_resources[idx]; } From 5029c87fcdef9358afbe2ff846820aed8e7e0a4e Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 23 Feb 2014 23:52:10 +0000 Subject: [PATCH 041/623] Avoid incoerences in resource insertion --- src/webserver.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/webserver.cpp b/src/webserver.cpp index 4d9df7ba..18164aed 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -249,11 +249,16 @@ void webserver::register_resource( details::http_endpoint idx(resource, family, true, regex_checking); - registered_resources.insert( + pair::iterator, bool> result = registered_resources.insert( pair(idx,hrm) ); - registered_resources[idx] = hrm; - registered_resources_str[idx.url_complete] = ®istered_resources[idx]; + + if(result.second) + { + registered_resources_str.insert( + pair(idx.get_url_complete(), &(result.first->second)) + ); + } } void* webserver::select(void* self) From 7763937d5ca391791b0886ad6e44a93f548540b5 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 23 Feb 2014 23:55:01 +0000 Subject: [PATCH 042/623] Boolean return code on resource registration --- src/httpserver/webserver.hpp | 2 +- src/webserver.cpp | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/httpserver/webserver.hpp b/src/httpserver/webserver.hpp index 5b610d09..560ee5e4 100644 --- a/src/httpserver/webserver.hpp +++ b/src/httpserver/webserver.hpp @@ -261,7 +261,7 @@ class webserver static void* select(void* self); static void* cleaner(void* self); - void register_resource(const std::string& resource, + bool register_resource(const std::string& resource, details::http_resource_mirror hrm, bool family = false ); diff --git a/src/webserver.cpp b/src/webserver.cpp index 18164aed..9858bff1 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -238,7 +238,7 @@ void webserver::request_completed ( } } -void webserver::register_resource( +bool webserver::register_resource( const std::string& resource, details::http_resource_mirror hrm, bool family @@ -250,7 +250,7 @@ void webserver::register_resource( details::http_endpoint idx(resource, family, true, regex_checking); pair::iterator, bool> result = registered_resources.insert( - pair(idx,hrm) + pair(idx, hrm) ); if(result.second) @@ -259,6 +259,8 @@ void webserver::register_resource( pair(idx.get_url_complete(), &(result.first->second)) ); } + + return result.second; } void* webserver::select(void* self) From fbc8bd0a9f89e3dd05dca3005fc511b94273f5a1 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Tue, 4 Mar 2014 01:07:10 +0000 Subject: [PATCH 043/623] Add logic to avoid error on empty args Created also test to enforce the logic --- src/webserver.cpp | 7 +++++-- test/basic.cpp | 15 ++++++++++++++- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/webserver.cpp b/src/webserver.cpp index 9858bff1..32195376 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -749,10 +749,11 @@ int webserver::build_request_args ( void *cls, enum MHD_ValueKind kind, const char *key, - const char *value + const char *arg_value ) { details::modded_request* mr = static_cast(cls); + char* value = (char*) ((arg_value == NULL) ? "" : arg_value); { char buf[strlen(key) + strlen(value) + 3]; if(mr->dhr->querystring == "") @@ -766,7 +767,7 @@ int webserver::build_request_args ( mr->dhr->querystring += string(buf); } } - int size = internal_unescaper((void*) mr->ws, (char*) value); + int size = internal_unescaper((void*) mr->ws, value); mr->dhr->set_arg(key, string(value, size)); return MHD_YES; } @@ -820,6 +821,8 @@ size_t unescaper_func(void * cls, struct MHD_Connection *c, char *s) size_t internal_unescaper(void* cls, char* s) { + if(strlen(s) == 0) return 0; + webserver* dws = static_cast(cls); if(dws->unescaper != 0x0) { diff --git a/test/basic.cpp b/test/basic.cpp index df1b7435..1f226a99 100644 --- a/test/basic.cpp +++ b/test/basic.cpp @@ -21,7 +21,7 @@ size_t headerfunc(void *ptr, size_t size, size_t nmemb, map* ss) string s_ptr((char*)ptr, size*nmemb); size_t pos = s_ptr.find(":"); if(pos != string::npos) - (*ss)[s_ptr.substr(0, pos)] = + (*ss)[s_ptr.substr(0, pos)] = s_ptr.substr(pos + 2, s_ptr.size() - pos - 4); return size*nmemb; } @@ -276,6 +276,19 @@ LT_BEGIN_AUTO_TEST(basic_suite, postprocessor) curl_easy_cleanup(curl); LT_END_AUTO_TEST(postprocessor) +LT_BEGIN_AUTO_TEST(basic_suite, empty_arg) + simple_resource* resource = new simple_resource(); + ws->register_resource("base", resource); + curl_global_init(CURL_GLOBAL_ALL); + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, "arg1"); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + curl_easy_cleanup(curl); +LT_END_AUTO_TEST(empty_arg) LT_BEGIN_AUTO_TEST_ENV() AUTORUN_TESTS() From 014a633d07d01b0bbb3f78e9f17a6f2ee4c6079d Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sat, 8 Mar 2014 20:50:15 +0000 Subject: [PATCH 044/623] Added markdown doc. It will be the new standard for the library - the texi version will be derived --- doc/libhttpserver.md | 79 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 doc/libhttpserver.md diff --git a/doc/libhttpserver.md b/doc/libhttpserver.md new file mode 100644 index 00000000..4f357ab2 --- /dev/null +++ b/doc/libhttpserver.md @@ -0,0 +1,79 @@ +The libhttpserver (0.7.1) reference manual +========================================== + +Copying +======= +This manual is for libhttpserver, C++ library for creating an +embedded Rest HTTP server (and more). + +> Permission is granted to copy, distribute and/or modify this document +> under the terms of the GNU Free Documentation License, Version 1.3 +> or any later version published by the Free Software Foundation; +> with no Invariant Sections, no Front-Cover Texts, and no Back-Cover +> Texts. A copy of the license is included in the section entitled GNU +> Free Documentation License. + +Contents +======== +* Introduction. +* Compilation. +* Constants. +* Structures and classes type definition. +* Callback functions definition. +* Create and work with server. +* Registering resources. +* Building responses to requests. +* Whitelists and Blacklists. +* Simple comet semantics. +* Utilizing Authentication. +* Obtaining and modifying status information. + +Appendices +---------- +* GNU-LGPL: The GNU Lesser General Public License says how you can copy and share almost all of libhttpserver. +* GNU-FDL: The GNU Free Documentation License says how you can copy and share the documentation of libhttpserver. + +Introduction +============ +libhttpserver is meant to constitute an easy system to build HTTP +servers with REST fashion. +libhttpserver is based on libmicrohttpd and, like this, it is a +daemon library. +The mission of this library is to support all possible HTTP features +directly and with a simple semantic allowing then the user to concentrate +only on his application and not on HTTP request handling details. + +The library is supposed to work transparently for the client Implementing +the business logic and using the library itself to realize an interface. +If the user wants it must be able to change every behavior of the library +itself through the registration of callbacks. + +Like the api is based on (libmicrohttpd), libhttpserver is able to decode +certain body format a and automatically format them in object oriented +fashion. This is true for query arguments and for *POST* and *PUT* +requests bodies if *application/x-www-form-urlencoded* or +*multipart/form-data* header are passed. + +The header reproduce all the constants defined by libhttpserver. +These maps various constant used by the HTTP protocol that are exported +as a convenience for users of the library. Is is possible for the user +to define their own extensions of the HTTP standard and use those with +libhttpserver. + +All functions are guaranteed to be completely reentrant and +thread-safe (unless differently specified). +Additionally, clients can specify resource limits on the overall +number of connections, number of connections per IP address and memory +used per connection to avoid resource exhaustion. + +Compilation +=========== +libhttpserver uses the standard system where the usual build process +involves running +> ./bootstrap +> mkdir build +> cd build +> ./configure +> make +> make install + From bd709215711d84c9d8eb4317cba8ca3b82b31073 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sat, 8 Mar 2014 20:55:00 +0000 Subject: [PATCH 045/623] Add line break in compile instructions --- doc/libhttpserver.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/doc/libhttpserver.md b/doc/libhttpserver.md index 4f357ab2..7328d387 100644 --- a/doc/libhttpserver.md +++ b/doc/libhttpserver.md @@ -70,10 +70,10 @@ Compilation =========== libhttpserver uses the standard system where the usual build process involves running -> ./bootstrap -> mkdir build -> cd build -> ./configure -> make -> make install +> ./bootstrap +> mkdir build +> cd build +> ./configure +> make +> make install From c33de073b8a2c7cfd9caec5091cc41df60292186 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sat, 8 Mar 2014 20:58:40 +0000 Subject: [PATCH 046/623] Missing dot --- doc/libhttpserver.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/libhttpserver.md b/doc/libhttpserver.md index 7328d387..1c9d56e0 100644 --- a/doc/libhttpserver.md +++ b/doc/libhttpserver.md @@ -73,7 +73,7 @@ involves running > ./bootstrap > mkdir build > cd build -> ./configure +> ../configure > make > make install From 895c87506b130df6297919ac3271946f74bef7dd Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sat, 8 Mar 2014 21:07:59 +0000 Subject: [PATCH 047/623] Added licenses to the doc --- doc/libhttpserver.md | 946 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 946 insertions(+) diff --git a/doc/libhttpserver.md b/doc/libhttpserver.md index 1c9d56e0..e40c19d8 100644 --- a/doc/libhttpserver.md +++ b/doc/libhttpserver.md @@ -77,3 +77,949 @@ involves running > make > make install +GNU Lesser General Public License +================================= + +Version 2.1, February 1999 + +Copyright © 1991, 1999 Free Software Foundation, Inc. +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +Everyone is permitted to copy and distribute verbatim copies +of this license document, but changing it is not allowed. + +_This is the first released version of the Lesser GPL. It also counts +as the successor of the GNU Library Public License, version 2, hence +the version number 2.1._ + +### Preamble + +The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + +This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + +When we speak of free software, we are referring to freedom of use, +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 and use pieces of +it in new free programs; and that you are informed that you can do +these things. + +To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + +For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + +We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + +To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + +Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + +Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + +When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + +We call this license the “Lesser” General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + +For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + +In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + +Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + +The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +“work based on the library” and a “work that uses the library”. The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + +### TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + +**0.** This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called “this License”). +Each licensee is addressed as “you”. + +A “library” means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + +The “Library”, below, refers to any such software library or work +which has been distributed under these terms. A “work based on the +Library” means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term “modification”.) + +“Source code” for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + +**1.** You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + +You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + +**2.** You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + +* **a)** The modified work must itself be a software library. +* **b)** You must cause the files modified to carry prominent notices +stating that you changed the files and the date of any change. +* **c)** You must cause the whole of the work to be licensed at no +charge to all third parties under the terms of this License. +* **d)** If a facility in the modified Library refers to a function or a +table of data to be supplied by an application program that uses +the facility, other than as an argument passed when the facility +is invoked, then you must make a good faith effort to ensure that, +in the event an application does not supply such function or +table, the facility still operates, and performs whatever part of +its purpose remains meaningful. +(For example, a function in a library to compute square roots has +a purpose that is entirely well-defined independent of the +application. Therefore, Subsection 2d requires that any +application-supplied function or table used by this function must +be optional: if the application does not supply it, the square +root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + +**3.** You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + +Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + +This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + +**4.** You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + +If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + +**5.** A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a “work that uses the Library”. Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + +However, linking a “work that uses the Library” with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a “work that uses the +library”. The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + +When a “work that uses the Library” uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + +If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + +Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + +**6.** As an exception to the Sections above, you may also combine or +link a “work that uses the Library” with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + +You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + +* **a)** Accompany the work with the complete corresponding +machine-readable source code for the Library including whatever +changes were used in the work (which must be distributed under +Sections 1 and 2 above); and, if the work is an executable linked +with the Library, with the complete machine-readable “work that +uses the Library”, as object code and/or source code, so that the +user can modify the Library and then relink to produce a modified +executable containing the modified Library. (It is understood +that the user who changes the contents of definitions files in the +Library will not necessarily be able to recompile the application +to use the modified definitions.) +* **b)** Use a suitable shared library mechanism for linking with the +Library. A suitable mechanism is one that (1) uses at run time a +copy of the library already present on the user's computer system, +rather than copying library functions into the executable, and (2) +will operate properly with a modified version of the library, if +the user installs one, as long as the modified version is +interface-compatible with the version that the work was made with. +* **c)** Accompany the work with a written offer, valid for at +least three years, to give the same user the materials +specified in Subsection 6a, above, for a charge no more +than the cost of performing this distribution. +* **d)** If distribution of the work is made by offering access to copy +from a designated place, offer equivalent access to copy the above +specified materials from the same place. +* **e)** Verify that the user has already received a copy of these +materials or that you have already sent this user a copy. + +For an executable, the required form of the “work that uses the +Library” must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + +It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + +**7.** You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + +* **a)** Accompany the combined library with a copy of the same work +based on the Library, uncombined with any other library +facilities. This must be distributed under the terms of the +Sections above. +* **b)** Give prominent notice with the combined library of the fact +that part of it is a work based on the Library, and explaining +where to find the accompanying uncombined form of the same work. + +**8.** You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + +**9.** You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + +**10.** Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + +**11.** If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + +**12.** If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + +**13.** The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +“any later version”, you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + +**14.** If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + +### NO WARRANTY + +**15.** BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY “AS IS” WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + +**16.** IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + +END OF TERMS AND CONDITIONS + +### How to Apply These Terms to Your New Libraries + +If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + +To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +“copyright” line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a “copyright disclaimer” for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + +GNU Free Documentation License +============================== + +Version 1.3, 3 November 2008 + +Copyright © 2000, 2001, 2002, 2007, 2008 Free Software Foundation, Inc. <> + +Everyone is permitted to copy and distribute verbatim copies +of this license document, but changing it is not allowed. + +### 0. PREAMBLE + +The purpose of this License is to make a manual, textbook, or other +functional and useful document “free” in the sense of freedom: to +assure everyone the effective freedom to copy and redistribute it, +with or without modifying it, either commercially or noncommercially. +Secondarily, this License preserves for the author and publisher a way +to get credit for their work, while not being considered responsible +for modifications made by others. + +This License is a kind of “copyleft”, which means that derivative +works of the document must themselves be free in the same sense. It +complements the GNU General Public License, which is a copyleft +license designed for free software. + +We have designed this License in order to use it for manuals for free +software, because free software needs free documentation: a free +program should come with manuals providing the same freedoms that the +software does. But this License is not limited to software manuals; +it can be used for any textual work, regardless of subject matter or +whether it is published as a printed book. We recommend this License +principally for works whose purpose is instruction or reference. + + +### 1. APPLICABILITY AND DEFINITIONS + +This License applies to any manual or other work, in any medium, that +contains a notice placed by the copyright holder saying it can be +distributed under the terms of this License. Such a notice grants a +world-wide, royalty-free license, unlimited in duration, to use that +work under the conditions stated herein. The “Document”, below, +refers to any such manual or work. Any member of the public is a +licensee, and is addressed as “you”. You accept the license if you +copy, modify or distribute the work in a way requiring permission +under copyright law. + +A “Modified Version” of the Document means any work containing the +Document or a portion of it, either copied verbatim, or with +modifications and/or translated into another language. + +A “Secondary Section” is a named appendix or a front-matter section of +the Document that deals exclusively with the relationship of the +publishers or authors of the Document to the Document's overall +subject (or to related matters) and contains nothing that could fall +directly within that overall subject. (Thus, if the Document is in +part a textbook of mathematics, a Secondary Section may not explain +any mathematics.) The relationship could be a matter of historical +connection with the subject or with related matters, or of legal, +commercial, philosophical, ethical or political position regarding +them. + +The “Invariant Sections” are certain Secondary Sections whose titles +are designated, as being those of Invariant Sections, in the notice +that says that the Document is released under this License. If a +section does not fit the above definition of Secondary then it is not +allowed to be designated as Invariant. The Document may contain zero +Invariant Sections. If the Document does not identify any Invariant +Sections then there are none. + +The “Cover Texts” are certain short passages of text that are listed, +as Front-Cover Texts or Back-Cover Texts, in the notice that says that +the Document is released under this License. A Front-Cover Text may +be at most 5 words, and a Back-Cover Text may be at most 25 words. + +A “Transparent” copy of the Document means a machine-readable copy, +represented in a format whose specification is available to the +general public, that is suitable for revising the document +straightforwardly with generic text editors or (for images composed of +pixels) generic paint programs or (for drawings) some widely available +drawing editor, and that is suitable for input to text formatters or +for automatic translation to a variety of formats suitable for input +to text formatters. A copy made in an otherwise Transparent file +format whose markup, or absence of markup, has been arranged to thwart +or discourage subsequent modification by readers is not Transparent. +An image format is not Transparent if used for any substantial amount +of text. A copy that is not “Transparent” is called “Opaque”. + +Examples of suitable formats for Transparent copies include plain +ASCII without markup, Texinfo input format, LaTeX input format, SGML +or XML using a publicly available DTD, and standard-conforming simple +HTML, PostScript or PDF designed for human modification. Examples of +transparent image formats include PNG, XCF and JPG. Opaque formats +include proprietary formats that can be read and edited only by +proprietary word processors, SGML or XML for which the DTD and/or +processing tools are not generally available, and the +machine-generated HTML, PostScript or PDF produced by some word +processors for output purposes only. + +The “Title Page” means, for a printed book, the title page itself, +plus such following pages as are needed to hold, legibly, the material +this License requires to appear in the title page. For works in +formats which do not have any title page as such, “Title Page” means +the text near the most prominent appearance of the work's title, +preceding the beginning of the body of the text. + +The “publisher” means any person or entity that distributes copies of +the Document to the public. + +A section “Entitled XYZ” means a named subunit of the Document whose +title either is precisely XYZ or contains XYZ in parentheses following +text that translates XYZ in another language. (Here XYZ stands for a +specific section name mentioned below, such as “Acknowledgements”, +“Dedications”, “Endorsements”, or “History”.) To “Preserve the Title” +of such a section when you modify the Document means that it remains a +section “Entitled XYZ” according to this definition. + +The Document may include Warranty Disclaimers next to the notice which +states that this License applies to the Document. These Warranty +Disclaimers are considered to be included by reference in this +License, but only as regards disclaiming warranties: any other +implication that these Warranty Disclaimers may have is void and has +no effect on the meaning of this License. + +### 2. VERBATIM COPYING + +You may copy and distribute the Document in any medium, either +commercially or noncommercially, provided that this License, the +copyright notices, and the license notice saying this License applies +to the Document are reproduced in all copies, and that you add no +other conditions whatsoever to those of this License. You may not use +technical measures to obstruct or control the reading or further +copying of the copies you make or distribute. However, you may accept +compensation in exchange for copies. If you distribute a large enough +number of copies you must also follow the conditions in section 3. + +You may also lend copies, under the same conditions stated above, and +you may publicly display copies. + + +### 3. COPYING IN QUANTITY + +If you publish printed copies (or copies in media that commonly have +printed covers) of the Document, numbering more than 100, and the +Document's license notice requires Cover Texts, you must enclose the +copies in covers that carry, clearly and legibly, all these Cover +Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on +the back cover. Both covers must also clearly and legibly identify +you as the publisher of these copies. The front cover must present +the full title with all words of the title equally prominent and +visible. You may add other material on the covers in addition. +Copying with changes limited to the covers, as long as they preserve +the title of the Document and satisfy these conditions, can be treated +as verbatim copying in other respects. + +If the required texts for either cover are too voluminous to fit +legibly, you should put the first ones listed (as many as fit +reasonably) on the actual cover, and continue the rest onto adjacent +pages. + +If you publish or distribute Opaque copies of the Document numbering +more than 100, you must either include a machine-readable Transparent +copy along with each Opaque copy, or state in or with each Opaque copy +a computer-network location from which the general network-using +public has access to download using public-standard network protocols +a complete Transparent copy of the Document, free of added material. +If you use the latter option, you must take reasonably prudent steps, +when you begin distribution of Opaque copies in quantity, to ensure +that this Transparent copy will remain thus accessible at the stated +location until at least one year after the last time you distribute an +Opaque copy (directly or through your agents or retailers) of that +edition to the public. + +It is requested, but not required, that you contact the authors of the +Document well before redistributing any large number of copies, to +give them a chance to provide you with an updated version of the +Document. + + +### 4. MODIFICATIONS + +You may copy and distribute a Modified Version of the Document under +the conditions of sections 2 and 3 above, provided that you release +the Modified Version under precisely this License, with the Modified +Version filling the role of the Document, thus licensing distribution +and modification of the Modified Version to whoever possesses a copy +of it. In addition, you must do these things in the Modified Version: + +* **A.** Use in the Title Page (and on the covers, if any) a title distinct +from that of the Document, and from those of previous versions +(which should, if there were any, be listed in the History section +of the Document). You may use the same title as a previous version +if the original publisher of that version gives permission. +* **B.** List on the Title Page, as authors, one or more persons or entities +responsible for authorship of the modifications in the Modified +Version, together with at least five of the principal authors of the +Document (all of its principal authors, if it has fewer than five), +unless they release you from this requirement. +* **C.** State on the Title page the name of the publisher of the +Modified Version, as the publisher. +* **D.** Preserve all the copyright notices of the Document. +* **E.** Add an appropriate copyright notice for your modifications +adjacent to the other copyright notices. +* **F.** Include, immediately after the copyright notices, a license notice +giving the public permission to use the Modified Version under the +terms of this License, in the form shown in the Addendum below. +* **G.** Preserve in that license notice the full lists of Invariant Sections +and required Cover Texts given in the Document's license notice. +* **H.** Include an unaltered copy of this License. +* **I.** Preserve the section Entitled “History”, Preserve its Title, and add +to it an item stating at least the title, year, new authors, and +publisher of the Modified Version as given on the Title Page. If +there is no section Entitled “History” in the Document, create one +stating the title, year, authors, and publisher of the Document as +given on its Title Page, then add an item describing the Modified +Version as stated in the previous sentence. +* **J.** Preserve the network location, if any, given in the Document for +public access to a Transparent copy of the Document, and likewise +the network locations given in the Document for previous versions +it was based on. These may be placed in the “History” section. +You may omit a network location for a work that was published at +least four years before the Document itself, or if the original +publisher of the version it refers to gives permission. +* **K.** For any section Entitled “Acknowledgements” or “Dedications”, +Preserve the Title of the section, and preserve in the section all +the substance and tone of each of the contributor acknowledgements +and/or dedications given therein. +* **L.** Preserve all the Invariant Sections of the Document, +unaltered in their text and in their titles. Section numbers +or the equivalent are not considered part of the section titles. +* **M.** Delete any section Entitled “Endorsements”. Such a section +may not be included in the Modified Version. +* **N.** Do not retitle any existing section to be Entitled “Endorsements” +or to conflict in title with any Invariant Section. +* **O.** Preserve any Warranty Disclaimers. + +If the Modified Version includes new front-matter sections or +appendices that qualify as Secondary Sections and contain no material +copied from the Document, you may at your option designate some or all +of these sections as invariant. To do this, add their titles to the +list of Invariant Sections in the Modified Version's license notice. +These titles must be distinct from any other section titles. + +You may add a section Entitled “Endorsements”, provided it contains +nothing but endorsements of your Modified Version by various +parties--for example, statements of peer review or that the text has +been approved by an organization as the authoritative definition of a +standard. + +You may add a passage of up to five words as a Front-Cover Text, and a +passage of up to 25 words as a Back-Cover Text, to the end of the list +of Cover Texts in the Modified Version. Only one passage of +Front-Cover Text and one of Back-Cover Text may be added by (or +through arrangements made by) any one entity. If the Document already +includes a cover text for the same cover, previously added by you or +by arrangement made by the same entity you are acting on behalf of, +you may not add another; but you may replace the old one, on explicit +permission from the previous publisher that added the old one. + +The author(s) and publisher(s) of the Document do not by this License +give permission to use their names for publicity for or to assert or +imply endorsement of any Modified Version. + + +### 5. COMBINING DOCUMENTS + +You may combine the Document with other documents released under this +License, under the terms defined in section 4 above for modified +versions, provided that you include in the combination all of the +Invariant Sections of all of the original documents, unmodified, and +list them all as Invariant Sections of your combined work in its +license notice, and that you preserve all their Warranty Disclaimers. + +The combined work need only contain one copy of this License, and +multiple identical Invariant Sections may be replaced with a single +copy. If there are multiple Invariant Sections with the same name but +different contents, make the title of each such section unique by +adding at the end of it, in parentheses, the name of the original +author or publisher of that section if known, or else a unique number. +Make the same adjustment to the section titles in the list of +Invariant Sections in the license notice of the combined work. + +In the combination, you must combine any sections Entitled “History” +in the various original documents, forming one section Entitled +“History”; likewise combine any sections Entitled “Acknowledgements”, +and any sections Entitled “Dedications”. You must delete all sections +Entitled “Endorsements”. + + +### 6. COLLECTIONS OF DOCUMENTS + +You may make a collection consisting of the Document and other +documents released under this License, and replace the individual +copies of this License in the various documents with a single copy +that is included in the collection, provided that you follow the rules +of this License for verbatim copying of each of the documents in all +other respects. + +You may extract a single document from such a collection, and +distribute it individually under this License, provided you insert a +copy of this License into the extracted document, and follow this +License in all other respects regarding verbatim copying of that +document. + + +### 7. AGGREGATION WITH INDEPENDENT WORKS + +A compilation of the Document or its derivatives with other separate +and independent documents or works, in or on a volume of a storage or +distribution medium, is called an “aggregate” if the copyright +resulting from the compilation is not used to limit the legal rights +of the compilation's users beyond what the individual works permit. +When the Document is included in an aggregate, this License does not +apply to the other works in the aggregate which are not themselves +derivative works of the Document. + +If the Cover Text requirement of section 3 is applicable to these +copies of the Document, then if the Document is less than one half of +the entire aggregate, the Document's Cover Texts may be placed on +covers that bracket the Document within the aggregate, or the +electronic equivalent of covers if the Document is in electronic form. +Otherwise they must appear on printed covers that bracket the whole +aggregate. + + +### 8. TRANSLATION + +Translation is considered a kind of modification, so you may +distribute translations of the Document under the terms of section 4. +Replacing Invariant Sections with translations requires special +permission from their copyright holders, but you may include +translations of some or all Invariant Sections in addition to the +original versions of these Invariant Sections. You may include a +translation of this License, and all the license notices in the +Document, and any Warranty Disclaimers, provided that you also include +the original English version of this License and the original versions +of those notices and disclaimers. In case of a disagreement between +the translation and the original version of this License or a notice +or disclaimer, the original version will prevail. + +If a section in the Document is Entitled “Acknowledgements”, +“Dedications”, or “History”, the requirement (section 4) to Preserve +its Title (section 1) will typically require changing the actual +title. + + +### 9. TERMINATION + +You may not copy, modify, sublicense, or distribute the Document +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense, or distribute it is void, and +will automatically terminate your rights under this License. + +However, if you cease all violation of this License, then your license +from a particular copyright holder is reinstated (a) provisionally, +unless and until the copyright holder explicitly and finally +terminates your license, and (b) permanently, if the copyright holder +fails to notify you of the violation by some reasonable means prior to +60 days after the cessation. + +Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + +Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, receipt of a copy of some or all of the same material does +not give you any rights to use it. + + +### 10. FUTURE REVISIONS OF THIS LICENSE + +The Free Software Foundation may publish new, revised versions of the +GNU Free Documentation 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. See +<>. + +Each version of the License is given a distinguishing version number. +If the Document specifies that a particular numbered version of this +License “or any later version” applies to it, you have the option of +following the terms and conditions either of that specified version or +of any later version that has been published (not as a draft) by the +Free Software Foundation. If the Document does not specify a version +number of this License, you may choose any version ever published (not +as a draft) by the Free Software Foundation. If the Document +specifies that a proxy can decide which future versions of this +License can be used, that proxy's public statement of acceptance of a +version permanently authorizes you to choose that version for the +Document. + +### 11. RELICENSING + +“Massive Multiauthor Collaboration Site” (or “MMC Site”) means any +World Wide Web server that publishes copyrightable works and also +provides prominent facilities for anybody to edit those works. A +public wiki that anybody can edit is an example of such a server. A +“Massive Multiauthor Collaboration” (or “MMC”) contained in the site +means any set of copyrightable works thus published on the MMC site. + +“CC-BY-SA” means the Creative Commons Attribution-Share Alike 3.0 +license published by Creative Commons Corporation, a not-for-profit +corporation with a principal place of business in San Francisco, +California, as well as future copyleft versions of that license +published by that same organization. + +“Incorporate” means to publish or republish a Document, in whole or in +part, as part of another Document. + +An MMC is “eligible for relicensing” if it is licensed under this +License, and if all works that were first published under this License +somewhere other than this MMC, and subsequently incorporated in whole or +in part into the MMC, (1) had no cover texts or invariant sections, and +(2) were thus incorporated prior to November 1, 2008. + +The operator of an MMC Site may republish an MMC contained in the site +under CC-BY-SA on the same site at any time before August 1, 2009, +provided the MMC is eligible for relicensing. + + +## ADDENDUM: How to use this License for your documents + +To use this License in a document you have written, include a copy of +the License in the document and put the following copyright and +license notices just after the title page: + + Copyright (c) YEAR YOUR NAME. + Permission is granted to copy, distribute and/or modify this document + under the terms of the GNU Free Documentation License, Version 1.3 + or any later version published by the Free Software Foundation; + with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. + A copy of the license is included in the section entitled “GNU + Free Documentation License”. + +If you have Invariant Sections, Front-Cover Texts and Back-Cover Texts, +replace the `with...Texts.` line with this: + + with the Invariant Sections being LIST THEIR TITLES, with the + Front-Cover Texts being LIST, and with the Back-Cover Texts being LIST. + +If you have Invariant Sections without Cover Texts, or some other +combination of the three, merge those two alternatives to suit the +situation. + +If your document contains nontrivial examples of program code, we +recommend releasing these examples in parallel under your choice of +free software license, such as the GNU General Public License, +to permit their use in free software. From 28d48b96b0f11643a7c7237efb867b8e3c243c2c Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sat, 8 Mar 2014 21:38:54 +0000 Subject: [PATCH 048/623] Solved problems when compiling with C++11 active --- configure.ac | 24 ++++++++++++------------ src/httpserver/webserver.hpp | 9 --------- src/webserver.cpp | 6 +++--- 3 files changed, 15 insertions(+), 24 deletions(-) diff --git a/configure.ac b/configure.ac index d313d94f..8259be76 100644 --- a/configure.ac +++ b/configure.ac @@ -121,18 +121,18 @@ if test x"have_gnutls" = x"yes"; then fi AC_MSG_CHECKING([whether to use c++0x std classes]) -AC_ARG_ENABLE([cpp0x], - [AS_HELP_STRING([--enable-cpp0x], - [enable c++0x std classes (def=no)])], - [cpp0x="$enableval"], - [cpp0x=no]) -AC_MSG_RESULT([$cpp0x]) - -if test x"$cpp0x" = x"yes"; then - AC_DEFINE([CPP0X],[],[c++0x Mode]) - AM_CXXFLAGS="$AM_CXXFLAGS -DUSE_CPP_ZEROX -std=c++0x" +AC_ARG_ENABLE([cpp11], + [AS_HELP_STRING([--enable-cpp11], + [enable c++11 std classes (def=no)])], + [cpp11="$enableval"], + [cpp11=no]) +AC_MSG_RESULT([$cpp11]) + +if test x"$cpp11" = x"yes"; then + AC_DEFINE([CPP11],[],[c++11 Mode]) + AM_CXXFLAGS="$AM_CXXFLAGS -DUSE_CPP_11 --std=c++11" else - AC_DEFINE([NCPP0X],[],[standard Mode]) + AC_DEFINE([NCPP11],[],[standard Mode]) fi DX_HTML_FEATURE(ON) @@ -176,5 +176,5 @@ AC_MSG_NOTICE([Configuration Summary: License : LGPL only Debug : ${debugit} TLS Enabled : ${have_gnutls} - C++0x : ${cpp0x} + C++11 : ${cpp11} ]) diff --git a/src/httpserver/webserver.hpp b/src/httpserver/webserver.hpp index 560ee5e4..b67e4ec3 100644 --- a/src/httpserver/webserver.hpp +++ b/src/httpserver/webserver.hpp @@ -33,11 +33,7 @@ #include #include #include -#ifdef USE_CPP_ZEROX -#include -#else #include -#endif #include #include #include @@ -235,13 +231,8 @@ class webserver std::map response_cache; int next_to_choose; pthread_rwlock_t cache_guard; -#ifdef USE_CPP_ZEROX - std::unordered_set bans; - std::unordered_set allowances; -#else std::set bans; std::set allowances; -#endif std::map > q_messages; std::map > q_waitings; diff --git a/src/webserver.cpp b/src/webserver.cpp index 32195376..32dd9b4c 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -250,7 +250,7 @@ bool webserver::register_resource( details::http_endpoint idx(resource, family, true, regex_checking); pair::iterator, bool> result = registered_resources.insert( - pair(idx, hrm) + map::value_type(idx, hrm) ); if(result.second) @@ -1414,7 +1414,7 @@ void webserver::register_to_topics( struct timeval curtime; gettimeofday(&curtime, NULL); q_keepalives[connection_id] = curtime.tv_sec; - q_keepalives_mem[connection_id] = make_pair( + q_keepalives_mem[connection_id] = make_pair( keepalive_secs, keepalive_msg ); } @@ -1425,7 +1425,7 @@ void webserver::register_to_topics( pthread_mutex_init(&m, NULL); pthread_cond_init(&c, NULL); q_blocks[connection_id] = - std::make_pair(m, c); + std::make_pair(m, c); } pthread_rwlock_unlock(&comet_guard); } From 608e62a4a619834da6450eee3ee55b1c96346de5 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sat, 8 Mar 2014 21:49:36 +0000 Subject: [PATCH 049/623] Updated travis script to compile also with C++11 option enabled --- .travis.yml | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 071b21f7..be832a29 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,4 +12,18 @@ before_install: - make - sudo make install - cd .. -script: ./bootstrap && mkdir build && cd build && ../configure && make && make check +script: + - ./bootstrap + - mkdir build + - cd build + - ../configure + - make + - make check + - cd .. + - rm -rf build + - mkdir build + - cd build + - ../configure --enable-cpp11 + - make + - make check + - cd .. From fa5a2593ad5c6567465979548112a7e2e0c39d3e Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sat, 8 Mar 2014 21:54:25 +0000 Subject: [PATCH 050/623] Travis g++ too old for --std=c++11 option --- .travis.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.travis.yml b/.travis.yml index be832a29..fc067d77 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,10 +20,10 @@ script: - make - make check - cd .. - - rm -rf build - - mkdir build - - cd build - - ../configure --enable-cpp11 - - make - - make check - - cd .. + # - rm -rf build + # - mkdir build + # - cd build + # - ../configure --enable-cpp11 + # - make + # - make check + # - cd .. From 933fc77797788023d2498bd202b3849b46275588 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sat, 15 Mar 2014 12:38:46 +0000 Subject: [PATCH 051/623] Temporary disabled texinfo doc production --- Makefile.am | 4 ++-- doc/version.texi | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Makefile.am b/Makefile.am index 55108fa9..4001ab70 100644 --- a/Makefile.am +++ b/Makefile.am @@ -24,8 +24,8 @@ LIBTOOL_DEPS = @LIBTOOL_DEPS@ AUTOMAKE_OPTIONS = foreign 1.4 ACLOCAL_AMFLAGS = -I m4 -SUBDIRS = src test doc -DIST_SUBDIRS = src test doc +SUBDIRS = src test +DIST_SUBDIRS = src test EXTRA_DIST = libhttpserver.pc.in debian/changelog.in debian/control.in debian/copyright.in debian/rules.in debian/libhttpserver-dev.install.in debian/libhttpserver.install.in redhat/libhttpserver.SPEC.in $(DX_CONFIG) MOSTLYCLEANFILES = $(DX_CLEANFILES) redhat/SOURCES/* diff --git a/doc/version.texi b/doc/version.texi index 4417f941..a8ff42c6 100644 --- a/doc/version.texi +++ b/doc/version.texi @@ -1,4 +1,4 @@ -@set UPDATED 12 January 2014 -@set UPDATED-MONTH January 2014 -@set EDITION 0.6.3 -@set VERSION 0.6.3 +@set UPDATED 8 March 2014 +@set UPDATED-MONTH March 2014 +@set EDITION 0.7.1 +@set VERSION 0.7.1 From fb95e82b582f750df12e3123d8400963d93d0242 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sat, 15 Mar 2014 12:45:28 +0000 Subject: [PATCH 052/623] Avoid warnings in bootstrap execution Modernized usage of autotools --- configure.ac | 2 +- src/Makefile.am | 2 +- test/Makefile.am | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/configure.ac b/configure.ac index 8259be76..c625a327 100644 --- a/configure.ac +++ b/configure.ac @@ -26,7 +26,7 @@ m4_define([libhttpserver_REVISION],[1])dnl m4_define([libhttpserver_PKG_VERSION],[libhttpserver_MAJOR_VERSION.libhttpserver_MINOR_VERSION.libhttpserver_REVISION])dnl m4_define([libhttpserver_LDF_VERSION],[libhttpserver_MAJOR_VERSION:libhttpserver_MINOR_VERSION:libhttpserver_REVISION])dnl AC_INIT([libhttpserver], libhttpserver_PKG_VERSION, [electrictwister2000@gmail.com]) -AM_INIT_AUTOMAKE([libhttpserver], libhttpserver_PKG_VERSION) +AM_INIT_AUTOMAKE AC_CONFIG_HEADERS([config.h]) AC_CONFIG_MACRO_DIR([m4]) diff --git a/src/Makefile.am b/src/Makefile.am index 1233bc0c..26d123d6 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -16,7 +16,7 @@ # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -INCLUDES = -I../ -I$(srcdir)/httpserver/ +AM_CPPFLAGS = -I../ -I$(srcdir)/httpserver/ METASOURCES = AUTO lib_LTLIBRARIES = libhttpserver.la libhttpserver_la_SOURCES = string_utilities.cpp webserver.cpp http_utils.cpp http_endpoint.cpp http_request.cpp http_response.cpp http_resource.cpp diff --git a/test/Makefile.am b/test/Makefile.am index 162f6d89..e5f6bee4 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -17,7 +17,7 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA LDADD = $(top_builddir)/src/libhttpserver.la -INCLUDES = -I$(top_srcdir)/src -I$(top_srcdir)/src/httpserver/ +AM_CPPFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/src/httpserver/ METASOURCES = AUTO check_PROGRAMS = basic From 8a14fd1126cef567c45c90289809fd7fbd7b89cd Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sat, 15 Mar 2014 12:50:14 +0000 Subject: [PATCH 053/623] Added license to files missing it --- examples/Test.cpp | 24 ++++++++++++++++++++++-- examples/Test.hpp | 20 ++++++++++++++++++++ examples/hello_world.cpp | 20 ++++++++++++++++++++ src/httpserver.hpp | 20 ++++++++++++++++++++ test/basic.cpp | 21 ++++++++++++++++++++- 5 files changed, 102 insertions(+), 3 deletions(-) diff --git a/examples/Test.cpp b/examples/Test.cpp index dbcafe06..cb78688a 100644 --- a/examples/Test.cpp +++ b/examples/Test.cpp @@ -1,3 +1,23 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011, 2012, 2013, 2014 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + #include "Test.hpp" #include #include @@ -17,11 +37,11 @@ void signal_callback_handler(int signum) ws_ptr->stop(); } */ -Test::Test() : http_resource() +Test::Test() : http_resource() { } -Test2::Test2() : http_resource() +Test2::Test2() : http_resource() { } diff --git a/examples/Test.hpp b/examples/Test.hpp index a447cf35..a5f37131 100644 --- a/examples/Test.hpp +++ b/examples/Test.hpp @@ -1,3 +1,23 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011, 2012, 2013, 2014 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + #ifndef _TEST_HPP_ #define _TEST_HPP_ #include diff --git a/examples/hello_world.cpp b/examples/hello_world.cpp index 5afac6e1..96fcd844 100755 --- a/examples/hello_world.cpp +++ b/examples/hello_world.cpp @@ -1,3 +1,23 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011, 2012, 2013, 2014 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + #include #include diff --git a/src/httpserver.hpp b/src/httpserver.hpp index 07d7edbb..6c5d86b9 100644 --- a/src/httpserver.hpp +++ b/src/httpserver.hpp @@ -1,3 +1,23 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011, 2012, 2013, 2014 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + #ifndef _HTTPSERVER_HPP_ #define _HTTPSERVER_HPP_ diff --git a/test/basic.cpp b/test/basic.cpp index 1f226a99..ed8dae12 100644 --- a/test/basic.cpp +++ b/test/basic.cpp @@ -1,3 +1,23 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011, 2012, 2013, 2014 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + #include "littletest.hpp" #include #include @@ -9,7 +29,6 @@ using namespace std; std::string lorem_ipsum(" , unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam eaque ipsa, quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt, explicabo. Nemo enim ipsam voluptatem, quia voluptas sit, aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos, qui ratione voluptatem sequi nesciunt, neque porro quisquam est, qui dolorem ipsum, quia dolor sit, amet, consectetur, adipisci v'elit, sed quia non numquam eius modi tempora incidunt, ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit, qui in ea voluptate velit esse, quam nihil molestiae consequatur, vel illum, qui dolorem eum fugiat, quo voluptas nulla pariatur? [33] At vero eos et accusamus et iusto odio dignissimos ducimus, qui blanditiis praesentium voluptatum deleniti atque corrupti, quos dolores et quas molestias excepturi sint, obcaecati cupiditate non provident, similique sunt in culpa, qui officia deserunt mollitia animi, id est laborum et dolorum fuga. Et harum quidem rerum facilis est et expedita distinctio. Nam libero tempore, cum soluta nobis est eligendi optio, cumque nihil impedit, quo minus id, quod maxime placeat, facere possimus, omnis voluptas assumenda est, omnis dolor repellendus. Temporibus autem quibusdam et aut officiis debitis aut rerum necessitatibus saepe eveniet, ut et voluptates repudiandae sint et molestiae non recusandae. Itaque earum rerum hic tenetur a sapiente delectus, ut aut reiciendis voluptatibus maiores alias consequatur aut perferendis doloribus asperiores repellat. , unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam eaque ipsa, quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt, explicabo. Nemo enim ipsam voluptatem, quia voluptas sit, aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos, qui ratione voluptatem sequi nesciunt, neque porro quisquam est, qui dolorem ipsum, quia dolor sit, amet, consectetur, adipisci v'elit, sed quia non numquam eius modi tempora incidunt, ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit, qui in ea voluptate velit esse, quam nihil molestiae consequatur, vel illum, qui dolorem eum fugiat, quo voluptas nulla pariatur? [33] At vero eos et accusamus et iusto odio dignissimos ducimus, qui blanditiis praesentium voluptatum deleniti atque corrupti, quos dolores et quas molestias excepturi sint, obcaecati cupiditate non provident, similique sunt in culpa, qui officia deserunt mollitia animi, id est laborum et dolorum fuga. Et harum quidem rerum facilis est et expedita distinctio. Nam libero tempore, cum soluta nobis est eligendi optio, cumque nihil impedit, quo minus id, quod maxime placeat, facere possimus, omnis voluptas assumenda est, omnis dolor repellendus. Temporibus autem quibusdam et aut officiis debitis aut rerum necessitatibus saepe eveniet, ut et voluptates repudiandae sint et molestiae non recusandae. Itaque earum rerum hic tenetur a sapiente delectus, ut aut reiciendis voluptatibus maiores alias consequatur aut perferendis doloribus asperiores repellat. , unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam eaque ipsa, quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt, explicabo. Nemo enim ipsam voluptatem, quia voluptas sit, aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos, qui ratione voluptatem sequi nesciunt, neque porro quisquam est, qui dolorem ipsum, quia dolor sit, amet, consectetur, adipisci v'elit, sed quia non numquam eius modi tempora incidunt, ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit, qui in ea voluptate velit esse, quam nihil molestiae consequatur, vel illum, qui dolorem eum fugiat, quo voluptas nulla pariatur? [33] At vero eos et accusamus et iusto odio dignissimos ducimus, qui blanditiis praesentium voluptatum deleniti atque corrupti, quos dolores et quas molestias excepturi sint, obcaecati cupiditate non provident, similique sunt in culpa, qui officia deserunt mollitia animi, id est laborum et dolorum fuga. Et harum quidem rerum facilis est et expedita distinctio. Nam libero tempore, cum soluta nobis est eligendi optio, cumque nihil impedit, quo minus id, quod maxime placeat, facere possimus, omnis voluptas assumenda est, omnis dolor repellendus. Temporibus autem quibusdam et aut officiis debitis aut rerum necessitatibus saepe eveniet, ut et voluptates repudiandae sint et molestiae non recusandae. Itaque earum rerum hic tenetur a sapiente delectus, ut aut reiciendis voluptatibus maiores alias consequatur aut perferendis doloribus asperiores repellat. , unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam eaque ipsa, quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt, explicabo. Nemo enim ipsam voluptatem, quia voluptas sit, aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos, qui ratione voluptatem sequi nesciunt, neque porro quisquam est, qui dolorem ipsum, quia dolor sit, amet, consectetur, adipisci v'elit, sed quia non numquam eius modi tempora incidunt, ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit, qui in ea voluptate velit esse, quam nihil molestiae consequatur, vel illum, qui dolorem eum fugiat, quo voluptas nulla pariatur? [33] At vero eos et accusamus et iusto odio dignissimos ducimus, qui blanditiis praesentium voluptatum deleniti atque corrupti, quos dolores et quas molestias excepturi sint, obcaecati cupiditate non provident, similique sunt in culpa, qui officia deserunt mollitia animi, id est laborum et dolorum fuga. Et harum quidem rerum facilis est et expedita distinctio. Nam libero tempore, cum soluta nobis est eligendi optio, cumque nihil impedit, quo minus id, quod maxime placeat, facere possimus, omnis voluptas assumenda est, omnis dolor repellendus. Temporibus autem quibusdam et aut officiis debitis aut rerum necessitatibus saepe eveniet, ut et voluptates repudiandae sint et molestiae non recusandae. Itaque earum rerum hic tenetur a sapiente delectus, ut aut reiciendis voluptatibus maiores alias consequatur aut perferendis doloribus asperiores repellat. , unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam eaque ipsa, quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt, explicabo. Nemo enim ipsam voluptatem, quia voluptas sit, aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos, qui ratione voluptatem sequi nesciunt, neque porro quisquam est, qui dolorem ipsum, quia dolor sit, amet, consectetur, adipisci v'elit, sed quia non numquam eius modi tempora incidunt, ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit, qui in ea voluptate velit esse, quam nihil molestiae consequatur, vel illum, qui dolorem eum fugiat, quo voluptas nulla pariatur? [33] At vero eos et accusamus et iusto odio dignissimos ducimus, qui blanditiis praesentium voluptatum deleniti atque corrupti, quos dolores et quas molestias excepturi sint, obcaecati cupiditate non provident, similique sunt in culpa, qui officia deserunt mollitia animi, id est laborum et dolorum fuga. Et harum quidem rerum facilis est et expedita distinctio. Nam libero tempore, cum soluta nobis est eligendi optio, cumque nihil impedit, quo minus id, quod maxime placeat, facere possimus, omnis voluptas assumenda est, omnis dolor repellendus. Temporibus autem quibusdam et aut officiis debitis aut rerum necessitatibus saepe eveniet, ut et voluptates repudiandae sint et molestiae non recusandae. Itaque earum rerum hic tenetur a sapiente delectus, ut aut reiciendis voluptatibus maiores alias consequatur aut perferendis doloribus asperiores repellat."); - size_t writefunc(void *ptr, size_t size, size_t nmemb, std::string *s) { s->append((char*) ptr, size*nmemb); From da3614e0706018f54d5ccc9dc31babb6b9fac851 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sat, 15 Mar 2014 12:58:19 +0000 Subject: [PATCH 054/623] Removed deprecated AC_PROG_LIBTOOL and replaced with LT_INIT --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index c625a327..60c69911 100644 --- a/configure.ac +++ b/configure.ac @@ -31,7 +31,7 @@ AC_CONFIG_HEADERS([config.h]) AC_CONFIG_MACRO_DIR([m4]) OLD_CXXFLAGS=$CXXFLAGS -AC_PROG_LIBTOOL +LT_INIT AC_PROG_CC AC_PROG_CXX AC_PROG_LN_S From 3e1376714393dbaf4331d2feb5a3fb1e530ca7be Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sat, 15 Mar 2014 16:02:11 +0000 Subject: [PATCH 055/623] Removed texinfo files They will be generated starting from markdown code --- doc/Makefile.am | 11 - doc/fdl-1.3.texi | 506 -- doc/lgpl.texi | 561 --- doc/libhttpserver.texi | 312 -- doc/texinfo.tex | 9977 ---------------------------------------- doc/version.texi | 4 - 6 files changed, 11371 deletions(-) delete mode 100644 doc/fdl-1.3.texi delete mode 100644 doc/lgpl.texi delete mode 100644 doc/libhttpserver.texi delete mode 100644 doc/texinfo.tex delete mode 100644 doc/version.texi diff --git a/doc/Makefile.am b/doc/Makefile.am index 69a8b038..50df01de 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -1,13 +1,2 @@ man_MANS = libhttpserver.3 EXTRA_DIST = $(man_MANS) - -DISTCLEANFILES = \ - libhttpserver.cps \ - libhttpserver.dvi \ - libhttpserver-tutorial.cps \ - libhttpserver-tutorial.dvi -info_TEXINFOS = \ - libhttpserver.texi -httpserver_TEXINFOS = \ - fdl-1.3.texi \ - lgpl.texi diff --git a/doc/fdl-1.3.texi b/doc/fdl-1.3.texi deleted file mode 100644 index 8805f1a4..00000000 --- a/doc/fdl-1.3.texi +++ /dev/null @@ -1,506 +0,0 @@ -@c The GNU Free Documentation License. -@center Version 1.3, 3 November 2008 - -@c This file is intended to be included within another document, -@c hence no sectioning command or @node. - -@display -Copyright @copyright{} 2000, 2001, 2002, 2007, 2008 Free Software Foundation, Inc. -@uref{http://fsf.org/} - -Everyone is permitted to copy and distribute verbatim copies -of this license document, but changing it is not allowed. -@end display - -@enumerate 0 -@item -PREAMBLE - -The purpose of this License is to make a manual, textbook, or other -functional and useful document @dfn{free} in the sense of freedom: to -assure everyone the effective freedom to copy and redistribute it, -with or without modifying it, either commercially or noncommercially. -Secondarily, this License preserves for the author and publisher a way -to get credit for their work, while not being considered responsible -for modifications made by others. - -This License is a kind of ``copyleft'', which means that derivative -works of the document must themselves be free in the same sense. It -complements the GNU General Public License, which is a copyleft -license designed for free software. - -We have designed this License in order to use it for manuals for free -software, because free software needs free documentation: a free -program should come with manuals providing the same freedoms that the -software does. But this License is not limited to software manuals; -it can be used for any textual work, regardless of subject matter or -whether it is published as a printed book. We recommend this License -principally for works whose purpose is instruction or reference. - -@item -APPLICABILITY AND DEFINITIONS - -This License applies to any manual or other work, in any medium, that -contains a notice placed by the copyright holder saying it can be -distributed under the terms of this License. Such a notice grants a -world-wide, royalty-free license, unlimited in duration, to use that -work under the conditions stated herein. The ``Document'', below, -refers to any such manual or work. Any member of the public is a -licensee, and is addressed as ``you''. You accept the license if you -copy, modify or distribute the work in a way requiring permission -under copyright law. - -A ``Modified Version'' of the Document means any work containing the -Document or a portion of it, either copied verbatim, or with -modifications and/or translated into another language. - -A ``Secondary Section'' is a named appendix or a front-matter section -of the Document that deals exclusively with the relationship of the -publishers or authors of the Document to the Document's overall -subject (or to related matters) and contains nothing that could fall -directly within that overall subject. (Thus, if the Document is in -part a textbook of mathematics, a Secondary Section may not explain -any mathematics.) The relationship could be a matter of historical -connection with the subject or with related matters, or of legal, -commercial, philosophical, ethical or political position regarding -them. - -The ``Invariant Sections'' are certain Secondary Sections whose titles -are designated, as being those of Invariant Sections, in the notice -that says that the Document is released under this License. If a -section does not fit the above definition of Secondary then it is not -allowed to be designated as Invariant. The Document may contain zero -Invariant Sections. If the Document does not identify any Invariant -Sections then there are none. - -The ``Cover Texts'' are certain short passages of text that are listed, -as Front-Cover Texts or Back-Cover Texts, in the notice that says that -the Document is released under this License. A Front-Cover Text may -be at most 5 words, and a Back-Cover Text may be at most 25 words. - -A ``Transparent'' copy of the Document means a machine-readable copy, -represented in a format whose specification is available to the -general public, that is suitable for revising the document -straightforwardly with generic text editors or (for images composed of -pixels) generic paint programs or (for drawings) some widely available -drawing editor, and that is suitable for input to text formatters or -for automatic translation to a variety of formats suitable for input -to text formatters. A copy made in an otherwise Transparent file -format whose markup, or absence of markup, has been arranged to thwart -or discourage subsequent modification by readers is not Transparent. -An image format is not Transparent if used for any substantial amount -of text. A copy that is not ``Transparent'' is called ``Opaque''. - -Examples of suitable formats for Transparent copies include plain -@sc{ascii} without markup, Texinfo input format, La@TeX{} input -format, @acronym{SGML} or @acronym{XML} using a publicly available -@acronym{DTD}, and standard-conforming simple @acronym{HTML}, -PostScript or @acronym{PDF} designed for human modification. Examples -of transparent image formats include @acronym{PNG}, @acronym{XCF} and -@acronym{JPG}. Opaque formats include proprietary formats that can be -read and edited only by proprietary word processors, @acronym{SGML} or -@acronym{XML} for which the @acronym{DTD} and/or processing tools are -not generally available, and the machine-generated @acronym{HTML}, -PostScript or @acronym{PDF} produced by some word processors for -output purposes only. - -The ``Title Page'' means, for a printed book, the title page itself, -plus such following pages as are needed to hold, legibly, the material -this License requires to appear in the title page. For works in -formats which do not have any title page as such, ``Title Page'' means -the text near the most prominent appearance of the work's title, -preceding the beginning of the body of the text. - -The ``publisher'' means any person or entity that distributes copies -of the Document to the public. - -A section ``Entitled XYZ'' means a named subunit of the Document whose -title either is precisely XYZ or contains XYZ in parentheses following -text that translates XYZ in another language. (Here XYZ stands for a -specific section name mentioned below, such as ``Acknowledgements'', -``Dedications'', ``Endorsements'', or ``History''.) To ``Preserve the Title'' -of such a section when you modify the Document means that it remains a -section ``Entitled XYZ'' according to this definition. - -The Document may include Warranty Disclaimers next to the notice which -states that this License applies to the Document. These Warranty -Disclaimers are considered to be included by reference in this -License, but only as regards disclaiming warranties: any other -implication that these Warranty Disclaimers may have is void and has -no effect on the meaning of this License. - -@item -VERBATIM COPYING - -You may copy and distribute the Document in any medium, either -commercially or noncommercially, provided that this License, the -copyright notices, and the license notice saying this License applies -to the Document are reproduced in all copies, and that you add no other -conditions whatsoever to those of this License. You may not use -technical measures to obstruct or control the reading or further -copying of the copies you make or distribute. However, you may accept -compensation in exchange for copies. If you distribute a large enough -number of copies you must also follow the conditions in section 3. - -You may also lend copies, under the same conditions stated above, and -you may publicly display copies. - -@item -COPYING IN QUANTITY - -If you publish printed copies (or copies in media that commonly have -printed covers) of the Document, numbering more than 100, and the -Document's license notice requires Cover Texts, you must enclose the -copies in covers that carry, clearly and legibly, all these Cover -Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on -the back cover. Both covers must also clearly and legibly identify -you as the publisher of these copies. The front cover must present -the full title with all words of the title equally prominent and -visible. You may add other material on the covers in addition. -Copying with changes limited to the covers, as long as they preserve -the title of the Document and satisfy these conditions, can be treated -as verbatim copying in other respects. - -If the required texts for either cover are too voluminous to fit -legibly, you should put the first ones listed (as many as fit -reasonably) on the actual cover, and continue the rest onto adjacent -pages. - -If you publish or distribute Opaque copies of the Document numbering -more than 100, you must either include a machine-readable Transparent -copy along with each Opaque copy, or state in or with each Opaque copy -a computer-network location from which the general network-using -public has access to download using public-standard network protocols -a complete Transparent copy of the Document, free of added material. -If you use the latter option, you must take reasonably prudent steps, -when you begin distribution of Opaque copies in quantity, to ensure -that this Transparent copy will remain thus accessible at the stated -location until at least one year after the last time you distribute an -Opaque copy (directly or through your agents or retailers) of that -edition to the public. - -It is requested, but not required, that you contact the authors of the -Document well before redistributing any large number of copies, to give -them a chance to provide you with an updated version of the Document. - -@item -MODIFICATIONS - -You may copy and distribute a Modified Version of the Document under -the conditions of sections 2 and 3 above, provided that you release -the Modified Version under precisely this License, with the Modified -Version filling the role of the Document, thus licensing distribution -and modification of the Modified Version to whoever possesses a copy -of it. In addition, you must do these things in the Modified Version: - -@enumerate A -@item -Use in the Title Page (and on the covers, if any) a title distinct -from that of the Document, and from those of previous versions -(which should, if there were any, be listed in the History section -of the Document). You may use the same title as a previous version -if the original publisher of that version gives permission. - -@item -List on the Title Page, as authors, one or more persons or entities -responsible for authorship of the modifications in the Modified -Version, together with at least five of the principal authors of the -Document (all of its principal authors, if it has fewer than five), -unless they release you from this requirement. - -@item -State on the Title page the name of the publisher of the -Modified Version, as the publisher. - -@item -Preserve all the copyright notices of the Document. - -@item -Add an appropriate copyright notice for your modifications -adjacent to the other copyright notices. - -@item -Include, immediately after the copyright notices, a license notice -giving the public permission to use the Modified Version under the -terms of this License, in the form shown in the Addendum below. - -@item -Preserve in that license notice the full lists of Invariant Sections -and required Cover Texts given in the Document's license notice. - -@item -Include an unaltered copy of this License. - -@item -Preserve the section Entitled ``History'', Preserve its Title, and add -to it an item stating at least the title, year, new authors, and -publisher of the Modified Version as given on the Title Page. If -there is no section Entitled ``History'' in the Document, create one -stating the title, year, authors, and publisher of the Document as -given on its Title Page, then add an item describing the Modified -Version as stated in the previous sentence. - -@item -Preserve the network location, if any, given in the Document for -public access to a Transparent copy of the Document, and likewise -the network locations given in the Document for previous versions -it was based on. These may be placed in the ``History'' section. -You may omit a network location for a work that was published at -least four years before the Document itself, or if the original -publisher of the version it refers to gives permission. - -@item -For any section Entitled ``Acknowledgements'' or ``Dedications'', Preserve -the Title of the section, and preserve in the section all the -substance and tone of each of the contributor acknowledgements and/or -dedications given therein. - -@item -Preserve all the Invariant Sections of the Document, -unaltered in their text and in their titles. Section numbers -or the equivalent are not considered part of the section titles. - -@item -Delete any section Entitled ``Endorsements''. Such a section -may not be included in the Modified Version. - -@item -Do not retitle any existing section to be Entitled ``Endorsements'' or -to conflict in title with any Invariant Section. - -@item -Preserve any Warranty Disclaimers. -@end enumerate - -If the Modified Version includes new front-matter sections or -appendices that qualify as Secondary Sections and contain no material -copied from the Document, you may at your option designate some or all -of these sections as invariant. To do this, add their titles to the -list of Invariant Sections in the Modified Version's license notice. -These titles must be distinct from any other section titles. - -You may add a section Entitled ``Endorsements'', provided it contains -nothing but endorsements of your Modified Version by various -parties---for example, statements of peer review or that the text has -been approved by an organization as the authoritative definition of a -standard. - -You may add a passage of up to five words as a Front-Cover Text, and a -passage of up to 25 words as a Back-Cover Text, to the end of the list -of Cover Texts in the Modified Version. Only one passage of -Front-Cover Text and one of Back-Cover Text may be added by (or -through arrangements made by) any one entity. If the Document already -includes a cover text for the same cover, previously added by you or -by arrangement made by the same entity you are acting on behalf of, -you may not add another; but you may replace the old one, on explicit -permission from the previous publisher that added the old one. - -The author(s) and publisher(s) of the Document do not by this License -give permission to use their names for publicity for or to assert or -imply endorsement of any Modified Version. - -@item -COMBINING DOCUMENTS - -You may combine the Document with other documents released under this -License, under the terms defined in section 4 above for modified -versions, provided that you include in the combination all of the -Invariant Sections of all of the original documents, unmodified, and -list them all as Invariant Sections of your combined work in its -license notice, and that you preserve all their Warranty Disclaimers. - -The combined work need only contain one copy of this License, and -multiple identical Invariant Sections may be replaced with a single -copy. If there are multiple Invariant Sections with the same name but -different contents, make the title of each such section unique by -adding at the end of it, in parentheses, the name of the original -author or publisher of that section if known, or else a unique number. -Make the same adjustment to the section titles in the list of -Invariant Sections in the license notice of the combined work. - -In the combination, you must combine any sections Entitled ``History'' -in the various original documents, forming one section Entitled -``History''; likewise combine any sections Entitled ``Acknowledgements'', -and any sections Entitled ``Dedications''. You must delete all -sections Entitled ``Endorsements.'' - -@item -COLLECTIONS OF DOCUMENTS - -You may make a collection consisting of the Document and other documents -released under this License, and replace the individual copies of this -License in the various documents with a single copy that is included in -the collection, provided that you follow the rules of this License for -verbatim copying of each of the documents in all other respects. - -You may extract a single document from such a collection, and distribute -it individually under this License, provided you insert a copy of this -License into the extracted document, and follow this License in all -other respects regarding verbatim copying of that document. - -@item -AGGREGATION WITH INDEPENDENT WORKS - -A compilation of the Document or its derivatives with other separate -and independent documents or works, in or on a volume of a storage or -distribution medium, is called an ``aggregate'' if the copyright -resulting from the compilation is not used to limit the legal rights -of the compilation's users beyond what the individual works permit. -When the Document is included in an aggregate, this License does not -apply to the other works in the aggregate which are not themselves -derivative works of the Document. - -If the Cover Text requirement of section 3 is applicable to these -copies of the Document, then if the Document is less than one half of -the entire aggregate, the Document's Cover Texts may be placed on -covers that bracket the Document within the aggregate, or the -electronic equivalent of covers if the Document is in electronic form. -Otherwise they must appear on printed covers that bracket the whole -aggregate. - -@item -TRANSLATION - -Translation is considered a kind of modification, so you may -distribute translations of the Document under the terms of section 4. -Replacing Invariant Sections with translations requires special -permission from their copyright holders, but you may include -translations of some or all Invariant Sections in addition to the -original versions of these Invariant Sections. You may include a -translation of this License, and all the license notices in the -Document, and any Warranty Disclaimers, provided that you also include -the original English version of this License and the original versions -of those notices and disclaimers. In case of a disagreement between -the translation and the original version of this License or a notice -or disclaimer, the original version will prevail. - -If a section in the Document is Entitled ``Acknowledgements'', -``Dedications'', or ``History'', the requirement (section 4) to Preserve -its Title (section 1) will typically require changing the actual -title. - -@item -TERMINATION - -You may not copy, modify, sublicense, or distribute the Document -except as expressly provided under this License. Any attempt -otherwise to copy, modify, sublicense, or distribute it is void, and -will automatically terminate your rights under this License. - -However, if you cease all violation of this License, then your license -from a particular copyright holder is reinstated (a) provisionally, -unless and until the copyright holder explicitly and finally -terminates your license, and (b) permanently, if the copyright holder -fails to notify you of the violation by some reasonable means prior to -60 days after the cessation. - -Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - -Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, receipt of a copy of some or all of the same material does -not give you any rights to use it. - -@item -FUTURE REVISIONS OF THIS LICENSE - -The Free Software Foundation may publish new, revised versions -of the GNU Free Documentation 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. See -@uref{http://www.gnu.org/copyleft/}. - -Each version of the License is given a distinguishing version number. -If the Document specifies that a particular numbered version of this -License ``or any later version'' applies to it, you have the option of -following the terms and conditions either of that specified version or -of any later version that has been published (not as a draft) by the -Free Software Foundation. If the Document does not specify a version -number of this License, you may choose any version ever published (not -as a draft) by the Free Software Foundation. If the Document -specifies that a proxy can decide which future versions of this -License can be used, that proxy's public statement of acceptance of a -version permanently authorizes you to choose that version for the -Document. - -@item -RELICENSING - -``Massive Multiauthor Collaboration Site'' (or ``MMC Site'') means any -World Wide Web server that publishes copyrightable works and also -provides prominent facilities for anybody to edit those works. A -public wiki that anybody can edit is an example of such a server. A -``Massive Multiauthor Collaboration'' (or ``MMC'') contained in the -site means any set of copyrightable works thus published on the MMC -site. - -``CC-BY-SA'' means the Creative Commons Attribution-Share Alike 3.0 -license published by Creative Commons Corporation, a not-for-profit -corporation with a principal place of business in San Francisco, -California, as well as future copyleft versions of that license -published by that same organization. - -``Incorporate'' means to publish or republish a Document, in whole or -in part, as part of another Document. - -An MMC is ``eligible for relicensing'' if it is licensed under this -License, and if all works that were first published under this License -somewhere other than this MMC, and subsequently incorporated in whole -or in part into the MMC, (1) had no cover texts or invariant sections, -and (2) were thus incorporated prior to November 1, 2008. - -The operator of an MMC Site may republish an MMC contained in the site -under CC-BY-SA on the same site at any time before August 1, 2009, -provided the MMC is eligible for relicensing. - -@end enumerate - -@page -@heading ADDENDUM: How to use this License for your documents - -To use this License in a document you have written, include a copy of -the License in the document and put the following copyright and -license notices just after the title page: - -@smallexample -@group - Copyright (C) @var{year} @var{your name}. - Permission is granted to copy, distribute and/or modify this document - under the terms of the GNU Free Documentation License, Version 1.3 - or any later version published by the Free Software Foundation; - with no Invariant Sections, no Front-Cover Texts, and no Back-Cover - Texts. A copy of the license is included in the section entitled ``GNU - Free Documentation License''. -@end group -@end smallexample - -If you have Invariant Sections, Front-Cover Texts and Back-Cover Texts, -replace the ``with@dots{}Texts.'' line with this: - -@smallexample -@group - with the Invariant Sections being @var{list their titles}, with - the Front-Cover Texts being @var{list}, and with the Back-Cover Texts - being @var{list}. -@end group -@end smallexample - -If you have Invariant Sections without Cover Texts, or some other -combination of the three, merge those two alternatives to suit the -situation. - -If your document contains nontrivial examples of program code, we -recommend releasing these examples in parallel under your choice of -free software license, such as the GNU General Public License, -to permit their use in free software. - -@c Local Variables: -@c ispell-local-pdict: "ispell-dict" -@c End: - diff --git a/doc/lgpl.texi b/doc/lgpl.texi deleted file mode 100644 index 260c0ce0..00000000 --- a/doc/lgpl.texi +++ /dev/null @@ -1,561 +0,0 @@ -@c The GNU Lesser General Public License. -@center Version 2.1, February 1999 - -@c This file is intended to be included within another document, -@c hence no sectioning command or @node. - -@display -Copyright @copyright{} 1991, 1999 Free Software Foundation, Inc. -51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - -Everyone is permitted to copy and distribute verbatim copies -of this license document, but changing it is not allowed. - -[This is the first released version of the Lesser GPL. It also counts -as the successor of the GNU Library Public License, version 2, hence the -version number 2.1.] -@end display - -@subheading Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -Licenses are intended to guarantee your freedom to share and change -free software---to make sure the software is free for all its users. - - This license, the Lesser General Public License, applies to some -specially designated software---typically libraries---of the Free -Software Foundation and other authors who decide to use it. You can use -it too, but we suggest you first think carefully about whether this -license or the ordinary General Public License is the better strategy to -use in any particular case, based on the explanations below. - - When we speak of free software, we are referring to freedom of use, -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 and use pieces of it -in new free programs; and that you are informed that you can do these -things. - - To protect your rights, we need to make restrictions that forbid -distributors to deny you these rights or to ask you to surrender these -rights. These restrictions translate to certain responsibilities for -you if you distribute copies of the library or if you modify it. - - For example, if you distribute copies of the library, whether gratis -or for a fee, you must give the recipients all the rights that we gave -you. You must make sure that they, too, receive or can get the source -code. If you link other code with the library, you must provide -complete object files to the recipients, so that they can relink them -with the library after making changes to the library and recompiling -it. And you must show them these terms so they know their rights. - - We protect your rights with a two-step method: (1) we copyright the -library, and (2) we offer you this license, which gives you legal -permission to copy, distribute and/or modify the library. - - To protect each distributor, we want to make it very clear that -there is no warranty for the free library. Also, if the library is -modified by someone else and passed on, the recipients should know -that what they have is not the original version, so that the original -author's reputation will not be affected by problems that might be -introduced by others. - - Finally, software patents pose a constant threat to the existence of -any free program. We wish to make sure that a company cannot -effectively restrict the users of a free program by obtaining a -restrictive license from a patent holder. Therefore, we insist that -any patent license obtained for a version of the library must be -consistent with the full freedom of use specified in this license. - - Most GNU software, including some libraries, is covered by the -ordinary GNU General Public License. This license, the GNU Lesser -General Public License, applies to certain designated libraries, and -is quite different from the ordinary General Public License. We use -this license for certain libraries in order to permit linking those -libraries into non-free programs. - - When a program is linked with a library, whether statically or using -a shared library, the combination of the two is legally speaking a -combined work, a derivative of the original library. The ordinary -General Public License therefore permits such linking only if the -entire combination fits its criteria of freedom. The Lesser General -Public License permits more lax criteria for linking other code with -the library. - - We call this license the @dfn{Lesser} General Public License because it -does @emph{Less} to protect the user's freedom than the ordinary General -Public License. It also provides other free software developers Less -of an advantage over competing non-free programs. These disadvantages -are the reason we use the ordinary General Public License for many -libraries. However, the Lesser license provides advantages in certain -special circumstances. - - For example, on rare occasions, there may be a special need to -encourage the widest possible use of a certain library, so that it becomes -a de-facto standard. To achieve this, non-free programs must be -allowed to use the library. A more frequent case is that a free -library does the same job as widely used non-free libraries. In this -case, there is little to gain by limiting the free library to free -software only, so we use the Lesser General Public License. - - In other cases, permission to use a particular library in non-free -programs enables a greater number of people to use a large body of -free software. For example, permission to use the GNU C Library in -non-free programs enables many more people to use the whole GNU -operating system, as well as its variant, the GNU/Linux operating -system. - - Although the Lesser General Public License is Less protective of the -users' freedom, it does ensure that the user of a program that is -linked with the Library has the freedom and the wherewithal to run -that program using a modified version of the Library. - - The precise terms and conditions for copying, distribution and -modification follow. Pay close attention to the difference between a -``work based on the library'' and a ``work that uses the library''. The -former contains code derived from the library, whereas the latter must -be combined with the library in order to run. - -@subheading TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - -@enumerate 0 -@item -This License Agreement applies to any software library or other program -which contains a notice placed by the copyright holder or other -authorized party saying it may be distributed under the terms of this -Lesser General Public License (also called ``this License''). Each -licensee is addressed as ``you''. - - A ``library'' means a collection of software functions and/or data -prepared so as to be conveniently linked with application programs -(which use some of those functions and data) to form executables. - - The ``Library'', below, refers to any such software library or work -which has been distributed under these terms. A ``work based on the -Library'' means either the Library or any derivative work under -copyright law: that is to say, a work containing the Library or a -portion of it, either verbatim or with modifications and/or translated -straightforwardly into another language. (Hereinafter, translation is -included without limitation in the term ``modification''.) - - ``Source code'' for a work means the preferred form of the work for -making modifications to it. For a library, complete source code means -all the source code for all modules it contains, plus any associated -interface definition files, plus the scripts used to control compilation -and installation of the library. - - Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running a program using the Library is not restricted, and output from -such a program is covered only if its contents constitute a work based -on the Library (independent of the use of the Library in a tool for -writing it). Whether that is true depends on what the Library does -and what the program that uses the Library does. - -@item -You may copy and distribute verbatim copies of the Library's -complete source code as you receive it, in any medium, provided that -you conspicuously and appropriately publish on each copy an -appropriate copyright notice and disclaimer of warranty; keep intact -all the notices that refer to this License and to the absence of any -warranty; and distribute a copy of this License along with the -Library. - - You may charge a fee for the physical act of transferring a copy, -and you may at your option offer warranty protection in exchange for a -fee. - -@item -You may modify your copy or copies of the Library or any portion -of it, thus forming a work based on the Library, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - -@enumerate a -@item -The modified work must itself be a software library. - -@item -You must cause the files modified to carry prominent notices -stating that you changed the files and the date of any change. - -@item -You must cause the whole of the work to be licensed at no -charge to all third parties under the terms of this License. - -@item -If a facility in the modified Library refers to a function or a -table of data to be supplied by an application program that uses -the facility, other than as an argument passed when the facility -is invoked, then you must make a good faith effort to ensure that, -in the event an application does not supply such function or -table, the facility still operates, and performs whatever part of -its purpose remains meaningful. - -(For example, a function in a library to compute square roots has -a purpose that is entirely well-defined independent of the -application. Therefore, Subsection 2d requires that any -application-supplied function or table used by this function must -be optional: if the application does not supply it, the square -root function must still compute square roots.) -@end enumerate - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Library, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Library, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote -it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Library. - -In addition, mere aggregation of another work not based on the Library -with the Library (or with a work based on the Library) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - -@item -You may opt to apply the terms of the ordinary GNU General Public -License instead of this License to a given copy of the Library. To do -this, you must alter all the notices that refer to this License, so -that they refer to the ordinary GNU General Public License, version 2, -instead of to this License. (If a newer version than version 2 of the -ordinary GNU General Public License has appeared, then you can specify -that version instead if you wish.) Do not make any other change in -these notices. - - Once this change is made in a given copy, it is irreversible for -that copy, so the ordinary GNU General Public License applies to all -subsequent copies and derivative works made from that copy. - - This option is useful when you wish to copy part of the code of -the Library into a program that is not a library. - -@item -You may copy and distribute the Library (or a portion or -derivative of it, under Section 2) in object code or executable form -under the terms of Sections 1 and 2 above provided that you accompany -it with the complete corresponding machine-readable source code, which -must be distributed under the terms of Sections 1 and 2 above on a -medium customarily used for software interchange. - - If distribution of object code is made by offering access to copy -from a designated place, then offering equivalent access to copy the -source code from the same place satisfies the requirement to -distribute the source code, even though third parties are not -compelled to copy the source along with the object code. - -@item -A program that contains no derivative of any portion of the -Library, but is designed to work with the Library by being compiled or -linked with it, is called a ``work that uses the Library''. Such a -work, in isolation, is not a derivative work of the Library, and -therefore falls outside the scope of this License. - - However, linking a ``work that uses the Library'' with the Library -creates an executable that is a derivative of the Library (because it -contains portions of the Library), rather than a ``work that uses the -library''. The executable is therefore covered by this License. -Section 6 states terms for distribution of such executables. - - When a ``work that uses the Library'' uses material from a header file -that is part of the Library, the object code for the work may be a -derivative work of the Library even though the source code is not. -Whether this is true is especially significant if the work can be -linked without the Library, or if the work is itself a library. The -threshold for this to be true is not precisely defined by law. - - If such an object file uses only numerical parameters, data -structure layouts and accessors, and small macros and small inline -functions (ten lines or less in length), then the use of the object -file is unrestricted, regardless of whether it is legally a derivative -work. (Executables containing this object code plus portions of the -Library will still fall under Section 6.) - - Otherwise, if the work is a derivative of the Library, you may -distribute the object code for the work under the terms of Section 6. -Any executables containing that work also fall under Section 6, -whether or not they are linked directly with the Library itself. - -@item -As an exception to the Sections above, you may also combine or -link a ``work that uses the Library'' with the Library to produce a -work containing portions of the Library, and distribute that work -under terms of your choice, provided that the terms permit -modification of the work for the customer's own use and reverse -engineering for debugging such modifications. - - You must give prominent notice with each copy of the work that the -Library is used in it and that the Library and its use are covered by -this License. You must supply a copy of this License. If the work -during execution displays copyright notices, you must include the -copyright notice for the Library among them, as well as a reference -directing the user to the copy of this License. Also, you must do one -of these things: - -@enumerate a -@item -Accompany the work with the complete corresponding -machine-readable source code for the Library including whatever -changes were used in the work (which must be distributed under -Sections 1 and 2 above); and, if the work is an executable linked -with the Library, with the complete machine-readable ``work that -uses the Library'', as object code and/or source code, so that the -user can modify the Library and then relink to produce a modified -executable containing the modified Library. (It is understood -that the user who changes the contents of definitions files in the -Library will not necessarily be able to recompile the application -to use the modified definitions.) - -@item -Use a suitable shared library mechanism for linking with the Library. A -suitable mechanism is one that (1) uses at run time a copy of the -library already present on the user's computer system, rather than -copying library functions into the executable, and (2) will operate -properly with a modified version of the library, if the user installs -one, as long as the modified version is interface-compatible with the -version that the work was made with. - -@item -Accompany the work with a written offer, valid for at -least three years, to give the same user the materials -specified in Subsection 6a, above, for a charge no more -than the cost of performing this distribution. - -@item -If distribution of the work is made by offering access to copy -from a designated place, offer equivalent access to copy the above -specified materials from the same place. - -@item -Verify that the user has already received a copy of these -materials or that you have already sent this user a copy. -@end enumerate - - For an executable, the required form of the ``work that uses the -Library'' must include any data and utility programs needed for -reproducing the executable from it. However, as a special exception, -the materials to be distributed need not include anything that is -normally distributed (in either source or binary form) with the major -components (compiler, kernel, and so on) of the operating system on -which the executable runs, unless that component itself accompanies the -executable. - - It may happen that this requirement contradicts the license -restrictions of other proprietary libraries that do not normally -accompany the operating system. Such a contradiction means you cannot -use both them and the Library together in an executable that you -distribute. - -@item -You may place library facilities that are a work based on the -Library side-by-side in a single library together with other library -facilities not covered by this License, and distribute such a combined -library, provided that the separate distribution of the work based on -the Library and of the other library facilities is otherwise -permitted, and provided that you do these two things: - -@enumerate a -@item -Accompany the combined library with a copy of the same work -based on the Library, uncombined with any other library -facilities. This must be distributed under the terms of the -Sections above. - -@item -Give prominent notice with the combined library of the fact -that part of it is a work based on the Library, and explaining -where to find the accompanying uncombined form of the same work. -@end enumerate - -@item -You may not copy, modify, sublicense, link with, or distribute -the Library except as expressly provided under this License. Any -attempt otherwise to copy, modify, sublicense, link with, or -distribute the Library is void, and will automatically terminate your -rights under this License. However, parties who have received copies, -or rights, from you under this License will not have their licenses -terminated so long as such parties remain in full compliance. - -@item -You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Library or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Library (or any work based on the -Library), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Library or works based on it. - -@item -Each time you redistribute the Library (or any work based on the -Library), the recipient automatically receives a license from the -original licensor to copy, distribute, link with or modify the Library -subject to these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties with -this License. - -@item -If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Library at all. For example, if a patent -license would not permit royalty-free redistribution of the Library by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Library. - -If any portion of this section is held invalid or unenforceable under any -particular circumstance, the balance of the section is intended to apply, -and the section as a whole is intended to apply in other circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - -@item -If the distribution and/or use of the Library is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Library under this License may add -an explicit geographical distribution limitation excluding those countries, -so that distribution is permitted only in or among countries not thus -excluded. In such case, this License incorporates the limitation as if -written in the body of this License. - -@item -The Free Software Foundation may publish revised and/or new -versions of the Lesser General Public License from time to time. -Such new versions will be similar in spirit to the present version, -but may differ in detail to address new problems or concerns. - -Each version is given a distinguishing version number. If the Library -specifies a version number of this License which applies to it and -``any later version'', you have the option of following the terms and -conditions either of that version or of any later version published by -the Free Software Foundation. If the Library does not specify a -license version number, you may choose any version ever published by -the Free Software Foundation. - -@item -If you wish to incorporate parts of the Library into other free -programs whose distribution conditions are incompatible with these, -write to the author to ask for permission. For software which is -copyrighted by the Free Software Foundation, write to the Free -Software Foundation; we sometimes make exceptions for this. Our -decision will be guided by the two goals of preserving the free status -of all derivatives of our free software and of promoting the sharing -and reuse of software generally. - -@iftex -@heading NO WARRANTY -@end iftex -@ifinfo -@center NO WARRANTY - -@end ifinfo - -@item -BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO -WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. -EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR -OTHER PARTIES PROVIDE THE LIBRARY ``AS IS'' WITHOUT WARRANTY OF ANY -KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE -LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME -THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - -@item -IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN -WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY -AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU -FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR -CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE -LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING -RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A -FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF -SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGES. -@end enumerate - -@iftex -@heading END OF TERMS AND CONDITIONS -@end iftex -@ifinfo -@center END OF TERMS AND CONDITIONS - -@end ifinfo - -@page -@subheading How to Apply These Terms to Your New Libraries - - If you develop a new library, and you want it to be of the greatest -possible use to the public, we recommend making it free software that -everyone can redistribute and change. You can do so by permitting -redistribution under these terms (or, alternatively, under the terms of the -ordinary General Public License). - - To apply these terms, attach the following notices to the library. It is -safest to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least the -``copyright'' line and a pointer to where the full notice is found. - -@smallexample -@var{one line to give the library's name and an idea of what it does.} -Copyright (C) @var{year} @var{name of author} - -This library is free software; you can redistribute it and/or modify it -under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation; either version 2.1 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 -Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public -License along with this library; if not, write to the Free Software -Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, -USA. -@end smallexample - -Also add information on how to contact you by electronic and paper mail. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a ``copyright disclaimer'' for the library, if -necessary. Here is a sample; alter the names: - -@smallexample -Yoyodyne, Inc., hereby disclaims all copyright interest in the library -`Frob' (a library for tweaking knobs) written by James Random Hacker. - -@var{signature of Ty Coon}, 1 April 1990 -Ty Coon, President of Vice -@end smallexample - -That's all there is to it! diff --git a/doc/libhttpserver.texi b/doc/libhttpserver.texi deleted file mode 100644 index 65930e87..00000000 --- a/doc/libhttpserver.texi +++ /dev/null @@ -1,312 +0,0 @@ -\input texinfo -@setfilename libhttpserver.info -@include version.texi -@settitle The libhttpserver Reference Manual -@c Unify all the indices into concept index. -@syncodeindex fn cp -@syncodeindex vr cp -@syncodeindex ky cp -@syncodeindex pg cp -@syncodeindex tp cp -@copying -This manual is for libhttpserver -(version @value{VERSION}, @value{UPDATED}), C++ library for creating an -embedded Rest HTTP server (and more). - -Copyright @copyright{} 2011--2013 Sebastiano Merlino - -@quotation -Permission is granted to copy, distribute and/or modify this document -under the terms of the GNU Free Documentation License, Version 1.3 -or any later version published by the Free Software Foundation; -with no Invariant Sections, no Front-Cover Texts, and no Back-Cover -Texts. A copy of the license is included in the section entitled "GNU -Free Documentation License". -@end quotation -@end copying - -@dircategory Software libraries -@direntry -* libhttpserver: (libhttpserver). C++ library for creating an -embedded Rest HTTP server (and more). -@end direntry - -@c -@c Titlepage -@c -@titlepage -@title The libhttpserver Reference Manual -@subtitle Version @value{VERSION} -@subtitle @value{UPDATED} -@author Sebastiano Merlino (@email{electrictwister2000@@gmail.com}) -@page -@vskip 0pt plus 1filll -@insertcopying -@end titlepage - -@ifnottex -@node Top -@top The libhttpserver Library -@insertcopying -@end ifnottex - -@c @summarycontents -@c @contents - -@c ------------------------------------------------------------ -@menu -* httpserver-intro:: Introduction. -* httpserver-const:: Constants. -* httpserver-struct:: Structures and classes type definition. -* httpserver-cb:: Callback functions definition. -* httpserver-init:: Create and work with server. -* httpserver-resources:: Registering resources. -* httpserver-responses:: Building responses to requests. -* httpserver-bans:: Whitelists and Blacklists. -* httpserver-comet:: Simple comet semantics. -* httpserver-dauth:: Utilizing Authentication. -* httpserver-info:: Obtaining and modifying status information. - -Appendices - -* GNU-LGPL:: The GNU Lesser General Public License says how you - can copy and share almost all of `libhttpserver'. -* GNU-FDL:: The GNU Free Documentation License says how you - can copy and share the documentation of `libhttpserver'. - -Indices - -* Concept Index:: Index of concepts and programs. -* Function and Data Index:: Index of functions, variables and data types. -* Type Index:: Index of data types. -@end menu - -@c ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - -@c ------------------------------------------------------------ -@node httpserver-intro -@chapter Introduction - - -@noindent -libhttpserver is meant to constitute an easy system to build HTTP -servers with REST fashion. -libhttpserver is based on libmicrohttpd and, like this, it is a -daemon library. -The mission of this library is to support all possible HTTP features -directly and with a simple semantic allowing then the user to concentrate -only on his application and not on HTTP request handling details. - -The library is supposed to work transparently for the client Implementing -the business logic and using the library itself to realize an interface. -If the user wants it must be able to change every behavior of the library -itself through the registration of callbacks. - -Like the api is based on (libmicrohttpd), libhttpserver is able to decode -certain body format a and automatically format them in object oriented -fashion. This is true for query arguments and for @code{POST} and @code{PUT} -requests bodies if @code{application/x-www-form-urlencoded} or -@code{multipart/form-data} header are passed. - -The header reproduce all the constants defined by libhttpserver. -These maps various constant used by the HTTP protocol that are exported -as a convenience for users of the library. Is is possible for the user -to define their own extensions of the HTTP standard and use those with -libhttpserver. - -All functions are guaranteed to be completely reentrant and -thread-safe. -Additionally, clients can specify resource limits on the overall -number of connections, number of connections per IP address and memory -used per connection to avoid resource exhaustion. - -@section Compiling libhttpserver -@cindex compilation -@cindex embedded systems -@cindex portability - -libhttpserver uses the standard system where the usual build process -involves running -@verbatim -$ ./bootstrap -$ mkdir build -$ cd build -$ ./configure -$ make -$ make install -@end verbatim - -@c ------------------------------------------------------------ -@node httpserver-const -@chapter Constants - -@c ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - -@c ------------------------------------------------------------ -@node httpserver-struct -@chapter Structures and classes type definition - - -@deftp {CPP Class} http_resource -Represents the resource corresponding to a specific endpoint. -@end deftp - -@deftp {CPP Class} http_request -Represents the request received by the resource that process it. -@end deftp - -@deftp {CPP Class} http_response -Represents the response sent by the server once the resource -finished its work. -@end deftp - -@deftp {CPP Class} webserver -Represents the daemon listening on a socket for HTTP traffic. -@end deftp - - -@c ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - -@c ------------------------------------------------------------ -@node httpserver-cb -@chapter Callback functions definition - -Callbacks are functions defined by the client and then used by the library. This way -it is possible to extend the library itself. - -@deftypefn {Function Pointer} void {*log_access_ptr} (const std::string& uri) -Invoked by the library when a new connection is opened by the client. - -@table @var -@item uri -A string containing the uri called by the client and the method it used. -@end table -@end deftypefn - - -@deftypefn {Function Pointer} void {*log_error_ptr} (const std::string& message) -Invoked by the library to send error messages. - -@table @var -@item message -A string containing the message to log. -@end table -@end deftypefn - -@deftypefn {Function Pointer} void {*unescaper_ptr} (char* uri) -Invoked by the library to unescape the uri requested by the client. -If no unescaper_ptr is passed to the webserver it uses the internal defined -unescaper function. - -@table @var -@item uri -A c-style string containing the uri to unescape. -@end table -@end deftypefn - - - -@c ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - -@c ------------------------------------------------------------ -@node httpserver-init -@chapter Create and work with server - -@deftypemethod {webserver}{} webserver (int port, const http_utils::start_method_T& start_method, int max_threads, int max_connections, int memory_limit, int connection_timeout, int per_IP_connection_limit, log_access_ptr log_access, log_error_ptr log_error, validator_ptr validator, unescaper_ptr unescaper, const struct sockaddr* bind_address, int bind_socket, int max_thread_stack_size, bool use_ssl, bool use_ipv6, bool debug, bool pedantic, const std::string& https_mem_key, const std::string& https_mem_cert, const std::string& https_mem_trust, const std::string& https_mem_priorities, const http_utils::cred_type_T& cred_type, const std::string digest_auth_random, int nonce_nc_size, const http_utils::policy_T& default_policy, bool basic_auth_enabled, bool digest_auth_enabled, bool regex_checking, bool ban_system_enabled, bool post_process_enabled, render_ptr single_resource, render_ptr not_found_resource, render_ptr method_not_allowed_resource, render_ptr method_not_acceptable_resource, render_ptr internal_error_resource) - -@end deftypemethod - -@c ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - -@c ----------------------------------------------------------- -@node httpserver-resources -@chapter Registering resources - - -@c ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - -@c ------------------------------------------------------------ -@node httpserver-responses -@chapter Building responses to requests - - -@c ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - -@c ------------------------------------------------------------ -@c @node httpserver-response create -@c @section Creating a response object - - - -@c ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - -@c ------------------------------------------------------------ -@c @node httpserver-response headers -@c @section Adding headers to a response - -@c ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - -@c ------------------------------------------------------------ -@c @node httpserver-response inspect -@c @section Inspecting a response object - -@c ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - -@c ------------------------------------------------------------ -@node httpserver-dauth -@chapter Utilizing Authentication - -@c ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - -@c ------------------------------------------------------------ -@node httpserver-info -@chapter Obtaining and modifying status information. - -@c ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - -@c ------------------------------------------------------------ -@node httpserver-bans -@chapter AAAA - - -@c ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - -@c ------------------------------------------------------------ -@node httpserver-comet -@chapter AAAA - - - -@c ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - - -@c ********************************************************** -@c ******************* Appendices ************************* -@c ********************************************************** - -@node GNU-LGPL -@unnumbered GNU-LGPL -@cindex license -@include lgpl.texi - -@node GNU-FDL -@unnumbered GNU-FDL -@cindex license -@include fdl-1.3.texi - -@node Concept Index -@unnumbered Concept Index - -@printindex cp - -@node Function and Data Index -@unnumbered Function and Data Index - -@printindex fn - -@node Type Index -@unnumbered Type Index - -@printindex tp - -@bye diff --git a/doc/texinfo.tex b/doc/texinfo.tex deleted file mode 100644 index 85b68e79..00000000 --- a/doc/texinfo.tex +++ /dev/null @@ -1,9977 +0,0 @@ -% texinfo.tex -- TeX macros to handle Texinfo files. -% -% Load plain if necessary, i.e., if running under initex. -\expandafter\ifx\csname fmtname\endcsname\relax\input plain\fi -% -\def\texinfoversion{2012-03-11.15} -% -% Copyright 1985, 1986, 1988, 1990, 1991, 1992, 1993, 1994, 1995, -% 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, -% 2007, 2008, 2009, 2010, 2011, 2012 Free Software Foundation, Inc. -% -% This texinfo.tex file 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 3 of the -% License, or (at your option) any later version. -% -% This texinfo.tex file 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, see . -% -% As a special exception, when this file is read by TeX when processing -% a Texinfo source document, you may use the result without -% restriction. (This has been our intent since Texinfo was invented.) -% -% Please try the latest version of texinfo.tex before submitting bug -% reports; you can get the latest version from: -% http://www.gnu.org/software/texinfo/ (the Texinfo home page), or -% ftp://tug.org/tex/texinfo.tex -% (and all CTAN mirrors, see http://www.ctan.org). -% The texinfo.tex in any given distribution could well be out -% of date, so if that's what you're using, please check. -% -% Send bug reports to bug-texinfo@gnu.org. Please include including a -% complete document in each bug report with which we can reproduce the -% problem. Patches are, of course, greatly appreciated. -% -% To process a Texinfo manual with TeX, it's most reliable to use the -% texi2dvi shell script that comes with the distribution. For a simple -% manual foo.texi, however, you can get away with this: -% tex foo.texi -% texindex foo.?? -% tex foo.texi -% tex foo.texi -% dvips foo.dvi -o # or whatever; this makes foo.ps. -% The extra TeX runs get the cross-reference information correct. -% Sometimes one run after texindex suffices, and sometimes you need more -% than two; texi2dvi does it as many times as necessary. -% -% It is possible to adapt texinfo.tex for other languages, to some -% extent. You can get the existing language-specific files from the -% full Texinfo distribution. -% -% The GNU Texinfo home page is http://www.gnu.org/software/texinfo. - - -\message{Loading texinfo [version \texinfoversion]:} - -% If in a .fmt file, print the version number -% and turn on active characters that we couldn't do earlier because -% they might have appeared in the input file name. -\everyjob{\message{[Texinfo version \texinfoversion]}% - \catcode`+=\active \catcode`\_=\active} - -\chardef\other=12 - -% We never want plain's \outer definition of \+ in Texinfo. -% For @tex, we can use \tabalign. -\let\+ = \relax - -% Save some plain tex macros whose names we will redefine. -\let\ptexb=\b -\let\ptexbullet=\bullet -\let\ptexc=\c -\let\ptexcomma=\, -\let\ptexdot=\. -\let\ptexdots=\dots -\let\ptexend=\end -\let\ptexequiv=\equiv -\let\ptexexclam=\! -\let\ptexfootnote=\footnote -\let\ptexgtr=> -\let\ptexhat=^ -\let\ptexi=\i -\let\ptexindent=\indent -\let\ptexinsert=\insert -\let\ptexlbrace=\{ -\let\ptexless=< -\let\ptexnewwrite\newwrite -\let\ptexnoindent=\noindent -\let\ptexplus=+ -\let\ptexraggedright=\raggedright -\let\ptexrbrace=\} -\let\ptexslash=\/ -\let\ptexstar=\* -\let\ptext=\t -\let\ptextop=\top -{\catcode`\'=\active \global\let\ptexquoteright'}% active in plain's math mode - -% If this character appears in an error message or help string, it -% starts a new line in the output. -\newlinechar = `^^J - -% Use TeX 3.0's \inputlineno to get the line number, for better error -% messages, but if we're using an old version of TeX, don't do anything. -% -\ifx\inputlineno\thisisundefined - \let\linenumber = \empty % Pre-3.0. -\else - \def\linenumber{l.\the\inputlineno:\space} -\fi - -% Set up fixed words for English if not already set. -\ifx\putwordAppendix\undefined \gdef\putwordAppendix{Appendix}\fi -\ifx\putwordChapter\undefined \gdef\putwordChapter{Chapter}\fi -\ifx\putworderror\undefined \gdef\putworderror{error}\fi -\ifx\putwordfile\undefined \gdef\putwordfile{file}\fi -\ifx\putwordin\undefined \gdef\putwordin{in}\fi -\ifx\putwordIndexIsEmpty\undefined \gdef\putwordIndexIsEmpty{(Index is empty)}\fi -\ifx\putwordIndexNonexistent\undefined \gdef\putwordIndexNonexistent{(Index is nonexistent)}\fi -\ifx\putwordInfo\undefined \gdef\putwordInfo{Info}\fi -\ifx\putwordInstanceVariableof\undefined \gdef\putwordInstanceVariableof{Instance Variable of}\fi -\ifx\putwordMethodon\undefined \gdef\putwordMethodon{Method on}\fi -\ifx\putwordNoTitle\undefined \gdef\putwordNoTitle{No Title}\fi -\ifx\putwordof\undefined \gdef\putwordof{of}\fi -\ifx\putwordon\undefined \gdef\putwordon{on}\fi -\ifx\putwordpage\undefined \gdef\putwordpage{page}\fi -\ifx\putwordsection\undefined \gdef\putwordsection{section}\fi -\ifx\putwordSection\undefined \gdef\putwordSection{Section}\fi -\ifx\putwordsee\undefined \gdef\putwordsee{see}\fi -\ifx\putwordSee\undefined \gdef\putwordSee{See}\fi -\ifx\putwordShortTOC\undefined \gdef\putwordShortTOC{Short Contents}\fi -\ifx\putwordTOC\undefined \gdef\putwordTOC{Table of Contents}\fi -% -\ifx\putwordMJan\undefined \gdef\putwordMJan{January}\fi -\ifx\putwordMFeb\undefined \gdef\putwordMFeb{February}\fi -\ifx\putwordMMar\undefined \gdef\putwordMMar{March}\fi -\ifx\putwordMApr\undefined \gdef\putwordMApr{April}\fi -\ifx\putwordMMay\undefined \gdef\putwordMMay{May}\fi -\ifx\putwordMJun\undefined \gdef\putwordMJun{June}\fi -\ifx\putwordMJul\undefined \gdef\putwordMJul{July}\fi -\ifx\putwordMAug\undefined \gdef\putwordMAug{August}\fi -\ifx\putwordMSep\undefined \gdef\putwordMSep{September}\fi -\ifx\putwordMOct\undefined \gdef\putwordMOct{October}\fi -\ifx\putwordMNov\undefined \gdef\putwordMNov{November}\fi -\ifx\putwordMDec\undefined \gdef\putwordMDec{December}\fi -% -\ifx\putwordDefmac\undefined \gdef\putwordDefmac{Macro}\fi -\ifx\putwordDefspec\undefined \gdef\putwordDefspec{Special Form}\fi -\ifx\putwordDefvar\undefined \gdef\putwordDefvar{Variable}\fi -\ifx\putwordDefopt\undefined \gdef\putwordDefopt{User Option}\fi -\ifx\putwordDeffunc\undefined \gdef\putwordDeffunc{Function}\fi - -% Since the category of space is not known, we have to be careful. -\chardef\spacecat = 10 -\def\spaceisspace{\catcode`\ =\spacecat} - -% sometimes characters are active, so we need control sequences. -\chardef\ampChar = `\& -\chardef\colonChar = `\: -\chardef\commaChar = `\, -\chardef\dashChar = `\- -\chardef\dotChar = `\. -\chardef\exclamChar= `\! -\chardef\hashChar = `\# -\chardef\lquoteChar= `\` -\chardef\questChar = `\? -\chardef\rquoteChar= `\' -\chardef\semiChar = `\; -\chardef\slashChar = `\/ -\chardef\underChar = `\_ - -% Ignore a token. -% -\def\gobble#1{} - -% The following is used inside several \edef's. -\def\makecsname#1{\expandafter\noexpand\csname#1\endcsname} - -% Hyphenation fixes. -\hyphenation{ - Flor-i-da Ghost-script Ghost-view Mac-OS Post-Script - ap-pen-dix bit-map bit-maps - data-base data-bases eshell fall-ing half-way long-est man-u-script - man-u-scripts mini-buf-fer mini-buf-fers over-view par-a-digm - par-a-digms rath-er rec-tan-gu-lar ro-bot-ics se-vere-ly set-up spa-ces - spell-ing spell-ings - stand-alone strong-est time-stamp time-stamps which-ever white-space - wide-spread wrap-around -} - -% Margin to add to right of even pages, to left of odd pages. -\newdimen\bindingoffset -\newdimen\normaloffset -\newdimen\pagewidth \newdimen\pageheight - -% For a final copy, take out the rectangles -% that mark overfull boxes (in case you have decided -% that the text looks ok even though it passes the margin). -% -\def\finalout{\overfullrule=0pt } - -% Sometimes it is convenient to have everything in the transcript file -% and nothing on the terminal. We don't just call \tracingall here, -% since that produces some useless output on the terminal. We also make -% some effort to order the tracing commands to reduce output in the log -% file; cf. trace.sty in LaTeX. -% -\def\gloggingall{\begingroup \globaldefs = 1 \loggingall \endgroup}% -\def\loggingall{% - \tracingstats2 - \tracingpages1 - \tracinglostchars2 % 2 gives us more in etex - \tracingparagraphs1 - \tracingoutput1 - \tracingmacros2 - \tracingrestores1 - \showboxbreadth\maxdimen \showboxdepth\maxdimen - \ifx\eTeXversion\thisisundefined\else % etex gives us more logging - \tracingscantokens1 - \tracingifs1 - \tracinggroups1 - \tracingnesting2 - \tracingassigns1 - \fi - \tracingcommands3 % 3 gives us more in etex - \errorcontextlines16 -}% - -% @errormsg{MSG}. Do the index-like expansions on MSG, but if things -% aren't perfect, it's not the end of the world, being an error message, -% after all. -% -\def\errormsg{\begingroup \indexnofonts \doerrormsg} -\def\doerrormsg#1{\errmessage{#1}} - -% add check for \lastpenalty to plain's definitions. If the last thing -% we did was a \nobreak, we don't want to insert more space. -% -\def\smallbreak{\ifnum\lastpenalty<10000\par\ifdim\lastskip<\smallskipamount - \removelastskip\penalty-50\smallskip\fi\fi} -\def\medbreak{\ifnum\lastpenalty<10000\par\ifdim\lastskip<\medskipamount - \removelastskip\penalty-100\medskip\fi\fi} -\def\bigbreak{\ifnum\lastpenalty<10000\par\ifdim\lastskip<\bigskipamount - \removelastskip\penalty-200\bigskip\fi\fi} - -% Do @cropmarks to get crop marks. -% -\newif\ifcropmarks -\let\cropmarks = \cropmarkstrue -% -% Dimensions to add cropmarks at corners. -% Added by P. A. MacKay, 12 Nov. 1986 -% -\newdimen\outerhsize \newdimen\outervsize % set by the paper size routines -\newdimen\cornerlong \cornerlong=1pc -\newdimen\cornerthick \cornerthick=.3pt -\newdimen\topandbottommargin \topandbottommargin=.75in - -% Output a mark which sets \thischapter, \thissection and \thiscolor. -% We dump everything together because we only have one kind of mark. -% This works because we only use \botmark / \topmark, not \firstmark. -% -% A mark contains a subexpression of the \ifcase ... \fi construct. -% \get*marks macros below extract the needed part using \ifcase. -% -% Another complication is to let the user choose whether \thischapter -% (\thissection) refers to the chapter (section) in effect at the top -% of a page, or that at the bottom of a page. The solution is -% described on page 260 of The TeXbook. It involves outputting two -% marks for the sectioning macros, one before the section break, and -% one after. I won't pretend I can describe this better than DEK... -\def\domark{% - \toks0=\expandafter{\lastchapterdefs}% - \toks2=\expandafter{\lastsectiondefs}% - \toks4=\expandafter{\prevchapterdefs}% - \toks6=\expandafter{\prevsectiondefs}% - \toks8=\expandafter{\lastcolordefs}% - \mark{% - \the\toks0 \the\toks2 - \noexpand\or \the\toks4 \the\toks6 - \noexpand\else \the\toks8 - }% -} -% \topmark doesn't work for the very first chapter (after the title -% page or the contents), so we use \firstmark there -- this gets us -% the mark with the chapter defs, unless the user sneaks in, e.g., -% @setcolor (or @url, or @link, etc.) between @contents and the very -% first @chapter. -\def\gettopheadingmarks{% - \ifcase0\topmark\fi - \ifx\thischapter\empty \ifcase0\firstmark\fi \fi -} -\def\getbottomheadingmarks{\ifcase1\botmark\fi} -\def\getcolormarks{\ifcase2\topmark\fi} - -% Avoid "undefined control sequence" errors. -\def\lastchapterdefs{} -\def\lastsectiondefs{} -\def\prevchapterdefs{} -\def\prevsectiondefs{} -\def\lastcolordefs{} - -% Main output routine. -\chardef\PAGE = 255 -\output = {\onepageout{\pagecontents\PAGE}} - -\newbox\headlinebox -\newbox\footlinebox - -% \onepageout takes a vbox as an argument. Note that \pagecontents -% does insertions, but you have to call it yourself. -\def\onepageout#1{% - \ifcropmarks \hoffset=0pt \else \hoffset=\normaloffset \fi - % - \ifodd\pageno \advance\hoffset by \bindingoffset - \else \advance\hoffset by -\bindingoffset\fi - % - % Do this outside of the \shipout so @code etc. will be expanded in - % the headline as they should be, not taken literally (outputting ''code). - \ifodd\pageno \getoddheadingmarks \else \getevenheadingmarks \fi - \setbox\headlinebox = \vbox{\let\hsize=\pagewidth \makeheadline}% - \ifodd\pageno \getoddfootingmarks \else \getevenfootingmarks \fi - \setbox\footlinebox = \vbox{\let\hsize=\pagewidth \makefootline}% - % - {% - % Have to do this stuff outside the \shipout because we want it to - % take effect in \write's, yet the group defined by the \vbox ends - % before the \shipout runs. - % - \indexdummies % don't expand commands in the output. - \normalturnoffactive % \ in index entries must not stay \, e.g., if - % the page break happens to be in the middle of an example. - % We don't want .vr (or whatever) entries like this: - % \entry{{\tt \indexbackslash }acronym}{32}{\code {\acronym}} - % "\acronym" won't work when it's read back in; - % it needs to be - % {\code {{\tt \backslashcurfont }acronym} - \shipout\vbox{% - % Do this early so pdf references go to the beginning of the page. - \ifpdfmakepagedest \pdfdest name{\the\pageno} xyz\fi - % - \ifcropmarks \vbox to \outervsize\bgroup - \hsize = \outerhsize - \vskip-\topandbottommargin - \vtop to0pt{% - \line{\ewtop\hfil\ewtop}% - \nointerlineskip - \line{% - \vbox{\moveleft\cornerthick\nstop}% - \hfill - \vbox{\moveright\cornerthick\nstop}% - }% - \vss}% - \vskip\topandbottommargin - \line\bgroup - \hfil % center the page within the outer (page) hsize. - \ifodd\pageno\hskip\bindingoffset\fi - \vbox\bgroup - \fi - % - \unvbox\headlinebox - \pagebody{#1}% - \ifdim\ht\footlinebox > 0pt - % Only leave this space if the footline is nonempty. - % (We lessened \vsize for it in \oddfootingyyy.) - % The \baselineskip=24pt in plain's \makefootline has no effect. - \vskip 24pt - \unvbox\footlinebox - \fi - % - \ifcropmarks - \egroup % end of \vbox\bgroup - \hfil\egroup % end of (centering) \line\bgroup - \vskip\topandbottommargin plus1fill minus1fill - \boxmaxdepth = \cornerthick - \vbox to0pt{\vss - \line{% - \vbox{\moveleft\cornerthick\nsbot}% - \hfill - \vbox{\moveright\cornerthick\nsbot}% - }% - \nointerlineskip - \line{\ewbot\hfil\ewbot}% - }% - \egroup % \vbox from first cropmarks clause - \fi - }% end of \shipout\vbox - }% end of group with \indexdummies - \advancepageno - \ifnum\outputpenalty>-20000 \else\dosupereject\fi -} - -\newinsert\margin \dimen\margin=\maxdimen - -\def\pagebody#1{\vbox to\pageheight{\boxmaxdepth=\maxdepth #1}} -{\catcode`\@ =11 -\gdef\pagecontents#1{\ifvoid\topins\else\unvbox\topins\fi -% marginal hacks, juha@viisa.uucp (Juha Takala) -\ifvoid\margin\else % marginal info is present - \rlap{\kern\hsize\vbox to\z@{\kern1pt\box\margin \vss}}\fi -\dimen@=\dp#1\relax \unvbox#1\relax -\ifvoid\footins\else\vskip\skip\footins\footnoterule \unvbox\footins\fi -\ifr@ggedbottom \kern-\dimen@ \vfil \fi} -} - -% Here are the rules for the cropmarks. Note that they are -% offset so that the space between them is truly \outerhsize or \outervsize -% (P. A. MacKay, 12 November, 1986) -% -\def\ewtop{\vrule height\cornerthick depth0pt width\cornerlong} -\def\nstop{\vbox - {\hrule height\cornerthick depth\cornerlong width\cornerthick}} -\def\ewbot{\vrule height0pt depth\cornerthick width\cornerlong} -\def\nsbot{\vbox - {\hrule height\cornerlong depth\cornerthick width\cornerthick}} - -% Parse an argument, then pass it to #1. The argument is the rest of -% the input line (except we remove a trailing comment). #1 should be a -% macro which expects an ordinary undelimited TeX argument. -% -\def\parsearg{\parseargusing{}} -\def\parseargusing#1#2{% - \def\argtorun{#2}% - \begingroup - \obeylines - \spaceisspace - #1% - \parseargline\empty% Insert the \empty token, see \finishparsearg below. -} - -{\obeylines % - \gdef\parseargline#1^^M{% - \endgroup % End of the group started in \parsearg. - \argremovecomment #1\comment\ArgTerm% - }% -} - -% First remove any @comment, then any @c comment. -\def\argremovecomment#1\comment#2\ArgTerm{\argremovec #1\c\ArgTerm} -\def\argremovec#1\c#2\ArgTerm{\argcheckspaces#1\^^M\ArgTerm} - -% Each occurrence of `\^^M' or `\^^M' is replaced by a single space. -% -% \argremovec might leave us with trailing space, e.g., -% @end itemize @c foo -% This space token undergoes the same procedure and is eventually removed -% by \finishparsearg. -% -\def\argcheckspaces#1\^^M{\argcheckspacesX#1\^^M \^^M} -\def\argcheckspacesX#1 \^^M{\argcheckspacesY#1\^^M} -\def\argcheckspacesY#1\^^M#2\^^M#3\ArgTerm{% - \def\temp{#3}% - \ifx\temp\empty - % Do not use \next, perhaps the caller of \parsearg uses it; reuse \temp: - \let\temp\finishparsearg - \else - \let\temp\argcheckspaces - \fi - % Put the space token in: - \temp#1 #3\ArgTerm -} - -% If a _delimited_ argument is enclosed in braces, they get stripped; so -% to get _exactly_ the rest of the line, we had to prevent such situation. -% We prepended an \empty token at the very beginning and we expand it now, -% just before passing the control to \argtorun. -% (Similarly, we have to think about #3 of \argcheckspacesY above: it is -% either the null string, or it ends with \^^M---thus there is no danger -% that a pair of braces would be stripped. -% -% But first, we have to remove the trailing space token. -% -\def\finishparsearg#1 \ArgTerm{\expandafter\argtorun\expandafter{#1}} - -% \parseargdef\foo{...} -% is roughly equivalent to -% \def\foo{\parsearg\Xfoo} -% \def\Xfoo#1{...} -% -% Actually, I use \csname\string\foo\endcsname, ie. \\foo, as it is my -% favourite TeX trick. --kasal, 16nov03 - -\def\parseargdef#1{% - \expandafter \doparseargdef \csname\string#1\endcsname #1% -} -\def\doparseargdef#1#2{% - \def#2{\parsearg#1}% - \def#1##1% -} - -% Several utility definitions with active space: -{ - \obeyspaces - \gdef\obeyedspace{ } - - % Make each space character in the input produce a normal interword - % space in the output. Don't allow a line break at this space, as this - % is used only in environments like @example, where each line of input - % should produce a line of output anyway. - % - \gdef\sepspaces{\obeyspaces\let =\tie} - - % If an index command is used in an @example environment, any spaces - % therein should become regular spaces in the raw index file, not the - % expansion of \tie (\leavevmode \penalty \@M \ ). - \gdef\unsepspaces{\let =\space} -} - - -\def\flushcr{\ifx\par\lisppar \def\next##1{}\else \let\next=\relax \fi \next} - -% Define the framework for environments in texinfo.tex. It's used like this: -% -% \envdef\foo{...} -% \def\Efoo{...} -% -% It's the responsibility of \envdef to insert \begingroup before the -% actual body; @end closes the group after calling \Efoo. \envdef also -% defines \thisenv, so the current environment is known; @end checks -% whether the environment name matches. The \checkenv macro can also be -% used to check whether the current environment is the one expected. -% -% Non-false conditionals (@iftex, @ifset) don't fit into this, so they -% are not treated as environments; they don't open a group. (The -% implementation of @end takes care not to call \endgroup in this -% special case.) - - -% At run-time, environments start with this: -\def\startenvironment#1{\begingroup\def\thisenv{#1}} -% initialize -\let\thisenv\empty - -% ... but they get defined via ``\envdef\foo{...}'': -\long\def\envdef#1#2{\def#1{\startenvironment#1#2}} -\def\envparseargdef#1#2{\parseargdef#1{\startenvironment#1#2}} - -% Check whether we're in the right environment: -\def\checkenv#1{% - \def\temp{#1}% - \ifx\thisenv\temp - \else - \badenverr - \fi -} - -% Environment mismatch, #1 expected: -\def\badenverr{% - \errhelp = \EMsimple - \errmessage{This command can appear only \inenvironment\temp, - not \inenvironment\thisenv}% -} -\def\inenvironment#1{% - \ifx#1\empty - outside of any environment% - \else - in environment \expandafter\string#1% - \fi -} - -% @end foo executes the definition of \Efoo. -% But first, it executes a specialized version of \checkenv -% -\parseargdef\end{% - \if 1\csname iscond.#1\endcsname - \else - % The general wording of \badenverr may not be ideal. - \expandafter\checkenv\csname#1\endcsname - \csname E#1\endcsname - \endgroup - \fi -} - -\newhelp\EMsimple{Press RETURN to continue.} - - -% Be sure we're in horizontal mode when doing a tie, since we make space -% equivalent to this in @example-like environments. Otherwise, a space -% at the beginning of a line will start with \penalty -- and -% since \penalty is valid in vertical mode, we'd end up putting the -% penalty on the vertical list instead of in the new paragraph. -{\catcode`@ = 11 - % Avoid using \@M directly, because that causes trouble - % if the definition is written into an index file. - \global\let\tiepenalty = \@M - \gdef\tie{\leavevmode\penalty\tiepenalty\ } -} - -% @: forces normal size whitespace following. -\def\:{\spacefactor=1000 } - -% @* forces a line break. -\def\*{\hfil\break\hbox{}\ignorespaces} - -% @/ allows a line break. -\let\/=\allowbreak - -% @. is an end-of-sentence period. -\def\.{.\spacefactor=\endofsentencespacefactor\space} - -% @! is an end-of-sentence bang. -\def\!{!\spacefactor=\endofsentencespacefactor\space} - -% @? is an end-of-sentence query. -\def\?{?\spacefactor=\endofsentencespacefactor\space} - -% @frenchspacing on|off says whether to put extra space after punctuation. -% -\def\onword{on} -\def\offword{off} -% -\parseargdef\frenchspacing{% - \def\temp{#1}% - \ifx\temp\onword \plainfrenchspacing - \else\ifx\temp\offword \plainnonfrenchspacing - \else - \errhelp = \EMsimple - \errmessage{Unknown @frenchspacing option `\temp', must be on|off}% - \fi\fi -} - -% @w prevents a word break. Without the \leavevmode, @w at the -% beginning of a paragraph, when TeX is still in vertical mode, would -% produce a whole line of output instead of starting the paragraph. -\def\w#1{\leavevmode\hbox{#1}} - -% @group ... @end group forces ... to be all on one page, by enclosing -% it in a TeX vbox. We use \vtop instead of \vbox to construct the box -% to keep its height that of a normal line. According to the rules for -% \topskip (p.114 of the TeXbook), the glue inserted is -% max (\topskip - \ht (first item), 0). If that height is large, -% therefore, no glue is inserted, and the space between the headline and -% the text is small, which looks bad. -% -% Another complication is that the group might be very large. This can -% cause the glue on the previous page to be unduly stretched, because it -% does not have much material. In this case, it's better to add an -% explicit \vfill so that the extra space is at the bottom. The -% threshold for doing this is if the group is more than \vfilllimit -% percent of a page (\vfilllimit can be changed inside of @tex). -% -\newbox\groupbox -\def\vfilllimit{0.7} -% -\envdef\group{% - \ifnum\catcode`\^^M=\active \else - \errhelp = \groupinvalidhelp - \errmessage{@group invalid in context where filling is enabled}% - \fi - \startsavinginserts - % - \setbox\groupbox = \vtop\bgroup - % Do @comment since we are called inside an environment such as - % @example, where each end-of-line in the input causes an - % end-of-line in the output. We don't want the end-of-line after - % the `@group' to put extra space in the output. Since @group - % should appear on a line by itself (according to the Texinfo - % manual), we don't worry about eating any user text. - \comment -} -% -% The \vtop produces a box with normal height and large depth; thus, TeX puts -% \baselineskip glue before it, and (when the next line of text is done) -% \lineskip glue after it. Thus, space below is not quite equal to space -% above. But it's pretty close. -\def\Egroup{% - % To get correct interline space between the last line of the group - % and the first line afterwards, we have to propagate \prevdepth. - \endgraf % Not \par, as it may have been set to \lisppar. - \global\dimen1 = \prevdepth - \egroup % End the \vtop. - % \dimen0 is the vertical size of the group's box. - \dimen0 = \ht\groupbox \advance\dimen0 by \dp\groupbox - % \dimen2 is how much space is left on the page (more or less). - \dimen2 = \pageheight \advance\dimen2 by -\pagetotal - % if the group doesn't fit on the current page, and it's a big big - % group, force a page break. - \ifdim \dimen0 > \dimen2 - \ifdim \pagetotal < \vfilllimit\pageheight - \page - \fi - \fi - \box\groupbox - \prevdepth = \dimen1 - \checkinserts -} -% -% TeX puts in an \escapechar (i.e., `@') at the beginning of the help -% message, so this ends up printing `@group can only ...'. -% -\newhelp\groupinvalidhelp{% -group can only be used in environments such as @example,^^J% -where each line of input produces a line of output.} - -% @need space-in-mils -% forces a page break if there is not space-in-mils remaining. - -\newdimen\mil \mil=0.001in - -\parseargdef\need{% - % Ensure vertical mode, so we don't make a big box in the middle of a - % paragraph. - \par - % - % If the @need value is less than one line space, it's useless. - \dimen0 = #1\mil - \dimen2 = \ht\strutbox - \advance\dimen2 by \dp\strutbox - \ifdim\dimen0 > \dimen2 - % - % Do a \strut just to make the height of this box be normal, so the - % normal leading is inserted relative to the preceding line. - % And a page break here is fine. - \vtop to #1\mil{\strut\vfil}% - % - % TeX does not even consider page breaks if a penalty added to the - % main vertical list is 10000 or more. But in order to see if the - % empty box we just added fits on the page, we must make it consider - % page breaks. On the other hand, we don't want to actually break the - % page after the empty box. So we use a penalty of 9999. - % - % There is an extremely small chance that TeX will actually break the - % page at this \penalty, if there are no other feasible breakpoints in - % sight. (If the user is using lots of big @group commands, which - % almost-but-not-quite fill up a page, TeX will have a hard time doing - % good page breaking, for example.) However, I could not construct an - % example where a page broke at this \penalty; if it happens in a real - % document, then we can reconsider our strategy. - \penalty9999 - % - % Back up by the size of the box, whether we did a page break or not. - \kern -#1\mil - % - % Do not allow a page break right after this kern. - \nobreak - \fi -} - -% @br forces paragraph break (and is undocumented). - -\let\br = \par - -% @page forces the start of a new page. -% -\def\page{\par\vfill\supereject} - -% @exdent text.... -% outputs text on separate line in roman font, starting at standard page margin - -% This records the amount of indent in the innermost environment. -% That's how much \exdent should take out. -\newskip\exdentamount - -% This defn is used inside fill environments such as @defun. -\parseargdef\exdent{\hfil\break\hbox{\kern -\exdentamount{\rm#1}}\hfil\break} - -% This defn is used inside nofill environments such as @example. -\parseargdef\nofillexdent{{\advance \leftskip by -\exdentamount - \leftline{\hskip\leftskip{\rm#1}}}} - -% @inmargin{WHICH}{TEXT} puts TEXT in the WHICH margin next to the current -% paragraph. For more general purposes, use the \margin insertion -% class. WHICH is `l' or `r'. Not documented, written for gawk manual. -% -\newskip\inmarginspacing \inmarginspacing=1cm -\def\strutdepth{\dp\strutbox} -% -\def\doinmargin#1#2{\strut\vadjust{% - \nobreak - \kern-\strutdepth - \vtop to \strutdepth{% - \baselineskip=\strutdepth - \vss - % if you have multiple lines of stuff to put here, you'll need to - % make the vbox yourself of the appropriate size. - \ifx#1l% - \llap{\ignorespaces #2\hskip\inmarginspacing}% - \else - \rlap{\hskip\hsize \hskip\inmarginspacing \ignorespaces #2}% - \fi - \null - }% -}} -\def\inleftmargin{\doinmargin l} -\def\inrightmargin{\doinmargin r} -% -% @inmargin{TEXT [, RIGHT-TEXT]} -% (if RIGHT-TEXT is given, use TEXT for left page, RIGHT-TEXT for right; -% else use TEXT for both). -% -\def\inmargin#1{\parseinmargin #1,,\finish} -\def\parseinmargin#1,#2,#3\finish{% not perfect, but better than nothing. - \setbox0 = \hbox{\ignorespaces #2}% - \ifdim\wd0 > 0pt - \def\lefttext{#1}% have both texts - \def\righttext{#2}% - \else - \def\lefttext{#1}% have only one text - \def\righttext{#1}% - \fi - % - \ifodd\pageno - \def\temp{\inrightmargin\righttext}% odd page -> outside is right margin - \else - \def\temp{\inleftmargin\lefttext}% - \fi - \temp -} - -% @| inserts a changebar to the left of the current line. It should -% surround any changed text. This approach does *not* work if the -% change spans more than two lines of output. To handle that, we would -% have adopt a much more difficult approach (putting marks into the main -% vertical list for the beginning and end of each change). This command -% is not documented, not supported, and doesn't work. -% -\def\|{% - % \vadjust can only be used in horizontal mode. - \leavevmode - % - % Append this vertical mode material after the current line in the output. - \vadjust{% - % We want to insert a rule with the height and depth of the current - % leading; that is exactly what \strutbox is supposed to record. - \vskip-\baselineskip - % - % \vadjust-items are inserted at the left edge of the type. So - % the \llap here moves out into the left-hand margin. - \llap{% - % - % For a thicker or thinner bar, change the `1pt'. - \vrule height\baselineskip width1pt - % - % This is the space between the bar and the text. - \hskip 12pt - }% - }% -} - -% @include FILE -- \input text of FILE. -% -\def\include{\parseargusing\filenamecatcodes\includezzz} -\def\includezzz#1{% - \pushthisfilestack - \def\thisfile{#1}% - {% - \makevalueexpandable % we want to expand any @value in FILE. - \turnoffactive % and allow special characters in the expansion - \indexnofonts % Allow `@@' and other weird things in file names. - \wlog{texinfo.tex: doing @include of #1^^J}% - \edef\temp{\noexpand\input #1 }% - % - % This trickery is to read FILE outside of a group, in case it makes - % definitions, etc. - \expandafter - }\temp - \popthisfilestack -} -\def\filenamecatcodes{% - \catcode`\\=\other - \catcode`~=\other - \catcode`^=\other - \catcode`_=\other - \catcode`|=\other - \catcode`<=\other - \catcode`>=\other - \catcode`+=\other - \catcode`-=\other - \catcode`\`=\other - \catcode`\'=\other -} - -\def\pushthisfilestack{% - \expandafter\pushthisfilestackX\popthisfilestack\StackTerm -} -\def\pushthisfilestackX{% - \expandafter\pushthisfilestackY\thisfile\StackTerm -} -\def\pushthisfilestackY #1\StackTerm #2\StackTerm {% - \gdef\popthisfilestack{\gdef\thisfile{#1}\gdef\popthisfilestack{#2}}% -} - -\def\popthisfilestack{\errthisfilestackempty} -\def\errthisfilestackempty{\errmessage{Internal error: - the stack of filenames is empty.}} -% -\def\thisfile{} - -% @center line -% outputs that line, centered. -% -\parseargdef\center{% - \ifhmode - \let\centersub\centerH - \else - \let\centersub\centerV - \fi - \centersub{\hfil \ignorespaces#1\unskip \hfil}% - \let\centersub\relax % don't let the definition persist, just in case -} -\def\centerH#1{{% - \hfil\break - \advance\hsize by -\leftskip - \advance\hsize by -\rightskip - \line{#1}% - \break -}} -% -\newcount\centerpenalty -\def\centerV#1{% - % The idea here is the same as in \startdefun, \cartouche, etc.: if - % @center is the first thing after a section heading, we need to wipe - % out the negative parskip inserted by \sectionheading, but still - % prevent a page break here. - \centerpenalty = \lastpenalty - \ifnum\centerpenalty>10000 \vskip\parskip \fi - \ifnum\centerpenalty>9999 \penalty\centerpenalty \fi - \line{\kern\leftskip #1\kern\rightskip}% -} - -% @sp n outputs n lines of vertical space -% -\parseargdef\sp{\vskip #1\baselineskip} - -% @comment ...line which is ignored... -% @c is the same as @comment -% @ignore ... @end ignore is another way to write a comment -% -\def\comment{\begingroup \catcode`\^^M=\other% -\catcode`\@=\other \catcode`\{=\other \catcode`\}=\other% -\commentxxx} -{\catcode`\^^M=\other \gdef\commentxxx#1^^M{\endgroup}} -% -\let\c=\comment - -% @paragraphindent NCHARS -% We'll use ems for NCHARS, close enough. -% NCHARS can also be the word `asis' or `none'. -% We cannot feasibly implement @paragraphindent asis, though. -% -\def\asisword{asis} % no translation, these are keywords -\def\noneword{none} -% -\parseargdef\paragraphindent{% - \def\temp{#1}% - \ifx\temp\asisword - \else - \ifx\temp\noneword - \defaultparindent = 0pt - \else - \defaultparindent = #1em - \fi - \fi - \parindent = \defaultparindent -} - -% @exampleindent NCHARS -% We'll use ems for NCHARS like @paragraphindent. -% It seems @exampleindent asis isn't necessary, but -% I preserve it to make it similar to @paragraphindent. -\parseargdef\exampleindent{% - \def\temp{#1}% - \ifx\temp\asisword - \else - \ifx\temp\noneword - \lispnarrowing = 0pt - \else - \lispnarrowing = #1em - \fi - \fi -} - -% @firstparagraphindent WORD -% If WORD is `none', then suppress indentation of the first paragraph -% after a section heading. If WORD is `insert', then do indent at such -% paragraphs. -% -% The paragraph indentation is suppressed or not by calling -% \suppressfirstparagraphindent, which the sectioning commands do. -% We switch the definition of this back and forth according to WORD. -% By default, we suppress indentation. -% -\def\suppressfirstparagraphindent{\dosuppressfirstparagraphindent} -\def\insertword{insert} -% -\parseargdef\firstparagraphindent{% - \def\temp{#1}% - \ifx\temp\noneword - \let\suppressfirstparagraphindent = \dosuppressfirstparagraphindent - \else\ifx\temp\insertword - \let\suppressfirstparagraphindent = \relax - \else - \errhelp = \EMsimple - \errmessage{Unknown @firstparagraphindent option `\temp'}% - \fi\fi -} - -% Here is how we actually suppress indentation. Redefine \everypar to -% \kern backwards by \parindent, and then reset itself to empty. -% -% We also make \indent itself not actually do anything until the next -% paragraph. -% -\gdef\dosuppressfirstparagraphindent{% - \gdef\indent{% - \restorefirstparagraphindent - \indent - }% - \gdef\noindent{% - \restorefirstparagraphindent - \noindent - }% - \global\everypar = {% - \kern -\parindent - \restorefirstparagraphindent - }% -} - -\gdef\restorefirstparagraphindent{% - \global \let \indent = \ptexindent - \global \let \noindent = \ptexnoindent - \global \everypar = {}% -} - - -% @refill is a no-op. -\let\refill=\relax - -% If working on a large document in chapters, it is convenient to -% be able to disable indexing, cross-referencing, and contents, for test runs. -% This is done with @novalidate (before @setfilename). -% -\newif\iflinks \linkstrue % by default we want the aux files. -\let\novalidate = \linksfalse - -% @setfilename is done at the beginning of every texinfo file. -% So open here the files we need to have open while reading the input. -% This makes it possible to make a .fmt file for texinfo. -\def\setfilename{% - \fixbackslash % Turn off hack to swallow `\input texinfo'. - \iflinks - \tryauxfile - % Open the new aux file. TeX will close it automatically at exit. - \immediate\openout\auxfile=\jobname.aux - \fi % \openindices needs to do some work in any case. - \openindices - \let\setfilename=\comment % Ignore extra @setfilename cmds. - % - % If texinfo.cnf is present on the system, read it. - % Useful for site-wide @afourpaper, etc. - \openin 1 texinfo.cnf - \ifeof 1 \else \input texinfo.cnf \fi - \closein 1 - % - \comment % Ignore the actual filename. -} - -% Called from \setfilename. -% -\def\openindices{% - \newindex{cp}% - \newcodeindex{fn}% - \newcodeindex{vr}% - \newcodeindex{tp}% - \newcodeindex{ky}% - \newcodeindex{pg}% -} - -% @bye. -\outer\def\bye{\pagealignmacro\tracingstats=1\ptexend} - - -\message{pdf,} -% adobe `portable' document format -\newcount\tempnum -\newcount\lnkcount -\newtoks\filename -\newcount\filenamelength -\newcount\pgn -\newtoks\toksA -\newtoks\toksB -\newtoks\toksC -\newtoks\toksD -\newbox\boxA -\newcount\countA -\newif\ifpdf -\newif\ifpdfmakepagedest - -% when pdftex is run in dvi mode, \pdfoutput is defined (so \pdfoutput=1 -% can be set). So we test for \relax and 0 as well as being undefined. -\ifx\pdfoutput\thisisundefined -\else - \ifx\pdfoutput\relax - \else - \ifcase\pdfoutput - \else - \pdftrue - \fi - \fi -\fi - -% PDF uses PostScript string constants for the names of xref targets, -% for display in the outlines, and in other places. Thus, we have to -% double any backslashes. Otherwise, a name like "\node" will be -% interpreted as a newline (\n), followed by o, d, e. Not good. -% -% See http://www.ntg.nl/pipermail/ntg-pdftex/2004-July/000654.html and -% related messages. The final outcome is that it is up to the TeX user -% to double the backslashes and otherwise make the string valid, so -% that's what we do. pdftex 1.30.0 (ca.2005) introduced a primitive to -% do this reliably, so we use it. - -% #1 is a control sequence in which to do the replacements, -% which we \xdef. -\def\txiescapepdf#1{% - \ifx\pdfescapestring\relax - % No primitive available; should we give a warning or log? - % Many times it won't matter. - \else - % The expandable \pdfescapestring primitive escapes parentheses, - % backslashes, and other special chars. - \xdef#1{\pdfescapestring{#1}}% - \fi -} - -\newhelp\nopdfimagehelp{Texinfo supports .png, .jpg, .jpeg, and .pdf images -with PDF output, and none of those formats could be found. (.eps cannot -be supported due to the design of the PDF format; use regular TeX (DVI -output) for that.)} - -\ifpdf - % - % Color manipulation macros based on pdfcolor.tex, - % except using rgb instead of cmyk; the latter is said to render as a - % very dark gray on-screen and a very dark halftone in print, instead - % of actual black. - \def\rgbDarkRed{0.50 0.09 0.12} - \def\rgbBlack{0 0 0} - % - % k sets the color for filling (usual text, etc.); - % K sets the color for stroking (thin rules, e.g., normal _'s). - \def\pdfsetcolor#1{\pdfliteral{#1 rg #1 RG}} - % - % Set color, and create a mark which defines \thiscolor accordingly, - % so that \makeheadline knows which color to restore. - \def\setcolor#1{% - \xdef\lastcolordefs{\gdef\noexpand\thiscolor{#1}}% - \domark - \pdfsetcolor{#1}% - } - % - \def\maincolor{\rgbBlack} - \pdfsetcolor{\maincolor} - \edef\thiscolor{\maincolor} - \def\lastcolordefs{} - % - \def\makefootline{% - \baselineskip24pt - \line{\pdfsetcolor{\maincolor}\the\footline}% - } - % - \def\makeheadline{% - \vbox to 0pt{% - \vskip-22.5pt - \line{% - \vbox to8.5pt{}% - % Extract \thiscolor definition from the marks. - \getcolormarks - % Typeset the headline with \maincolor, then restore the color. - \pdfsetcolor{\maincolor}\the\headline\pdfsetcolor{\thiscolor}% - }% - \vss - }% - \nointerlineskip - } - % - % - \pdfcatalog{/PageMode /UseOutlines} - % - % #1 is image name, #2 width (might be empty/whitespace), #3 height (ditto). - \def\dopdfimage#1#2#3{% - \def\pdfimagewidth{#2}\setbox0 = \hbox{\ignorespaces #2}% - \def\pdfimageheight{#3}\setbox2 = \hbox{\ignorespaces #3}% - % - % pdftex (and the PDF format) support .pdf, .png, .jpg (among - % others). Let's try in that order, PDF first since if - % someone has a scalable image, presumably better to use that than a - % bitmap. - \let\pdfimgext=\empty - \begingroup - \openin 1 #1.pdf \ifeof 1 - \openin 1 #1.PDF \ifeof 1 - \openin 1 #1.png \ifeof 1 - \openin 1 #1.jpg \ifeof 1 - \openin 1 #1.jpeg \ifeof 1 - \openin 1 #1.JPG \ifeof 1 - \errhelp = \nopdfimagehelp - \errmessage{Could not find image file #1 for pdf}% - \else \gdef\pdfimgext{JPG}% - \fi - \else \gdef\pdfimgext{jpeg}% - \fi - \else \gdef\pdfimgext{jpg}% - \fi - \else \gdef\pdfimgext{png}% - \fi - \else \gdef\pdfimgext{PDF}% - \fi - \else \gdef\pdfimgext{pdf}% - \fi - \closein 1 - \endgroup - % - % without \immediate, ancient pdftex seg faults when the same image is - % included twice. (Version 3.14159-pre-1.0-unofficial-20010704.) - \ifnum\pdftexversion < 14 - \immediate\pdfimage - \else - \immediate\pdfximage - \fi - \ifdim \wd0 >0pt width \pdfimagewidth \fi - \ifdim \wd2 >0pt height \pdfimageheight \fi - \ifnum\pdftexversion<13 - #1.\pdfimgext - \else - {#1.\pdfimgext}% - \fi - \ifnum\pdftexversion < 14 \else - \pdfrefximage \pdflastximage - \fi} - % - \def\pdfmkdest#1{{% - % We have to set dummies so commands such as @code, and characters - % such as \, aren't expanded when present in a section title. - \indexnofonts - \turnoffactive - \makevalueexpandable - \def\pdfdestname{#1}% - \txiescapepdf\pdfdestname - \safewhatsit{\pdfdest name{\pdfdestname} xyz}% - }} - % - % used to mark target names; must be expandable. - \def\pdfmkpgn#1{#1} - % - % by default, use a color that is dark enough to print on paper as - % nearly black, but still distinguishable for online viewing. - \def\urlcolor{\rgbDarkRed} - \def\linkcolor{\rgbDarkRed} - \def\endlink{\setcolor{\maincolor}\pdfendlink} - % - % Adding outlines to PDF; macros for calculating structure of outlines - % come from Petr Olsak - \def\expnumber#1{\expandafter\ifx\csname#1\endcsname\relax 0% - \else \csname#1\endcsname \fi} - \def\advancenumber#1{\tempnum=\expnumber{#1}\relax - \advance\tempnum by 1 - \expandafter\xdef\csname#1\endcsname{\the\tempnum}} - % - % #1 is the section text, which is what will be displayed in the - % outline by the pdf viewer. #2 is the pdf expression for the number - % of subentries (or empty, for subsubsections). #3 is the node text, - % which might be empty if this toc entry had no corresponding node. - % #4 is the page number - % - \def\dopdfoutline#1#2#3#4{% - % Generate a link to the node text if that exists; else, use the - % page number. We could generate a destination for the section - % text in the case where a section has no node, but it doesn't - % seem worth the trouble, since most documents are normally structured. - \edef\pdfoutlinedest{#3}% - \ifx\pdfoutlinedest\empty - \def\pdfoutlinedest{#4}% - \else - \txiescapepdf\pdfoutlinedest - \fi - % - % Also escape PDF chars in the display string. - \edef\pdfoutlinetext{#1}% - \txiescapepdf\pdfoutlinetext - % - \pdfoutline goto name{\pdfmkpgn{\pdfoutlinedest}}#2{\pdfoutlinetext}% - } - % - \def\pdfmakeoutlines{% - \begingroup - % Read toc silently, to get counts of subentries for \pdfoutline. - \def\partentry##1##2##3##4{}% ignore parts in the outlines - \def\numchapentry##1##2##3##4{% - \def\thischapnum{##2}% - \def\thissecnum{0}% - \def\thissubsecnum{0}% - }% - \def\numsecentry##1##2##3##4{% - \advancenumber{chap\thischapnum}% - \def\thissecnum{##2}% - \def\thissubsecnum{0}% - }% - \def\numsubsecentry##1##2##3##4{% - \advancenumber{sec\thissecnum}% - \def\thissubsecnum{##2}% - }% - \def\numsubsubsecentry##1##2##3##4{% - \advancenumber{subsec\thissubsecnum}% - }% - \def\thischapnum{0}% - \def\thissecnum{0}% - \def\thissubsecnum{0}% - % - % use \def rather than \let here because we redefine \chapentry et - % al. a second time, below. - \def\appentry{\numchapentry}% - \def\appsecentry{\numsecentry}% - \def\appsubsecentry{\numsubsecentry}% - \def\appsubsubsecentry{\numsubsubsecentry}% - \def\unnchapentry{\numchapentry}% - \def\unnsecentry{\numsecentry}% - \def\unnsubsecentry{\numsubsecentry}% - \def\unnsubsubsecentry{\numsubsubsecentry}% - \readdatafile{toc}% - % - % Read toc second time, this time actually producing the outlines. - % The `-' means take the \expnumber as the absolute number of - % subentries, which we calculated on our first read of the .toc above. - % - % We use the node names as the destinations. - \def\numchapentry##1##2##3##4{% - \dopdfoutline{##1}{count-\expnumber{chap##2}}{##3}{##4}}% - \def\numsecentry##1##2##3##4{% - \dopdfoutline{##1}{count-\expnumber{sec##2}}{##3}{##4}}% - \def\numsubsecentry##1##2##3##4{% - \dopdfoutline{##1}{count-\expnumber{subsec##2}}{##3}{##4}}% - \def\numsubsubsecentry##1##2##3##4{% count is always zero - \dopdfoutline{##1}{}{##3}{##4}}% - % - % PDF outlines are displayed using system fonts, instead of - % document fonts. Therefore we cannot use special characters, - % since the encoding is unknown. For example, the eogonek from - % Latin 2 (0xea) gets translated to a | character. Info from - % Staszek Wawrykiewicz, 19 Jan 2004 04:09:24 +0100. - % - % TODO this right, we have to translate 8-bit characters to - % their "best" equivalent, based on the @documentencoding. Too - % much work for too little return. Just use the ASCII equivalents - % we use for the index sort strings. - % - \indexnofonts - \setupdatafile - % We can have normal brace characters in the PDF outlines, unlike - % Texinfo index files. So set that up. - \def\{{\lbracecharliteral}% - \def\}{\rbracecharliteral}% - \catcode`\\=\active \otherbackslash - \input \tocreadfilename - \endgroup - } - {\catcode`[=1 \catcode`]=2 - \catcode`{=\other \catcode`}=\other - \gdef\lbracecharliteral[{]% - \gdef\rbracecharliteral[}]% - ] - % - \def\skipspaces#1{\def\PP{#1}\def\D{|}% - \ifx\PP\D\let\nextsp\relax - \else\let\nextsp\skipspaces - \ifx\p\space\else\addtokens{\filename}{\PP}% - \advance\filenamelength by 1 - \fi - \fi - \nextsp} - \def\getfilename#1{% - \filenamelength=0 - % If we don't expand the argument now, \skipspaces will get - % snagged on things like "@value{foo}". - \edef\temp{#1}% - \expandafter\skipspaces\temp|\relax - } - \ifnum\pdftexversion < 14 - \let \startlink \pdfannotlink - \else - \let \startlink \pdfstartlink - \fi - % make a live url in pdf output. - \def\pdfurl#1{% - \begingroup - % it seems we really need yet another set of dummies; have not - % tried to figure out what each command should do in the context - % of @url. for now, just make @/ a no-op, that's the only one - % people have actually reported a problem with. - % - \normalturnoffactive - \def\@{@}% - \let\/=\empty - \makevalueexpandable - % do we want to go so far as to use \indexnofonts instead of just - % special-casing \var here? - \def\var##1{##1}% - % - \leavevmode\setcolor{\urlcolor}% - \startlink attr{/Border [0 0 0]}% - user{/Subtype /Link /A << /S /URI /URI (#1) >>}% - \endgroup} - \def\pdfgettoks#1.{\setbox\boxA=\hbox{\toksA={#1.}\toksB={}\maketoks}} - \def\addtokens#1#2{\edef\addtoks{\noexpand#1={\the#1#2}}\addtoks} - \def\adn#1{\addtokens{\toksC}{#1}\global\countA=1\let\next=\maketoks} - \def\poptoks#1#2|ENDTOKS|{\let\first=#1\toksD={#1}\toksA={#2}} - \def\maketoks{% - \expandafter\poptoks\the\toksA|ENDTOKS|\relax - \ifx\first0\adn0 - \else\ifx\first1\adn1 \else\ifx\first2\adn2 \else\ifx\first3\adn3 - \else\ifx\first4\adn4 \else\ifx\first5\adn5 \else\ifx\first6\adn6 - \else\ifx\first7\adn7 \else\ifx\first8\adn8 \else\ifx\first9\adn9 - \else - \ifnum0=\countA\else\makelink\fi - \ifx\first.\let\next=\done\else - \let\next=\maketoks - \addtokens{\toksB}{\the\toksD} - \ifx\first,\addtokens{\toksB}{\space}\fi - \fi - \fi\fi\fi\fi\fi\fi\fi\fi\fi\fi - \next} - \def\makelink{\addtokens{\toksB}% - {\noexpand\pdflink{\the\toksC}}\toksC={}\global\countA=0} - \def\pdflink#1{% - \startlink attr{/Border [0 0 0]} goto name{\pdfmkpgn{#1}} - \setcolor{\linkcolor}#1\endlink} - \def\done{\edef\st{\global\noexpand\toksA={\the\toksB}}\st} -\else - % non-pdf mode - \let\pdfmkdest = \gobble - \let\pdfurl = \gobble - \let\endlink = \relax - \let\setcolor = \gobble - \let\pdfsetcolor = \gobble - \let\pdfmakeoutlines = \relax -\fi % \ifx\pdfoutput - - -\message{fonts,} - -% Change the current font style to #1, remembering it in \curfontstyle. -% For now, we do not accumulate font styles: @b{@i{foo}} prints foo in -% italics, not bold italics. -% -\def\setfontstyle#1{% - \def\curfontstyle{#1}% not as a control sequence, because we are \edef'd. - \csname ten#1\endcsname % change the current font -} - -% Select #1 fonts with the current style. -% -\def\selectfonts#1{\csname #1fonts\endcsname \csname\curfontstyle\endcsname} - -\def\rm{\fam=0 \setfontstyle{rm}} -\def\it{\fam=\itfam \setfontstyle{it}} -\def\sl{\fam=\slfam \setfontstyle{sl}} -\def\bf{\fam=\bffam \setfontstyle{bf}}\def\bfstylename{bf} -\def\tt{\fam=\ttfam \setfontstyle{tt}} - -% Unfortunately, we have to override this for titles and the like, since -% in those cases "rm" is bold. Sigh. -\def\rmisbold{\rm\def\curfontstyle{bf}} - -% Texinfo sort of supports the sans serif font style, which plain TeX does not. -% So we set up a \sf. -\newfam\sffam -\def\sf{\fam=\sffam \setfontstyle{sf}} -\let\li = \sf % Sometimes we call it \li, not \sf. - -% We don't need math for this font style. -\def\ttsl{\setfontstyle{ttsl}} - - -% Default leading. -\newdimen\textleading \textleading = 13.2pt - -% Set the baselineskip to #1, and the lineskip and strut size -% correspondingly. There is no deep meaning behind these magic numbers -% used as factors; they just match (closely enough) what Knuth defined. -% -\def\lineskipfactor{.08333} -\def\strutheightpercent{.70833} -\def\strutdepthpercent {.29167} -% -% can get a sort of poor man's double spacing by redefining this. -\def\baselinefactor{1} -% -\def\setleading#1{% - \dimen0 = #1\relax - \normalbaselineskip = \baselinefactor\dimen0 - \normallineskip = \lineskipfactor\normalbaselineskip - \normalbaselines - \setbox\strutbox =\hbox{% - \vrule width0pt height\strutheightpercent\baselineskip - depth \strutdepthpercent \baselineskip - }% -} - -% PDF CMaps. See also LaTeX's t1.cmap. -% -% do nothing with this by default. -\expandafter\let\csname cmapOT1\endcsname\gobble -\expandafter\let\csname cmapOT1IT\endcsname\gobble -\expandafter\let\csname cmapOT1TT\endcsname\gobble - -% if we are producing pdf, and we have \pdffontattr, then define cmaps. -% (\pdffontattr was introduced many years ago, but people still run -% older pdftex's; it's easy to conditionalize, so we do.) -\ifpdf \ifx\pdffontattr\thisisundefined \else - \begingroup - \catcode`\^^M=\active \def^^M{^^J}% Output line endings as the ^^J char. - \catcode`\%=12 \immediate\pdfobj stream {%!PS-Adobe-3.0 Resource-CMap -%%DocumentNeededResources: ProcSet (CIDInit) -%%IncludeResource: ProcSet (CIDInit) -%%BeginResource: CMap (TeX-OT1-0) -%%Title: (TeX-OT1-0 TeX OT1 0) -%%Version: 1.000 -%%EndComments -/CIDInit /ProcSet findresource begin -12 dict begin -begincmap -/CIDSystemInfo -<< /Registry (TeX) -/Ordering (OT1) -/Supplement 0 ->> def -/CMapName /TeX-OT1-0 def -/CMapType 2 def -1 begincodespacerange -<00> <7F> -endcodespacerange -8 beginbfrange -<00> <01> <0393> -<09> <0A> <03A8> -<23> <26> <0023> -<28> <3B> <0028> -<3F> <5B> <003F> -<5D> <5E> <005D> -<61> <7A> <0061> -<7B> <7C> <2013> -endbfrange -40 beginbfchar -<02> <0398> -<03> <039B> -<04> <039E> -<05> <03A0> -<06> <03A3> -<07> <03D2> -<08> <03A6> -<0B> <00660066> -<0C> <00660069> -<0D> <0066006C> -<0E> <006600660069> -<0F> <00660066006C> -<10> <0131> -<11> <0237> -<12> <0060> -<13> <00B4> -<14> <02C7> -<15> <02D8> -<16> <00AF> -<17> <02DA> -<18> <00B8> -<19> <00DF> -<1A> <00E6> -<1B> <0153> -<1C> <00F8> -<1D> <00C6> -<1E> <0152> -<1F> <00D8> -<21> <0021> -<22> <201D> -<27> <2019> -<3C> <00A1> -<3D> <003D> -<3E> <00BF> -<5C> <201C> -<5F> <02D9> -<60> <2018> -<7D> <02DD> -<7E> <007E> -<7F> <00A8> -endbfchar -endcmap -CMapName currentdict /CMap defineresource pop -end -end -%%EndResource -%%EOF - }\endgroup - \expandafter\edef\csname cmapOT1\endcsname#1{% - \pdffontattr#1{/ToUnicode \the\pdflastobj\space 0 R}% - }% -% -% \cmapOT1IT - \begingroup - \catcode`\^^M=\active \def^^M{^^J}% Output line endings as the ^^J char. - \catcode`\%=12 \immediate\pdfobj stream {%!PS-Adobe-3.0 Resource-CMap -%%DocumentNeededResources: ProcSet (CIDInit) -%%IncludeResource: ProcSet (CIDInit) -%%BeginResource: CMap (TeX-OT1IT-0) -%%Title: (TeX-OT1IT-0 TeX OT1IT 0) -%%Version: 1.000 -%%EndComments -/CIDInit /ProcSet findresource begin -12 dict begin -begincmap -/CIDSystemInfo -<< /Registry (TeX) -/Ordering (OT1IT) -/Supplement 0 ->> def -/CMapName /TeX-OT1IT-0 def -/CMapType 2 def -1 begincodespacerange -<00> <7F> -endcodespacerange -8 beginbfrange -<00> <01> <0393> -<09> <0A> <03A8> -<25> <26> <0025> -<28> <3B> <0028> -<3F> <5B> <003F> -<5D> <5E> <005D> -<61> <7A> <0061> -<7B> <7C> <2013> -endbfrange -42 beginbfchar -<02> <0398> -<03> <039B> -<04> <039E> -<05> <03A0> -<06> <03A3> -<07> <03D2> -<08> <03A6> -<0B> <00660066> -<0C> <00660069> -<0D> <0066006C> -<0E> <006600660069> -<0F> <00660066006C> -<10> <0131> -<11> <0237> -<12> <0060> -<13> <00B4> -<14> <02C7> -<15> <02D8> -<16> <00AF> -<17> <02DA> -<18> <00B8> -<19> <00DF> -<1A> <00E6> -<1B> <0153> -<1C> <00F8> -<1D> <00C6> -<1E> <0152> -<1F> <00D8> -<21> <0021> -<22> <201D> -<23> <0023> -<24> <00A3> -<27> <2019> -<3C> <00A1> -<3D> <003D> -<3E> <00BF> -<5C> <201C> -<5F> <02D9> -<60> <2018> -<7D> <02DD> -<7E> <007E> -<7F> <00A8> -endbfchar -endcmap -CMapName currentdict /CMap defineresource pop -end -end -%%EndResource -%%EOF - }\endgroup - \expandafter\edef\csname cmapOT1IT\endcsname#1{% - \pdffontattr#1{/ToUnicode \the\pdflastobj\space 0 R}% - }% -% -% \cmapOT1TT - \begingroup - \catcode`\^^M=\active \def^^M{^^J}% Output line endings as the ^^J char. - \catcode`\%=12 \immediate\pdfobj stream {%!PS-Adobe-3.0 Resource-CMap -%%DocumentNeededResources: ProcSet (CIDInit) -%%IncludeResource: ProcSet (CIDInit) -%%BeginResource: CMap (TeX-OT1TT-0) -%%Title: (TeX-OT1TT-0 TeX OT1TT 0) -%%Version: 1.000 -%%EndComments -/CIDInit /ProcSet findresource begin -12 dict begin -begincmap -/CIDSystemInfo -<< /Registry (TeX) -/Ordering (OT1TT) -/Supplement 0 ->> def -/CMapName /TeX-OT1TT-0 def -/CMapType 2 def -1 begincodespacerange -<00> <7F> -endcodespacerange -5 beginbfrange -<00> <01> <0393> -<09> <0A> <03A8> -<21> <26> <0021> -<28> <5F> <0028> -<61> <7E> <0061> -endbfrange -32 beginbfchar -<02> <0398> -<03> <039B> -<04> <039E> -<05> <03A0> -<06> <03A3> -<07> <03D2> -<08> <03A6> -<0B> <2191> -<0C> <2193> -<0D> <0027> -<0E> <00A1> -<0F> <00BF> -<10> <0131> -<11> <0237> -<12> <0060> -<13> <00B4> -<14> <02C7> -<15> <02D8> -<16> <00AF> -<17> <02DA> -<18> <00B8> -<19> <00DF> -<1A> <00E6> -<1B> <0153> -<1C> <00F8> -<1D> <00C6> -<1E> <0152> -<1F> <00D8> -<20> <2423> -<27> <2019> -<60> <2018> -<7F> <00A8> -endbfchar -endcmap -CMapName currentdict /CMap defineresource pop -end -end -%%EndResource -%%EOF - }\endgroup - \expandafter\edef\csname cmapOT1TT\endcsname#1{% - \pdffontattr#1{/ToUnicode \the\pdflastobj\space 0 R}% - }% -\fi\fi - - -% Set the font macro #1 to the font named #2, adding on the -% specified font prefix (normally `cm'). -% #3 is the font's design size, #4 is a scale factor, #5 is the CMap -% encoding (currently only OT1, OT1IT and OT1TT are allowed, pass -% empty to omit). -\def\setfont#1#2#3#4#5{% - \font#1=\fontprefix#2#3 scaled #4 - \csname cmap#5\endcsname#1% -} -% This is what gets called when #5 of \setfont is empty. -\let\cmap\gobble -% emacs-page end of cmaps - -% Use cm as the default font prefix. -% To specify the font prefix, you must define \fontprefix -% before you read in texinfo.tex. -\ifx\fontprefix\thisisundefined -\def\fontprefix{cm} -\fi -% Support font families that don't use the same naming scheme as CM. -\def\rmshape{r} -\def\rmbshape{bx} %where the normal face is bold -\def\bfshape{b} -\def\bxshape{bx} -\def\ttshape{tt} -\def\ttbshape{tt} -\def\ttslshape{sltt} -\def\itshape{ti} -\def\itbshape{bxti} -\def\slshape{sl} -\def\slbshape{bxsl} -\def\sfshape{ss} -\def\sfbshape{ss} -\def\scshape{csc} -\def\scbshape{csc} - -% Definitions for a main text size of 11pt. This is the default in -% Texinfo. -% -\def\definetextfontsizexi{% -% Text fonts (11.2pt, magstep1). -\def\textnominalsize{11pt} -\edef\mainmagstep{\magstephalf} -\setfont\textrm\rmshape{10}{\mainmagstep}{OT1} -\setfont\texttt\ttshape{10}{\mainmagstep}{OT1TT} -\setfont\textbf\bfshape{10}{\mainmagstep}{OT1} -\setfont\textit\itshape{10}{\mainmagstep}{OT1IT} -\setfont\textsl\slshape{10}{\mainmagstep}{OT1} -\setfont\textsf\sfshape{10}{\mainmagstep}{OT1} -\setfont\textsc\scshape{10}{\mainmagstep}{OT1} -\setfont\textttsl\ttslshape{10}{\mainmagstep}{OT1TT} -\font\texti=cmmi10 scaled \mainmagstep -\font\textsy=cmsy10 scaled \mainmagstep -\def\textecsize{1095} - -% A few fonts for @defun names and args. -\setfont\defbf\bfshape{10}{\magstep1}{OT1} -\setfont\deftt\ttshape{10}{\magstep1}{OT1TT} -\setfont\defttsl\ttslshape{10}{\magstep1}{OT1TT} -\def\df{\let\tentt=\deftt \let\tenbf = \defbf \let\tenttsl=\defttsl \bf} - -% Fonts for indices, footnotes, small examples (9pt). -\def\smallnominalsize{9pt} -\setfont\smallrm\rmshape{9}{1000}{OT1} -\setfont\smalltt\ttshape{9}{1000}{OT1TT} -\setfont\smallbf\bfshape{10}{900}{OT1} -\setfont\smallit\itshape{9}{1000}{OT1IT} -\setfont\smallsl\slshape{9}{1000}{OT1} -\setfont\smallsf\sfshape{9}{1000}{OT1} -\setfont\smallsc\scshape{10}{900}{OT1} -\setfont\smallttsl\ttslshape{10}{900}{OT1TT} -\font\smalli=cmmi9 -\font\smallsy=cmsy9 -\def\smallecsize{0900} - -% Fonts for small examples (8pt). -\def\smallernominalsize{8pt} -\setfont\smallerrm\rmshape{8}{1000}{OT1} -\setfont\smallertt\ttshape{8}{1000}{OT1TT} -\setfont\smallerbf\bfshape{10}{800}{OT1} -\setfont\smallerit\itshape{8}{1000}{OT1IT} -\setfont\smallersl\slshape{8}{1000}{OT1} -\setfont\smallersf\sfshape{8}{1000}{OT1} -\setfont\smallersc\scshape{10}{800}{OT1} -\setfont\smallerttsl\ttslshape{10}{800}{OT1TT} -\font\smalleri=cmmi8 -\font\smallersy=cmsy8 -\def\smallerecsize{0800} - -% Fonts for title page (20.4pt): -\def\titlenominalsize{20pt} -\setfont\titlerm\rmbshape{12}{\magstep3}{OT1} -\setfont\titleit\itbshape{10}{\magstep4}{OT1IT} -\setfont\titlesl\slbshape{10}{\magstep4}{OT1} -\setfont\titlett\ttbshape{12}{\magstep3}{OT1TT} -\setfont\titlettsl\ttslshape{10}{\magstep4}{OT1TT} -\setfont\titlesf\sfbshape{17}{\magstep1}{OT1} -\let\titlebf=\titlerm -\setfont\titlesc\scbshape{10}{\magstep4}{OT1} -\font\titlei=cmmi12 scaled \magstep3 -\font\titlesy=cmsy10 scaled \magstep4 -\def\titleecsize{2074} - -% Chapter (and unnumbered) fonts (17.28pt). -\def\chapnominalsize{17pt} -\setfont\chaprm\rmbshape{12}{\magstep2}{OT1} -\setfont\chapit\itbshape{10}{\magstep3}{OT1IT} -\setfont\chapsl\slbshape{10}{\magstep3}{OT1} -\setfont\chaptt\ttbshape{12}{\magstep2}{OT1TT} -\setfont\chapttsl\ttslshape{10}{\magstep3}{OT1TT} -\setfont\chapsf\sfbshape{17}{1000}{OT1} -\let\chapbf=\chaprm -\setfont\chapsc\scbshape{10}{\magstep3}{OT1} -\font\chapi=cmmi12 scaled \magstep2 -\font\chapsy=cmsy10 scaled \magstep3 -\def\chapecsize{1728} - -% Section fonts (14.4pt). -\def\secnominalsize{14pt} -\setfont\secrm\rmbshape{12}{\magstep1}{OT1} -\setfont\secit\itbshape{10}{\magstep2}{OT1IT} -\setfont\secsl\slbshape{10}{\magstep2}{OT1} -\setfont\sectt\ttbshape{12}{\magstep1}{OT1TT} -\setfont\secttsl\ttslshape{10}{\magstep2}{OT1TT} -\setfont\secsf\sfbshape{12}{\magstep1}{OT1} -\let\secbf\secrm -\setfont\secsc\scbshape{10}{\magstep2}{OT1} -\font\seci=cmmi12 scaled \magstep1 -\font\secsy=cmsy10 scaled \magstep2 -\def\sececsize{1440} - -% Subsection fonts (13.15pt). -\def\ssecnominalsize{13pt} -\setfont\ssecrm\rmbshape{12}{\magstephalf}{OT1} -\setfont\ssecit\itbshape{10}{1315}{OT1IT} -\setfont\ssecsl\slbshape{10}{1315}{OT1} -\setfont\ssectt\ttbshape{12}{\magstephalf}{OT1TT} -\setfont\ssecttsl\ttslshape{10}{1315}{OT1TT} -\setfont\ssecsf\sfbshape{12}{\magstephalf}{OT1} -\let\ssecbf\ssecrm -\setfont\ssecsc\scbshape{10}{1315}{OT1} -\font\sseci=cmmi12 scaled \magstephalf -\font\ssecsy=cmsy10 scaled 1315 -\def\ssececsize{1200} - -% Reduced fonts for @acro in text (10pt). -\def\reducednominalsize{10pt} -\setfont\reducedrm\rmshape{10}{1000}{OT1} -\setfont\reducedtt\ttshape{10}{1000}{OT1TT} -\setfont\reducedbf\bfshape{10}{1000}{OT1} -\setfont\reducedit\itshape{10}{1000}{OT1IT} -\setfont\reducedsl\slshape{10}{1000}{OT1} -\setfont\reducedsf\sfshape{10}{1000}{OT1} -\setfont\reducedsc\scshape{10}{1000}{OT1} -\setfont\reducedttsl\ttslshape{10}{1000}{OT1TT} -\font\reducedi=cmmi10 -\font\reducedsy=cmsy10 -\def\reducedecsize{1000} - -\textleading = 13.2pt % line spacing for 11pt CM -\textfonts % reset the current fonts -\rm -} % end of 11pt text font size definitions - - -% Definitions to make the main text be 10pt Computer Modern, with -% section, chapter, etc., sizes following suit. This is for the GNU -% Press printing of the Emacs 22 manual. Maybe other manuals in the -% future. Used with @smallbook, which sets the leading to 12pt. -% -\def\definetextfontsizex{% -% Text fonts (10pt). -\def\textnominalsize{10pt} -\edef\mainmagstep{1000} -\setfont\textrm\rmshape{10}{\mainmagstep}{OT1} -\setfont\texttt\ttshape{10}{\mainmagstep}{OT1TT} -\setfont\textbf\bfshape{10}{\mainmagstep}{OT1} -\setfont\textit\itshape{10}{\mainmagstep}{OT1IT} -\setfont\textsl\slshape{10}{\mainmagstep}{OT1} -\setfont\textsf\sfshape{10}{\mainmagstep}{OT1} -\setfont\textsc\scshape{10}{\mainmagstep}{OT1} -\setfont\textttsl\ttslshape{10}{\mainmagstep}{OT1TT} -\font\texti=cmmi10 scaled \mainmagstep -\font\textsy=cmsy10 scaled \mainmagstep -\def\textecsize{1000} - -% A few fonts for @defun names and args. -\setfont\defbf\bfshape{10}{\magstephalf}{OT1} -\setfont\deftt\ttshape{10}{\magstephalf}{OT1TT} -\setfont\defttsl\ttslshape{10}{\magstephalf}{OT1TT} -\def\df{\let\tentt=\deftt \let\tenbf = \defbf \let\tenttsl=\defttsl \bf} - -% Fonts for indices, footnotes, small examples (9pt). -\def\smallnominalsize{9pt} -\setfont\smallrm\rmshape{9}{1000}{OT1} -\setfont\smalltt\ttshape{9}{1000}{OT1TT} -\setfont\smallbf\bfshape{10}{900}{OT1} -\setfont\smallit\itshape{9}{1000}{OT1IT} -\setfont\smallsl\slshape{9}{1000}{OT1} -\setfont\smallsf\sfshape{9}{1000}{OT1} -\setfont\smallsc\scshape{10}{900}{OT1} -\setfont\smallttsl\ttslshape{10}{900}{OT1TT} -\font\smalli=cmmi9 -\font\smallsy=cmsy9 -\def\smallecsize{0900} - -% Fonts for small examples (8pt). -\def\smallernominalsize{8pt} -\setfont\smallerrm\rmshape{8}{1000}{OT1} -\setfont\smallertt\ttshape{8}{1000}{OT1TT} -\setfont\smallerbf\bfshape{10}{800}{OT1} -\setfont\smallerit\itshape{8}{1000}{OT1IT} -\setfont\smallersl\slshape{8}{1000}{OT1} -\setfont\smallersf\sfshape{8}{1000}{OT1} -\setfont\smallersc\scshape{10}{800}{OT1} -\setfont\smallerttsl\ttslshape{10}{800}{OT1TT} -\font\smalleri=cmmi8 -\font\smallersy=cmsy8 -\def\smallerecsize{0800} - -% Fonts for title page (20.4pt): -\def\titlenominalsize{20pt} -\setfont\titlerm\rmbshape{12}{\magstep3}{OT1} -\setfont\titleit\itbshape{10}{\magstep4}{OT1IT} -\setfont\titlesl\slbshape{10}{\magstep4}{OT1} -\setfont\titlett\ttbshape{12}{\magstep3}{OT1TT} -\setfont\titlettsl\ttslshape{10}{\magstep4}{OT1TT} -\setfont\titlesf\sfbshape{17}{\magstep1}{OT1} -\let\titlebf=\titlerm -\setfont\titlesc\scbshape{10}{\magstep4}{OT1} -\font\titlei=cmmi12 scaled \magstep3 -\font\titlesy=cmsy10 scaled \magstep4 -\def\titleecsize{2074} - -% Chapter fonts (14.4pt). -\def\chapnominalsize{14pt} -\setfont\chaprm\rmbshape{12}{\magstep1}{OT1} -\setfont\chapit\itbshape{10}{\magstep2}{OT1IT} -\setfont\chapsl\slbshape{10}{\magstep2}{OT1} -\setfont\chaptt\ttbshape{12}{\magstep1}{OT1TT} -\setfont\chapttsl\ttslshape{10}{\magstep2}{OT1TT} -\setfont\chapsf\sfbshape{12}{\magstep1}{OT1} -\let\chapbf\chaprm -\setfont\chapsc\scbshape{10}{\magstep2}{OT1} -\font\chapi=cmmi12 scaled \magstep1 -\font\chapsy=cmsy10 scaled \magstep2 -\def\chapecsize{1440} - -% Section fonts (12pt). -\def\secnominalsize{12pt} -\setfont\secrm\rmbshape{12}{1000}{OT1} -\setfont\secit\itbshape{10}{\magstep1}{OT1IT} -\setfont\secsl\slbshape{10}{\magstep1}{OT1} -\setfont\sectt\ttbshape{12}{1000}{OT1TT} -\setfont\secttsl\ttslshape{10}{\magstep1}{OT1TT} -\setfont\secsf\sfbshape{12}{1000}{OT1} -\let\secbf\secrm -\setfont\secsc\scbshape{10}{\magstep1}{OT1} -\font\seci=cmmi12 -\font\secsy=cmsy10 scaled \magstep1 -\def\sececsize{1200} - -% Subsection fonts (10pt). -\def\ssecnominalsize{10pt} -\setfont\ssecrm\rmbshape{10}{1000}{OT1} -\setfont\ssecit\itbshape{10}{1000}{OT1IT} -\setfont\ssecsl\slbshape{10}{1000}{OT1} -\setfont\ssectt\ttbshape{10}{1000}{OT1TT} -\setfont\ssecttsl\ttslshape{10}{1000}{OT1TT} -\setfont\ssecsf\sfbshape{10}{1000}{OT1} -\let\ssecbf\ssecrm -\setfont\ssecsc\scbshape{10}{1000}{OT1} -\font\sseci=cmmi10 -\font\ssecsy=cmsy10 -\def\ssececsize{1000} - -% Reduced fonts for @acro in text (9pt). -\def\reducednominalsize{9pt} -\setfont\reducedrm\rmshape{9}{1000}{OT1} -\setfont\reducedtt\ttshape{9}{1000}{OT1TT} -\setfont\reducedbf\bfshape{10}{900}{OT1} -\setfont\reducedit\itshape{9}{1000}{OT1IT} -\setfont\reducedsl\slshape{9}{1000}{OT1} -\setfont\reducedsf\sfshape{9}{1000}{OT1} -\setfont\reducedsc\scshape{10}{900}{OT1} -\setfont\reducedttsl\ttslshape{10}{900}{OT1TT} -\font\reducedi=cmmi9 -\font\reducedsy=cmsy9 -\def\reducedecsize{0900} - -\divide\parskip by 2 % reduce space between paragraphs -\textleading = 12pt % line spacing for 10pt CM -\textfonts % reset the current fonts -\rm -} % end of 10pt text font size definitions - - -% We provide the user-level command -% @fonttextsize 10 -% (or 11) to redefine the text font size. pt is assumed. -% -\def\xiword{11} -\def\xword{10} -\def\xwordpt{10pt} -% -\parseargdef\fonttextsize{% - \def\textsizearg{#1}% - %\wlog{doing @fonttextsize \textsizearg}% - % - % Set \globaldefs so that documents can use this inside @tex, since - % makeinfo 4.8 does not support it, but we need it nonetheless. - % - \begingroup \globaldefs=1 - \ifx\textsizearg\xword \definetextfontsizex - \else \ifx\textsizearg\xiword \definetextfontsizexi - \else - \errhelp=\EMsimple - \errmessage{@fonttextsize only supports `10' or `11', not `\textsizearg'} - \fi\fi - \endgroup -} - - -% In order for the font changes to affect most math symbols and letters, -% we have to define the \textfont of the standard families. Since -% texinfo doesn't allow for producing subscripts and superscripts except -% in the main text, we don't bother to reset \scriptfont and -% \scriptscriptfont (which would also require loading a lot more fonts). -% -\def\resetmathfonts{% - \textfont0=\tenrm \textfont1=\teni \textfont2=\tensy - \textfont\itfam=\tenit \textfont\slfam=\tensl \textfont\bffam=\tenbf - \textfont\ttfam=\tentt \textfont\sffam=\tensf -} - -% The font-changing commands redefine the meanings of \tenSTYLE, instead -% of just \STYLE. We do this because \STYLE needs to also set the -% current \fam for math mode. Our \STYLE (e.g., \rm) commands hardwire -% \tenSTYLE to set the current font. -% -% Each font-changing command also sets the names \lsize (one size lower) -% and \lllsize (three sizes lower). These relative commands are used in -% the LaTeX logo and acronyms. -% -% This all needs generalizing, badly. -% -\def\textfonts{% - \let\tenrm=\textrm \let\tenit=\textit \let\tensl=\textsl - \let\tenbf=\textbf \let\tentt=\texttt \let\smallcaps=\textsc - \let\tensf=\textsf \let\teni=\texti \let\tensy=\textsy - \let\tenttsl=\textttsl - \def\curfontsize{text}% - \def\lsize{reduced}\def\lllsize{smaller}% - \resetmathfonts \setleading{\textleading}} -\def\titlefonts{% - \let\tenrm=\titlerm \let\tenit=\titleit \let\tensl=\titlesl - \let\tenbf=\titlebf \let\tentt=\titlett \let\smallcaps=\titlesc - \let\tensf=\titlesf \let\teni=\titlei \let\tensy=\titlesy - \let\tenttsl=\titlettsl - \def\curfontsize{title}% - \def\lsize{chap}\def\lllsize{subsec}% - \resetmathfonts \setleading{27pt}} -\def\titlefont#1{{\titlefonts\rmisbold #1}} -\def\chapfonts{% - \let\tenrm=\chaprm \let\tenit=\chapit \let\tensl=\chapsl - \let\tenbf=\chapbf \let\tentt=\chaptt \let\smallcaps=\chapsc - \let\tensf=\chapsf \let\teni=\chapi \let\tensy=\chapsy - \let\tenttsl=\chapttsl - \def\curfontsize{chap}% - \def\lsize{sec}\def\lllsize{text}% - \resetmathfonts \setleading{19pt}} -\def\secfonts{% - \let\tenrm=\secrm \let\tenit=\secit \let\tensl=\secsl - \let\tenbf=\secbf \let\tentt=\sectt \let\smallcaps=\secsc - \let\tensf=\secsf \let\teni=\seci \let\tensy=\secsy - \let\tenttsl=\secttsl - \def\curfontsize{sec}% - \def\lsize{subsec}\def\lllsize{reduced}% - \resetmathfonts \setleading{16pt}} -\def\subsecfonts{% - \let\tenrm=\ssecrm \let\tenit=\ssecit \let\tensl=\ssecsl - \let\tenbf=\ssecbf \let\tentt=\ssectt \let\smallcaps=\ssecsc - \let\tensf=\ssecsf \let\teni=\sseci \let\tensy=\ssecsy - \let\tenttsl=\ssecttsl - \def\curfontsize{ssec}% - \def\lsize{text}\def\lllsize{small}% - \resetmathfonts \setleading{15pt}} -\let\subsubsecfonts = \subsecfonts -\def\reducedfonts{% - \let\tenrm=\reducedrm \let\tenit=\reducedit \let\tensl=\reducedsl - \let\tenbf=\reducedbf \let\tentt=\reducedtt \let\reducedcaps=\reducedsc - \let\tensf=\reducedsf \let\teni=\reducedi \let\tensy=\reducedsy - \let\tenttsl=\reducedttsl - \def\curfontsize{reduced}% - \def\lsize{small}\def\lllsize{smaller}% - \resetmathfonts \setleading{10.5pt}} -\def\smallfonts{% - \let\tenrm=\smallrm \let\tenit=\smallit \let\tensl=\smallsl - \let\tenbf=\smallbf \let\tentt=\smalltt \let\smallcaps=\smallsc - \let\tensf=\smallsf \let\teni=\smalli \let\tensy=\smallsy - \let\tenttsl=\smallttsl - \def\curfontsize{small}% - \def\lsize{smaller}\def\lllsize{smaller}% - \resetmathfonts \setleading{10.5pt}} -\def\smallerfonts{% - \let\tenrm=\smallerrm \let\tenit=\smallerit \let\tensl=\smallersl - \let\tenbf=\smallerbf \let\tentt=\smallertt \let\smallcaps=\smallersc - \let\tensf=\smallersf \let\teni=\smalleri \let\tensy=\smallersy - \let\tenttsl=\smallerttsl - \def\curfontsize{smaller}% - \def\lsize{smaller}\def\lllsize{smaller}% - \resetmathfonts \setleading{9.5pt}} - -% Fonts for short table of contents. -\setfont\shortcontrm\rmshape{12}{1000}{OT1} -\setfont\shortcontbf\bfshape{10}{\magstep1}{OT1} % no cmb12 -\setfont\shortcontsl\slshape{12}{1000}{OT1} -\setfont\shortconttt\ttshape{12}{1000}{OT1TT} - -% Define these just so they can be easily changed for other fonts. -\def\angleleft{$\langle$} -\def\angleright{$\rangle$} - -% Set the fonts to use with the @small... environments. -\let\smallexamplefonts = \smallfonts - -% About \smallexamplefonts. If we use \smallfonts (9pt), @smallexample -% can fit this many characters: -% 8.5x11=86 smallbook=72 a4=90 a5=69 -% If we use \scriptfonts (8pt), then we can fit this many characters: -% 8.5x11=90+ smallbook=80 a4=90+ a5=77 -% For me, subjectively, the few extra characters that fit aren't worth -% the additional smallness of 8pt. So I'm making the default 9pt. -% -% By the way, for comparison, here's what fits with @example (10pt): -% 8.5x11=71 smallbook=60 a4=75 a5=58 -% --karl, 24jan03. - -% Set up the default fonts, so we can use them for creating boxes. -% -\definetextfontsizexi - - -\message{markup,} - -% Check if we are currently using a typewriter font. Since all the -% Computer Modern typewriter fonts have zero interword stretch (and -% shrink), and it is reasonable to expect all typewriter fonts to have -% this property, we can check that font parameter. -% -\def\ifmonospace{\ifdim\fontdimen3\font=0pt } - -% Markup style infrastructure. \defmarkupstylesetup\INITMACRO will -% define and register \INITMACRO to be called on markup style changes. -% \INITMACRO can check \currentmarkupstyle for the innermost -% style and the set of \ifmarkupSTYLE switches for all styles -% currently in effect. -\newif\ifmarkupvar -\newif\ifmarkupsamp -\newif\ifmarkupkey -%\newif\ifmarkupfile % @file == @samp. -%\newif\ifmarkupoption % @option == @samp. -\newif\ifmarkupcode -\newif\ifmarkupkbd -%\newif\ifmarkupenv % @env == @code. -%\newif\ifmarkupcommand % @command == @code. -\newif\ifmarkuptex % @tex (and part of @math, for now). -\newif\ifmarkupexample -\newif\ifmarkupverb -\newif\ifmarkupverbatim - -\let\currentmarkupstyle\empty - -\def\setupmarkupstyle#1{% - \csname markup#1true\endcsname - \def\currentmarkupstyle{#1}% - \markupstylesetup -} - -\let\markupstylesetup\empty - -\def\defmarkupstylesetup#1{% - \expandafter\def\expandafter\markupstylesetup - \expandafter{\markupstylesetup #1}% - \def#1% -} - -% Markup style setup for left and right quotes. -\defmarkupstylesetup\markupsetuplq{% - \expandafter\let\expandafter \temp - \csname markupsetuplq\currentmarkupstyle\endcsname - \ifx\temp\relax \markupsetuplqdefault \else \temp \fi -} - -\defmarkupstylesetup\markupsetuprq{% - \expandafter\let\expandafter \temp - \csname markupsetuprq\currentmarkupstyle\endcsname - \ifx\temp\relax \markupsetuprqdefault \else \temp \fi -} - -{ -\catcode`\'=\active -\catcode`\`=\active - -\gdef\markupsetuplqdefault{\let`\lq} -\gdef\markupsetuprqdefault{\let'\rq} - -\gdef\markupsetcodequoteleft{\let`\codequoteleft} -\gdef\markupsetcodequoteright{\let'\codequoteright} - -\gdef\markupsetnoligaturesquoteleft{\let`\noligaturesquoteleft} -} - -\let\markupsetuplqcode \markupsetcodequoteleft -\let\markupsetuprqcode \markupsetcodequoteright -% -\let\markupsetuplqexample \markupsetcodequoteleft -\let\markupsetuprqexample \markupsetcodequoteright -% -\let\markupsetuplqsamp \markupsetcodequoteleft -\let\markupsetuprqsamp \markupsetcodequoteright -% -\let\markupsetuplqverb \markupsetcodequoteleft -\let\markupsetuprqverb \markupsetcodequoteright -% -\let\markupsetuplqverbatim \markupsetcodequoteleft -\let\markupsetuprqverbatim \markupsetcodequoteright - -\let\markupsetuplqkbd \markupsetnoligaturesquoteleft - -% Allow an option to not use regular directed right quote/apostrophe -% (char 0x27), but instead the undirected quote from cmtt (char 0x0d). -% The undirected quote is ugly, so don't make it the default, but it -% works for pasting with more pdf viewers (at least evince), the -% lilypond developers report. xpdf does work with the regular 0x27. -% -\def\codequoteright{% - \expandafter\ifx\csname SETtxicodequoteundirected\endcsname\relax - \expandafter\ifx\csname SETcodequoteundirected\endcsname\relax - '% - \else \char'15 \fi - \else \char'15 \fi -} -% -% and a similar option for the left quote char vs. a grave accent. -% Modern fonts display ASCII 0x60 as a grave accent, so some people like -% the code environments to do likewise. -% -\def\codequoteleft{% - \expandafter\ifx\csname SETtxicodequotebacktick\endcsname\relax - \expandafter\ifx\csname SETcodequotebacktick\endcsname\relax - % [Knuth] pp. 380,381,391 - % \relax disables Spanish ligatures ?` and !` of \tt font. - \relax`% - \else \char'22 \fi - \else \char'22 \fi -} - -% Commands to set the quote options. -% -\parseargdef\codequoteundirected{% - \def\temp{#1}% - \ifx\temp\onword - \expandafter\let\csname SETtxicodequoteundirected\endcsname - = t% - \else\ifx\temp\offword - \expandafter\let\csname SETtxicodequoteundirected\endcsname - = \relax - \else - \errhelp = \EMsimple - \errmessage{Unknown @codequoteundirected value `\temp', must be on|off}% - \fi\fi -} -% -\parseargdef\codequotebacktick{% - \def\temp{#1}% - \ifx\temp\onword - \expandafter\let\csname SETtxicodequotebacktick\endcsname - = t% - \else\ifx\temp\offword - \expandafter\let\csname SETtxicodequotebacktick\endcsname - = \relax - \else - \errhelp = \EMsimple - \errmessage{Unknown @codequotebacktick value `\temp', must be on|off}% - \fi\fi -} - -% [Knuth] pp. 380,381,391, disable Spanish ligatures ?` and !` of \tt font. -\def\noligaturesquoteleft{\relax\lq} - -% Count depth in font-changes, for error checks -\newcount\fontdepth \fontdepth=0 - -% Font commands. - -% #1 is the font command (\sl or \it), #2 is the text to slant. -% If we are in a monospaced environment, however, 1) always use \ttsl, -% and 2) do not add an italic correction. -\def\dosmartslant#1#2{% - \ifusingtt - {{\ttsl #2}\let\next=\relax}% - {\def\next{{#1#2}\futurelet\next\smartitaliccorrection}}% - \next -} -\def\smartslanted{\dosmartslant\sl} -\def\smartitalic{\dosmartslant\it} - -% Output an italic correction unless \next (presumed to be the following -% character) is such as not to need one. -\def\smartitaliccorrection{% - \ifx\next,% - \else\ifx\next-% - \else\ifx\next.% - \else\ptexslash - \fi\fi\fi - \aftersmartic -} - -% like \smartslanted except unconditionally uses \ttsl, and no ic. -% @var is set to this for defun arguments. -\def\ttslanted#1{{\ttsl #1}} - -% @cite is like \smartslanted except unconditionally use \sl. We never want -% ttsl for book titles, do we? -\def\cite#1{{\sl #1}\futurelet\next\smartitaliccorrection} - -\def\aftersmartic{} -\def\var#1{% - \let\saveaftersmartic = \aftersmartic - \def\aftersmartic{\null\let\aftersmartic=\saveaftersmartic}% - \smartslanted{#1}% -} - -\let\i=\smartitalic -\let\slanted=\smartslanted -\let\dfn=\smartslanted -\let\emph=\smartitalic - -% Explicit font changes: @r, @sc, undocumented @ii. -\def\r#1{{\rm #1}} % roman font -\def\sc#1{{\smallcaps#1}} % smallcaps font -\def\ii#1{{\it #1}} % italic font - -% @b, explicit bold. Also @strong. -\def\b#1{{\bf #1}} -\let\strong=\b - -% @sansserif, explicit sans. -\def\sansserif#1{{\sf #1}} - -% We can't just use \exhyphenpenalty, because that only has effect at -% the end of a paragraph. Restore normal hyphenation at the end of the -% group within which \nohyphenation is presumably called. -% -\def\nohyphenation{\hyphenchar\font = -1 \aftergroup\restorehyphenation} -\def\restorehyphenation{\hyphenchar\font = `- } - -% Set sfcode to normal for the chars that usually have another value. -% Can't use plain's \frenchspacing because it uses the `\x notation, and -% sometimes \x has an active definition that messes things up. -% -\catcode`@=11 - \def\plainfrenchspacing{% - \sfcode\dotChar =\@m \sfcode\questChar=\@m \sfcode\exclamChar=\@m - \sfcode\colonChar=\@m \sfcode\semiChar =\@m \sfcode\commaChar =\@m - \def\endofsentencespacefactor{1000}% for @. and friends - } - \def\plainnonfrenchspacing{% - \sfcode`\.3000\sfcode`\?3000\sfcode`\!3000 - \sfcode`\:2000\sfcode`\;1500\sfcode`\,1250 - \def\endofsentencespacefactor{3000}% for @. and friends - } -\catcode`@=\other -\def\endofsentencespacefactor{3000}% default - -% @t, explicit typewriter. -\def\t#1{% - {\tt \rawbackslash \plainfrenchspacing #1}% - \null -} - -% @samp. -\def\samp#1{{\setupmarkupstyle{samp}\lq\tclose{#1}\rq\null}} - -% definition of @key that produces a lozenge. Doesn't adjust to text size. -%\setfont\keyrm\rmshape{8}{1000}{OT1} -%\font\keysy=cmsy9 -%\def\key#1{{\keyrm\textfont2=\keysy \leavevmode\hbox{% -% \raise0.4pt\hbox{\angleleft}\kern-.08em\vtop{% -% \vbox{\hrule\kern-0.4pt -% \hbox{\raise0.4pt\hbox{\vphantom{\angleleft}}#1}}% -% \kern-0.4pt\hrule}% -% \kern-.06em\raise0.4pt\hbox{\angleright}}}} - -% definition of @key with no lozenge. If the current font is already -% monospace, don't change it; that way, we respect @kbdinputstyle. But -% if it isn't monospace, then use \tt. -% -\def\key#1{{\setupmarkupstyle{key}% - \nohyphenation - \ifmonospace\else\tt\fi - #1}\null} - -% ctrl is no longer a Texinfo command. -\def\ctrl #1{{\tt \rawbackslash \hat}#1} - -% @file, @option are the same as @samp. -\let\file=\samp -\let\option=\samp - -% @code is a modification of @t, -% which makes spaces the same size as normal in the surrounding text. -\def\tclose#1{% - {% - % Change normal interword space to be same as for the current font. - \spaceskip = \fontdimen2\font - % - % Switch to typewriter. - \tt - % - % But `\ ' produces the large typewriter interword space. - \def\ {{\spaceskip = 0pt{} }}% - % - % Turn off hyphenation. - \nohyphenation - % - \rawbackslash - \plainfrenchspacing - #1% - }% - \null % reset spacefactor to 1000 -} - -% We *must* turn on hyphenation at `-' and `_' in @code. -% Otherwise, it is too hard to avoid overfull hboxes -% in the Emacs manual, the Library manual, etc. - -% Unfortunately, TeX uses one parameter (\hyphenchar) to control -% both hyphenation at - and hyphenation within words. -% We must therefore turn them both off (\tclose does that) -% and arrange explicitly to hyphenate at a dash. -% -- rms. -{ - \catcode`\-=\active \catcode`\_=\active - \catcode`\'=\active \catcode`\`=\active - \global\let'=\rq \global\let`=\lq % default definitions - % - \global\def\code{\begingroup - \setupmarkupstyle{code}% - % The following should really be moved into \setupmarkupstyle handlers. - \catcode\dashChar=\active \catcode\underChar=\active - \ifallowcodebreaks - \let-\codedash - \let_\codeunder - \else - \let-\realdash - \let_\realunder - \fi - \codex - } -} - -\def\codex #1{\tclose{#1}\endgroup} - -\def\realdash{-} -\def\codedash{-\discretionary{}{}{}} -\def\codeunder{% - % this is all so @math{@code{var_name}+1} can work. In math mode, _ - % is "active" (mathcode"8000) and \normalunderscore (or \char95, etc.) - % will therefore expand the active definition of _, which is us - % (inside @code that is), therefore an endless loop. - \ifusingtt{\ifmmode - \mathchar"075F % class 0=ordinary, family 7=ttfam, pos 0x5F=_. - \else\normalunderscore \fi - \discretionary{}{}{}}% - {\_}% -} - -% An additional complication: the above will allow breaks after, e.g., -% each of the four underscores in __typeof__. This is undesirable in -% some manuals, especially if they don't have long identifiers in -% general. @allowcodebreaks provides a way to control this. -% -\newif\ifallowcodebreaks \allowcodebreakstrue - -\def\keywordtrue{true} -\def\keywordfalse{false} - -\parseargdef\allowcodebreaks{% - \def\txiarg{#1}% - \ifx\txiarg\keywordtrue - \allowcodebreakstrue - \else\ifx\txiarg\keywordfalse - \allowcodebreaksfalse - \else - \errhelp = \EMsimple - \errmessage{Unknown @allowcodebreaks option `\txiarg', must be true|false}% - \fi\fi -} - -% @uref (abbreviation for `urlref') takes an optional (comma-separated) -% second argument specifying the text to display and an optional third -% arg as text to display instead of (rather than in addition to) the url -% itself. First (mandatory) arg is the url. -% (This \urefnobreak definition isn't used now, leaving it for a while -% for comparison.) -\def\urefnobreak#1{\dourefnobreak #1,,,\finish} -\def\dourefnobreak#1,#2,#3,#4\finish{\begingroup - \unsepspaces - \pdfurl{#1}% - \setbox0 = \hbox{\ignorespaces #3}% - \ifdim\wd0 > 0pt - \unhbox0 % third arg given, show only that - \else - \setbox0 = \hbox{\ignorespaces #2}% - \ifdim\wd0 > 0pt - \ifpdf - \unhbox0 % PDF: 2nd arg given, show only it - \else - \unhbox0\ (\code{#1})% DVI: 2nd arg given, show both it and url - \fi - \else - \code{#1}% only url given, so show it - \fi - \fi - \endlink -\endgroup} - -% This \urefbreak definition is the active one. -\def\urefbreak{\begingroup \urefcatcodes \dourefbreak} -\let\uref=\urefbreak -\def\dourefbreak#1{\urefbreakfinish #1,,,\finish} -\def\urefbreakfinish#1,#2,#3,#4\finish{% doesn't work in @example - \unsepspaces - \pdfurl{#1}% - \setbox0 = \hbox{\ignorespaces #3}% - \ifdim\wd0 > 0pt - \unhbox0 % third arg given, show only that - \else - \setbox0 = \hbox{\ignorespaces #2}% - \ifdim\wd0 > 0pt - \ifpdf - \unhbox0 % PDF: 2nd arg given, show only it - \else - \unhbox0\ (\urefcode{#1})% DVI: 2nd arg given, show both it and url - \fi - \else - \urefcode{#1}% only url given, so show it - \fi - \fi - \endlink -\endgroup} - -% Allow line breaks around only a few characters (only). -\def\urefcatcodes{% - \catcode\ampChar=\active \catcode\dotChar=\active - \catcode\hashChar=\active \catcode\questChar=\active - \catcode\slashChar=\active -} -{ - \urefcatcodes - % - \global\def\urefcode{\begingroup - \setupmarkupstyle{code}% - \urefcatcodes - \let&\urefcodeamp - \let.\urefcodedot - \let#\urefcodehash - \let?\urefcodequest - \let/\urefcodeslash - \codex - } - % - % By default, they are just regular characters. - \global\def&{\normalamp} - \global\def.{\normaldot} - \global\def#{\normalhash} - \global\def?{\normalquest} - \global\def/{\normalslash} -} - -% we put a little stretch before and after the breakable chars, to help -% line breaking of long url's. The unequal skips make look better in -% cmtt at least, especially for dots. -\def\urefprestretch{\urefprebreak \hskip0pt plus.13em } -\def\urefpoststretch{\urefpostbreak \hskip0pt plus.1em } -% -\def\urefcodeamp{\urefprestretch \&\urefpoststretch} -\def\urefcodedot{\urefprestretch .\urefpoststretch} -\def\urefcodehash{\urefprestretch \#\urefpoststretch} -\def\urefcodequest{\urefprestretch ?\urefpoststretch} -\def\urefcodeslash{\futurelet\next\urefcodeslashfinish} -{ - \catcode`\/=\active - \global\def\urefcodeslashfinish{% - \urefprestretch \slashChar - % Allow line break only after the final / in a sequence of - % slashes, to avoid line break between the slashes in http://. - \ifx\next/\else \urefpoststretch \fi - } -} - -% One more complication: by default we'll break after the special -% characters, but some people like to break before the special chars, so -% allow that. Also allow no breaking at all, for manual control. -% -\parseargdef\urefbreakstyle{% - \def\txiarg{#1}% - \ifx\txiarg\wordnone - \def\urefprebreak{\nobreak}\def\urefpostbreak{\nobreak} - \else\ifx\txiarg\wordbefore - \def\urefprebreak{\allowbreak}\def\urefpostbreak{\nobreak} - \else\ifx\txiarg\wordafter - \def\urefprebreak{\nobreak}\def\urefpostbreak{\allowbreak} - \else - \errhelp = \EMsimple - \errmessage{Unknown @urefbreakstyle setting `\txiarg'}% - \fi\fi\fi -} -\def\wordafter{after} -\def\wordbefore{before} -\def\wordnone{none} - -\urefbreakstyle after - -% @url synonym for @uref, since that's how everyone uses it. -% -\let\url=\uref - -% rms does not like angle brackets --karl, 17may97. -% So now @email is just like @uref, unless we are pdf. -% -%\def\email#1{\angleleft{\tt #1}\angleright} -\ifpdf - \def\email#1{\doemail#1,,\finish} - \def\doemail#1,#2,#3\finish{\begingroup - \unsepspaces - \pdfurl{mailto:#1}% - \setbox0 = \hbox{\ignorespaces #2}% - \ifdim\wd0>0pt\unhbox0\else\code{#1}\fi - \endlink - \endgroup} -\else - \let\email=\uref -\fi - -% @kbd is like @code, except that if the argument is just one @key command, -% then @kbd has no effect. -\def\kbd#1{{\setupmarkupstyle{kbd}\def\look{#1}\expandafter\kbdfoo\look??\par}} - -% @kbdinputstyle -- arg is `distinct' (@kbd uses slanted tty font always), -% `example' (@kbd uses ttsl only inside of @example and friends), -% or `code' (@kbd uses normal tty font always). -\parseargdef\kbdinputstyle{% - \def\txiarg{#1}% - \ifx\txiarg\worddistinct - \gdef\kbdexamplefont{\ttsl}\gdef\kbdfont{\ttsl}% - \else\ifx\txiarg\wordexample - \gdef\kbdexamplefont{\ttsl}\gdef\kbdfont{\tt}% - \else\ifx\txiarg\wordcode - \gdef\kbdexamplefont{\tt}\gdef\kbdfont{\tt}% - \else - \errhelp = \EMsimple - \errmessage{Unknown @kbdinputstyle setting `\txiarg'}% - \fi\fi\fi -} -\def\worddistinct{distinct} -\def\wordexample{example} -\def\wordcode{code} - -% Default is `distinct'. -\kbdinputstyle distinct - -\def\xkey{\key} -\def\kbdfoo#1#2#3\par{\def\one{#1}\def\three{#3}\def\threex{??}% -\ifx\one\xkey\ifx\threex\three \key{#2}% -\else{\tclose{\kbdfont\setupmarkupstyle{kbd}\look}}\fi -\else{\tclose{\kbdfont\setupmarkupstyle{kbd}\look}}\fi} - -% For @indicateurl, @env, @command quotes seem unnecessary, so use \code. -\let\indicateurl=\code -\let\env=\code -\let\command=\code - -% @clicksequence{File @click{} Open ...} -\def\clicksequence#1{\begingroup #1\endgroup} - -% @clickstyle @arrow (by default) -\parseargdef\clickstyle{\def\click{#1}} -\def\click{\arrow} - -% Typeset a dimension, e.g., `in' or `pt'. The only reason for the -% argument is to make the input look right: @dmn{pt} instead of @dmn{}pt. -% -\def\dmn#1{\thinspace #1} - -% @l was never documented to mean ``switch to the Lisp font'', -% and it is not used as such in any manual I can find. We need it for -% Polish suppressed-l. --karl, 22sep96. -%\def\l#1{{\li #1}\null} - -% @acronym for "FBI", "NATO", and the like. -% We print this one point size smaller, since it's intended for -% all-uppercase. -% -\def\acronym#1{\doacronym #1,,\finish} -\def\doacronym#1,#2,#3\finish{% - {\selectfonts\lsize #1}% - \def\temp{#2}% - \ifx\temp\empty \else - \space ({\unsepspaces \ignorespaces \temp \unskip})% - \fi - \null % reset \spacefactor=1000 -} - -% @abbr for "Comput. J." and the like. -% No font change, but don't do end-of-sentence spacing. -% -\def\abbr#1{\doabbr #1,,\finish} -\def\doabbr#1,#2,#3\finish{% - {\plainfrenchspacing #1}% - \def\temp{#2}% - \ifx\temp\empty \else - \space ({\unsepspaces \ignorespaces \temp \unskip})% - \fi - \null % reset \spacefactor=1000 -} - -% @asis just yields its argument. Used with @table, for example. -% -\def\asis#1{#1} - -% @math outputs its argument in math mode. -% -% One complication: _ usually means subscripts, but it could also mean -% an actual _ character, as in @math{@var{some_variable} + 1}. So make -% _ active, and distinguish by seeing if the current family is \slfam, -% which is what @var uses. -{ - \catcode`\_ = \active - \gdef\mathunderscore{% - \catcode`\_=\active - \def_{\ifnum\fam=\slfam \_\else\sb\fi}% - } -} -% Another complication: we want \\ (and @\) to output a math (or tt) \. -% FYI, plain.tex uses \\ as a temporary control sequence (for no -% particular reason), but this is not advertised and we don't care. -% -% The \mathchar is class=0=ordinary, family=7=ttfam, position=5C=\. -\def\mathbackslash{\ifnum\fam=\ttfam \mathchar"075C \else\backslash \fi} -% -\def\math{% - \tex - \mathunderscore - \let\\ = \mathbackslash - \mathactive - % make the texinfo accent commands work in math mode - \let\"=\ddot - \let\'=\acute - \let\==\bar - \let\^=\hat - \let\`=\grave - \let\u=\breve - \let\v=\check - \let\~=\tilde - \let\dotaccent=\dot - $\finishmath -} -\def\finishmath#1{#1$\endgroup} % Close the group opened by \tex. - -% Some active characters (such as <) are spaced differently in math. -% We have to reset their definitions in case the @math was an argument -% to a command which sets the catcodes (such as @item or @section). -% -{ - \catcode`^ = \active - \catcode`< = \active - \catcode`> = \active - \catcode`+ = \active - \catcode`' = \active - \gdef\mathactive{% - \let^ = \ptexhat - \let< = \ptexless - \let> = \ptexgtr - \let+ = \ptexplus - \let' = \ptexquoteright - } -} - -% @inlinefmt{FMTNAME,PROCESSED-TEXT} and @inlineraw{FMTNAME,RAW-TEXT}. -% Ignore unless FMTNAME == tex; then it is like @iftex and @tex, -% except specified as a normal braced arg, so no newlines to worry about. -% -\def\outfmtnametex{tex} -% -\long\def\inlinefmt#1{\doinlinefmt #1,\finish} -\long\def\doinlinefmt#1,#2,\finish{% - \def\inlinefmtname{#1}% - \ifx\inlinefmtname\outfmtnametex \ignorespaces #2\fi -} -% For raw, must switch into @tex before parsing the argument, to avoid -% setting catcodes prematurely. Doing it this way means that, for -% example, @inlineraw{html, foo{bar} gets a parse error instead of being -% ignored. But this isn't important because if people want a literal -% *right* brace they would have to use a command anyway, so they may as -% well use a command to get a left brace too. We could re-use the -% delimiter character idea from \verb, but it seems like overkill. -% -\long\def\inlineraw{\tex \doinlineraw} -\long\def\doinlineraw#1{\doinlinerawtwo #1,\finish} -\def\doinlinerawtwo#1,#2,\finish{% - \def\inlinerawname{#1}% - \ifx\inlinerawname\outfmtnametex \ignorespaces #2\fi - \endgroup % close group opened by \tex. -} - - -\message{glyphs,} -% and logos. - -% @@ prints an @, as does @atchar{}. -\def\@{\char64 } -\let\atchar=\@ - -% @{ @} @lbracechar{} @rbracechar{} all generate brace characters. -% Unless we're in typewriter, use \ecfont because the CM text fonts do -% not have braces, and we don't want to switch into math. -\def\mylbrace{{\ifmonospace\else\ecfont\fi \char123}} -\def\myrbrace{{\ifmonospace\else\ecfont\fi \char125}} -\let\{=\mylbrace \let\lbracechar=\{ -\let\}=\myrbrace \let\rbracechar=\} -\begingroup - % Definitions to produce \{ and \} commands for indices, - % and @{ and @} for the aux/toc files. - \catcode`\{ = \other \catcode`\} = \other - \catcode`\[ = 1 \catcode`\] = 2 - \catcode`\! = 0 \catcode`\\ = \other - !gdef!lbracecmd[\{]% - !gdef!rbracecmd[\}]% - !gdef!lbraceatcmd[@{]% - !gdef!rbraceatcmd[@}]% -!endgroup - -% @comma{} to avoid , parsing problems. -\let\comma = , - -% Accents: @, @dotaccent @ringaccent @ubaraccent @udotaccent -% Others are defined by plain TeX: @` @' @" @^ @~ @= @u @v @H. -\let\, = \ptexc -\let\dotaccent = \ptexdot -\def\ringaccent#1{{\accent23 #1}} -\let\tieaccent = \ptext -\let\ubaraccent = \ptexb -\let\udotaccent = \d - -% Other special characters: @questiondown @exclamdown @ordf @ordm -% Plain TeX defines: @AA @AE @O @OE @L (plus lowercase versions) @ss. -\def\questiondown{?`} -\def\exclamdown{!`} -\def\ordf{\leavevmode\raise1ex\hbox{\selectfonts\lllsize \underbar{a}}} -\def\ordm{\leavevmode\raise1ex\hbox{\selectfonts\lllsize \underbar{o}}} - -% Dotless i and dotless j, used for accents. -\def\imacro{i} -\def\jmacro{j} -\def\dotless#1{% - \def\temp{#1}% - \ifx\temp\imacro \ifmmode\imath \else\ptexi \fi - \else\ifx\temp\jmacro \ifmmode\jmath \else\j \fi - \else \errmessage{@dotless can be used only with i or j}% - \fi\fi -} - -% The \TeX{} logo, as in plain, but resetting the spacing so that a -% period following counts as ending a sentence. (Idea found in latex.) -% -\edef\TeX{\TeX \spacefactor=1000 } - -% @LaTeX{} logo. Not quite the same results as the definition in -% latex.ltx, since we use a different font for the raised A; it's most -% convenient for us to use an explicitly smaller font, rather than using -% the \scriptstyle font (since we don't reset \scriptstyle and -% \scriptscriptstyle). -% -\def\LaTeX{% - L\kern-.36em - {\setbox0=\hbox{T}% - \vbox to \ht0{\hbox{% - \ifx\textnominalsize\xwordpt - % for 10pt running text, \lllsize (8pt) is too small for the A in LaTeX. - % Revert to plain's \scriptsize, which is 7pt. - \count255=\the\fam $\fam\count255 \scriptstyle A$% - \else - % For 11pt, we can use our lllsize. - \selectfonts\lllsize A% - \fi - }% - \vss - }}% - \kern-.15em - \TeX -} - -% Some math mode symbols. -\def\bullet{$\ptexbullet$} -\def\geq{\ifmmode \ge\else $\ge$\fi} -\def\leq{\ifmmode \le\else $\le$\fi} -\def\minus{\ifmmode -\else $-$\fi} - -% @dots{} outputs an ellipsis using the current font. -% We do .5em per period so that it has the same spacing in the cm -% typewriter fonts as three actual period characters; on the other hand, -% in other typewriter fonts three periods are wider than 1.5em. So do -% whichever is larger. -% -\def\dots{% - \leavevmode - \setbox0=\hbox{...}% get width of three periods - \ifdim\wd0 > 1.5em - \dimen0 = \wd0 - \else - \dimen0 = 1.5em - \fi - \hbox to \dimen0{% - \hskip 0pt plus.25fil - .\hskip 0pt plus1fil - .\hskip 0pt plus1fil - .\hskip 0pt plus.5fil - }% -} - -% @enddots{} is an end-of-sentence ellipsis. -% -\def\enddots{% - \dots - \spacefactor=\endofsentencespacefactor -} - -% @point{}, @result{}, @expansion{}, @print{}, @equiv{}. -% -% Since these characters are used in examples, they should be an even number of -% \tt widths. Each \tt character is 1en, so two makes it 1em. -% -\def\point{$\star$} -\def\arrow{\leavevmode\raise.05ex\hbox to 1em{\hfil$\rightarrow$\hfil}} -\def\result{\leavevmode\raise.05ex\hbox to 1em{\hfil$\Rightarrow$\hfil}} -\def\expansion{\leavevmode\hbox to 1em{\hfil$\mapsto$\hfil}} -\def\print{\leavevmode\lower.1ex\hbox to 1em{\hfil$\dashv$\hfil}} -\def\equiv{\leavevmode\hbox to 1em{\hfil$\ptexequiv$\hfil}} - -% The @error{} command. -% Adapted from the TeXbook's \boxit. -% -\newbox\errorbox -% -{\tentt \global\dimen0 = 3em}% Width of the box. -\dimen2 = .55pt % Thickness of rules -% The text. (`r' is open on the right, `e' somewhat less so on the left.) -\setbox0 = \hbox{\kern-.75pt \reducedsf \putworderror\kern-1.5pt} -% -\setbox\errorbox=\hbox to \dimen0{\hfil - \hsize = \dimen0 \advance\hsize by -5.8pt % Space to left+right. - \advance\hsize by -2\dimen2 % Rules. - \vbox{% - \hrule height\dimen2 - \hbox{\vrule width\dimen2 \kern3pt % Space to left of text. - \vtop{\kern2.4pt \box0 \kern2.4pt}% Space above/below. - \kern3pt\vrule width\dimen2}% Space to right. - \hrule height\dimen2} - \hfil} -% -\def\error{\leavevmode\lower.7ex\copy\errorbox} - -% @pounds{} is a sterling sign, which Knuth put in the CM italic font. -% -\def\pounds{{\it\$}} - -% @euro{} comes from a separate font, depending on the current style. -% We use the free feym* fonts from the eurosym package by Henrik -% Theiling, which support regular, slanted, bold and bold slanted (and -% "outlined" (blackboard board, sort of) versions, which we don't need). -% It is available from http://www.ctan.org/tex-archive/fonts/eurosym. -% -% Although only regular is the truly official Euro symbol, we ignore -% that. The Euro is designed to be slightly taller than the regular -% font height. -% -% feymr - regular -% feymo - slanted -% feybr - bold -% feybo - bold slanted -% -% There is no good (free) typewriter version, to my knowledge. -% A feymr10 euro is ~7.3pt wide, while a normal cmtt10 char is ~5.25pt wide. -% Hmm. -% -% Also doesn't work in math. Do we need to do math with euro symbols? -% Hope not. -% -% -\def\euro{{\eurofont e}} -\def\eurofont{% - % We set the font at each command, rather than predefining it in - % \textfonts and the other font-switching commands, so that - % installations which never need the symbol don't have to have the - % font installed. - % - % There is only one designed size (nominal 10pt), so we always scale - % that to the current nominal size. - % - % By the way, simply using "at 1em" works for cmr10 and the like, but - % does not work for cmbx10 and other extended/shrunken fonts. - % - \def\eurosize{\csname\curfontsize nominalsize\endcsname}% - % - \ifx\curfontstyle\bfstylename - % bold: - \font\thiseurofont = \ifusingit{feybo10}{feybr10} at \eurosize - \else - % regular: - \font\thiseurofont = \ifusingit{feymo10}{feymr10} at \eurosize - \fi - \thiseurofont -} - -% Glyphs from the EC fonts. We don't use \let for the aliases, because -% sometimes we redefine the original macro, and the alias should reflect -% the redefinition. -% -% Use LaTeX names for the Icelandic letters. -\def\DH{{\ecfont \char"D0}} % Eth -\def\dh{{\ecfont \char"F0}} % eth -\def\TH{{\ecfont \char"DE}} % Thorn -\def\th{{\ecfont \char"FE}} % thorn -% -\def\guillemetleft{{\ecfont \char"13}} -\def\guillemotleft{\guillemetleft} -\def\guillemetright{{\ecfont \char"14}} -\def\guillemotright{\guillemetright} -\def\guilsinglleft{{\ecfont \char"0E}} -\def\guilsinglright{{\ecfont \char"0F}} -\def\quotedblbase{{\ecfont \char"12}} -\def\quotesinglbase{{\ecfont \char"0D}} -% -% This positioning is not perfect (see the ogonek LaTeX package), but -% we have the precomposed glyphs for the most common cases. We put the -% tests to use those glyphs in the single \ogonek macro so we have fewer -% dummy definitions to worry about for index entries, etc. -% -% ogonek is also used with other letters in Lithuanian (IOU), but using -% the precomposed glyphs for those is not so easy since they aren't in -% the same EC font. -\def\ogonek#1{{% - \def\temp{#1}% - \ifx\temp\macrocharA\Aogonek - \else\ifx\temp\macrochara\aogonek - \else\ifx\temp\macrocharE\Eogonek - \else\ifx\temp\macrochare\eogonek - \else - \ecfont \setbox0=\hbox{#1}% - \ifdim\ht0=1ex\accent"0C #1% - \else\ooalign{\unhbox0\crcr\hidewidth\char"0C \hidewidth}% - \fi - \fi\fi\fi\fi - }% -} -\def\Aogonek{{\ecfont \char"81}}\def\macrocharA{A} -\def\aogonek{{\ecfont \char"A1}}\def\macrochara{a} -\def\Eogonek{{\ecfont \char"86}}\def\macrocharE{E} -\def\eogonek{{\ecfont \char"A6}}\def\macrochare{e} -% -% Use the ec* fonts (cm-super in outline format) for non-CM glyphs. -\def\ecfont{% - % We can't distinguish serif/sans and italic/slanted, but this - % is used for crude hacks anyway (like adding French and German - % quotes to documents typeset with CM, where we lose kerning), so - % hopefully nobody will notice/care. - \edef\ecsize{\csname\curfontsize ecsize\endcsname}% - \edef\nominalsize{\csname\curfontsize nominalsize\endcsname}% - \ifx\curfontstyle\bfstylename - % bold: - \font\thisecfont = ecb\ifusingit{i}{x}\ecsize \space at \nominalsize - \else - % regular: - \font\thisecfont = ec\ifusingit{ti}{rm}\ecsize \space at \nominalsize - \fi - \thisecfont -} - -% @registeredsymbol - R in a circle. The font for the R should really -% be smaller yet, but lllsize is the best we can do for now. -% Adapted from the plain.tex definition of \copyright. -% -\def\registeredsymbol{% - $^{{\ooalign{\hfil\raise.07ex\hbox{\selectfonts\lllsize R}% - \hfil\crcr\Orb}}% - }$% -} - -% @textdegree - the normal degrees sign. -% -\def\textdegree{$^\circ$} - -% Laurent Siebenmann reports \Orb undefined with: -% Textures 1.7.7 (preloaded format=plain 93.10.14) (68K) 16 APR 2004 02:38 -% so we'll define it if necessary. -% -\ifx\Orb\thisisundefined -\def\Orb{\mathhexbox20D} -\fi - -% Quotes. -\chardef\quotedblleft="5C -\chardef\quotedblright=`\" -\chardef\quoteleft=`\` -\chardef\quoteright=`\' - - -\message{page headings,} - -\newskip\titlepagetopglue \titlepagetopglue = 1.5in -\newskip\titlepagebottomglue \titlepagebottomglue = 2pc - -% First the title page. Must do @settitle before @titlepage. -\newif\ifseenauthor -\newif\iffinishedtitlepage - -% Do an implicit @contents or @shortcontents after @end titlepage if the -% user says @setcontentsaftertitlepage or @setshortcontentsaftertitlepage. -% -\newif\ifsetcontentsaftertitlepage - \let\setcontentsaftertitlepage = \setcontentsaftertitlepagetrue -\newif\ifsetshortcontentsaftertitlepage - \let\setshortcontentsaftertitlepage = \setshortcontentsaftertitlepagetrue - -\parseargdef\shorttitlepage{% - \begingroup \hbox{}\vskip 1.5in \chaprm \centerline{#1}% - \endgroup\page\hbox{}\page} - -\envdef\titlepage{% - % Open one extra group, as we want to close it in the middle of \Etitlepage. - \begingroup - \parindent=0pt \textfonts - % Leave some space at the very top of the page. - \vglue\titlepagetopglue - % No rule at page bottom unless we print one at the top with @title. - \finishedtitlepagetrue - % - % Most title ``pages'' are actually two pages long, with space - % at the top of the second. We don't want the ragged left on the second. - \let\oldpage = \page - \def\page{% - \iffinishedtitlepage\else - \finishtitlepage - \fi - \let\page = \oldpage - \page - \null - }% -} - -\def\Etitlepage{% - \iffinishedtitlepage\else - \finishtitlepage - \fi - % It is important to do the page break before ending the group, - % because the headline and footline are only empty inside the group. - % If we use the new definition of \page, we always get a blank page - % after the title page, which we certainly don't want. - \oldpage - \endgroup - % - % Need this before the \...aftertitlepage checks so that if they are - % in effect the toc pages will come out with page numbers. - \HEADINGSon - % - % If they want short, they certainly want long too. - \ifsetshortcontentsaftertitlepage - \shortcontents - \contents - \global\let\shortcontents = \relax - \global\let\contents = \relax - \fi - % - \ifsetcontentsaftertitlepage - \contents - \global\let\contents = \relax - \global\let\shortcontents = \relax - \fi -} - -\def\finishtitlepage{% - \vskip4pt \hrule height 2pt width \hsize - \vskip\titlepagebottomglue - \finishedtitlepagetrue -} - -% Macros to be used within @titlepage: - -\let\subtitlerm=\tenrm -\def\subtitlefont{\subtitlerm \normalbaselineskip = 13pt \normalbaselines} - -\parseargdef\title{% - \checkenv\titlepage - \leftline{\titlefonts\rmisbold #1} - % print a rule at the page bottom also. - \finishedtitlepagefalse - \vskip4pt \hrule height 4pt width \hsize \vskip4pt -} - -\parseargdef\subtitle{% - \checkenv\titlepage - {\subtitlefont \rightline{#1}}% -} - -% @author should come last, but may come many times. -% It can also be used inside @quotation. -% -\parseargdef\author{% - \def\temp{\quotation}% - \ifx\thisenv\temp - \def\quotationauthor{#1}% printed in \Equotation. - \else - \checkenv\titlepage - \ifseenauthor\else \vskip 0pt plus 1filll \seenauthortrue \fi - {\secfonts\rmisbold \leftline{#1}}% - \fi -} - - -% Set up page headings and footings. - -\let\thispage=\folio - -\newtoks\evenheadline % headline on even pages -\newtoks\oddheadline % headline on odd pages -\newtoks\evenfootline % footline on even pages -\newtoks\oddfootline % footline on odd pages - -% Now make TeX use those variables -\headline={{\textfonts\rm \ifodd\pageno \the\oddheadline - \else \the\evenheadline \fi}} -\footline={{\textfonts\rm \ifodd\pageno \the\oddfootline - \else \the\evenfootline \fi}\HEADINGShook} -\let\HEADINGShook=\relax - -% Commands to set those variables. -% For example, this is what @headings on does -% @evenheading @thistitle|@thispage|@thischapter -% @oddheading @thischapter|@thispage|@thistitle -% @evenfooting @thisfile|| -% @oddfooting ||@thisfile - - -\def\evenheading{\parsearg\evenheadingxxx} -\def\evenheadingxxx #1{\evenheadingyyy #1\|\|\|\|\finish} -\def\evenheadingyyy #1\|#2\|#3\|#4\finish{% -\global\evenheadline={\rlap{\centerline{#2}}\line{#1\hfil#3}}} - -\def\oddheading{\parsearg\oddheadingxxx} -\def\oddheadingxxx #1{\oddheadingyyy #1\|\|\|\|\finish} -\def\oddheadingyyy #1\|#2\|#3\|#4\finish{% -\global\oddheadline={\rlap{\centerline{#2}}\line{#1\hfil#3}}} - -\parseargdef\everyheading{\oddheadingxxx{#1}\evenheadingxxx{#1}}% - -\def\evenfooting{\parsearg\evenfootingxxx} -\def\evenfootingxxx #1{\evenfootingyyy #1\|\|\|\|\finish} -\def\evenfootingyyy #1\|#2\|#3\|#4\finish{% -\global\evenfootline={\rlap{\centerline{#2}}\line{#1\hfil#3}}} - -\def\oddfooting{\parsearg\oddfootingxxx} -\def\oddfootingxxx #1{\oddfootingyyy #1\|\|\|\|\finish} -\def\oddfootingyyy #1\|#2\|#3\|#4\finish{% - \global\oddfootline = {\rlap{\centerline{#2}}\line{#1\hfil#3}}% - % - % Leave some space for the footline. Hopefully ok to assume - % @evenfooting will not be used by itself. - \global\advance\pageheight by -12pt - \global\advance\vsize by -12pt -} - -\parseargdef\everyfooting{\oddfootingxxx{#1}\evenfootingxxx{#1}} - -% @evenheadingmarks top \thischapter <- chapter at the top of a page -% @evenheadingmarks bottom \thischapter <- chapter at the bottom of a page -% -% The same set of arguments for: -% -% @oddheadingmarks -% @evenfootingmarks -% @oddfootingmarks -% @everyheadingmarks -% @everyfootingmarks - -\def\evenheadingmarks{\headingmarks{even}{heading}} -\def\oddheadingmarks{\headingmarks{odd}{heading}} -\def\evenfootingmarks{\headingmarks{even}{footing}} -\def\oddfootingmarks{\headingmarks{odd}{footing}} -\def\everyheadingmarks#1 {\headingmarks{even}{heading}{#1} - \headingmarks{odd}{heading}{#1} } -\def\everyfootingmarks#1 {\headingmarks{even}{footing}{#1} - \headingmarks{odd}{footing}{#1} } -% #1 = even/odd, #2 = heading/footing, #3 = top/bottom. -\def\headingmarks#1#2#3 {% - \expandafter\let\expandafter\temp \csname get#3headingmarks\endcsname - \global\expandafter\let\csname get#1#2marks\endcsname \temp -} - -\everyheadingmarks bottom -\everyfootingmarks bottom - -% @headings double turns headings on for double-sided printing. -% @headings single turns headings on for single-sided printing. -% @headings off turns them off. -% @headings on same as @headings double, retained for compatibility. -% @headings after turns on double-sided headings after this page. -% @headings doubleafter turns on double-sided headings after this page. -% @headings singleafter turns on single-sided headings after this page. -% By default, they are off at the start of a document, -% and turned `on' after @end titlepage. - -\def\headings #1 {\csname HEADINGS#1\endcsname} - -\def\headingsoff{% non-global headings elimination - \evenheadline={\hfil}\evenfootline={\hfil}% - \oddheadline={\hfil}\oddfootline={\hfil}% -} - -\def\HEADINGSoff{{\globaldefs=1 \headingsoff}} % global setting -\HEADINGSoff % it's the default - -% When we turn headings on, set the page number to 1. -% For double-sided printing, put current file name in lower left corner, -% chapter name on inside top of right hand pages, document -% title on inside top of left hand pages, and page numbers on outside top -% edge of all pages. -\def\HEADINGSdouble{% -\global\pageno=1 -\global\evenfootline={\hfil} -\global\oddfootline={\hfil} -\global\evenheadline={\line{\folio\hfil\thistitle}} -\global\oddheadline={\line{\thischapter\hfil\folio}} -\global\let\contentsalignmacro = \chapoddpage -} -\let\contentsalignmacro = \chappager - -% For single-sided printing, chapter title goes across top left of page, -% page number on top right. -\def\HEADINGSsingle{% -\global\pageno=1 -\global\evenfootline={\hfil} -\global\oddfootline={\hfil} -\global\evenheadline={\line{\thischapter\hfil\folio}} -\global\oddheadline={\line{\thischapter\hfil\folio}} -\global\let\contentsalignmacro = \chappager -} -\def\HEADINGSon{\HEADINGSdouble} - -\def\HEADINGSafter{\let\HEADINGShook=\HEADINGSdoublex} -\let\HEADINGSdoubleafter=\HEADINGSafter -\def\HEADINGSdoublex{% -\global\evenfootline={\hfil} -\global\oddfootline={\hfil} -\global\evenheadline={\line{\folio\hfil\thistitle}} -\global\oddheadline={\line{\thischapter\hfil\folio}} -\global\let\contentsalignmacro = \chapoddpage -} - -\def\HEADINGSsingleafter{\let\HEADINGShook=\HEADINGSsinglex} -\def\HEADINGSsinglex{% -\global\evenfootline={\hfil} -\global\oddfootline={\hfil} -\global\evenheadline={\line{\thischapter\hfil\folio}} -\global\oddheadline={\line{\thischapter\hfil\folio}} -\global\let\contentsalignmacro = \chappager -} - -% Subroutines used in generating headings -% This produces Day Month Year style of output. -% Only define if not already defined, in case a txi-??.tex file has set -% up a different format (e.g., txi-cs.tex does this). -\ifx\today\thisisundefined -\def\today{% - \number\day\space - \ifcase\month - \or\putwordMJan\or\putwordMFeb\or\putwordMMar\or\putwordMApr - \or\putwordMMay\or\putwordMJun\or\putwordMJul\or\putwordMAug - \or\putwordMSep\or\putwordMOct\or\putwordMNov\or\putwordMDec - \fi - \space\number\year} -\fi - -% @settitle line... specifies the title of the document, for headings. -% It generates no output of its own. -\def\thistitle{\putwordNoTitle} -\def\settitle{\parsearg{\gdef\thistitle}} - - -\message{tables,} -% Tables -- @table, @ftable, @vtable, @item(x). - -% default indentation of table text -\newdimen\tableindent \tableindent=.8in -% default indentation of @itemize and @enumerate text -\newdimen\itemindent \itemindent=.3in -% margin between end of table item and start of table text. -\newdimen\itemmargin \itemmargin=.1in - -% used internally for \itemindent minus \itemmargin -\newdimen\itemmax - -% Note @table, @ftable, and @vtable define @item, @itemx, etc., with -% these defs. -% They also define \itemindex -% to index the item name in whatever manner is desired (perhaps none). - -\newif\ifitemxneedsnegativevskip - -\def\itemxpar{\par\ifitemxneedsnegativevskip\nobreak\vskip-\parskip\nobreak\fi} - -\def\internalBitem{\smallbreak \parsearg\itemzzz} -\def\internalBitemx{\itemxpar \parsearg\itemzzz} - -\def\itemzzz #1{\begingroup % - \advance\hsize by -\rightskip - \advance\hsize by -\tableindent - \setbox0=\hbox{\itemindicate{#1}}% - \itemindex{#1}% - \nobreak % This prevents a break before @itemx. - % - % If the item text does not fit in the space we have, put it on a line - % by itself, and do not allow a page break either before or after that - % line. We do not start a paragraph here because then if the next - % command is, e.g., @kindex, the whatsit would get put into the - % horizontal list on a line by itself, resulting in extra blank space. - \ifdim \wd0>\itemmax - % - % Make this a paragraph so we get the \parskip glue and wrapping, - % but leave it ragged-right. - \begingroup - \advance\leftskip by-\tableindent - \advance\hsize by\tableindent - \advance\rightskip by0pt plus1fil\relax - \leavevmode\unhbox0\par - \endgroup - % - % We're going to be starting a paragraph, but we don't want the - % \parskip glue -- logically it's part of the @item we just started. - \nobreak \vskip-\parskip - % - % Stop a page break at the \parskip glue coming up. However, if - % what follows is an environment such as @example, there will be no - % \parskip glue; then the negative vskip we just inserted would - % cause the example and the item to crash together. So we use this - % bizarre value of 10001 as a signal to \aboveenvbreak to insert - % \parskip glue after all. Section titles are handled this way also. - % - \penalty 10001 - \endgroup - \itemxneedsnegativevskipfalse - \else - % The item text fits into the space. Start a paragraph, so that the - % following text (if any) will end up on the same line. - \noindent - % Do this with kerns and \unhbox so that if there is a footnote in - % the item text, it can migrate to the main vertical list and - % eventually be printed. - \nobreak\kern-\tableindent - \dimen0 = \itemmax \advance\dimen0 by \itemmargin \advance\dimen0 by -\wd0 - \unhbox0 - \nobreak\kern\dimen0 - \endgroup - \itemxneedsnegativevskiptrue - \fi -} - -\def\item{\errmessage{@item while not in a list environment}} -\def\itemx{\errmessage{@itemx while not in a list environment}} - -% @table, @ftable, @vtable. -\envdef\table{% - \let\itemindex\gobble - \tablecheck{table}% -} -\envdef\ftable{% - \def\itemindex ##1{\doind {fn}{\code{##1}}}% - \tablecheck{ftable}% -} -\envdef\vtable{% - \def\itemindex ##1{\doind {vr}{\code{##1}}}% - \tablecheck{vtable}% -} -\def\tablecheck#1{% - \ifnum \the\catcode`\^^M=\active - \endgroup - \errmessage{This command won't work in this context; perhaps the problem is - that we are \inenvironment\thisenv}% - \def\next{\doignore{#1}}% - \else - \let\next\tablex - \fi - \next -} -\def\tablex#1{% - \def\itemindicate{#1}% - \parsearg\tabley -} -\def\tabley#1{% - {% - \makevalueexpandable - \edef\temp{\noexpand\tablez #1\space\space\space}% - \expandafter - }\temp \endtablez -} -\def\tablez #1 #2 #3 #4\endtablez{% - \aboveenvbreak - \ifnum 0#1>0 \advance \leftskip by #1\mil \fi - \ifnum 0#2>0 \tableindent=#2\mil \fi - \ifnum 0#3>0 \advance \rightskip by #3\mil \fi - \itemmax=\tableindent - \advance \itemmax by -\itemmargin - \advance \leftskip by \tableindent - \exdentamount=\tableindent - \parindent = 0pt - \parskip = \smallskipamount - \ifdim \parskip=0pt \parskip=2pt \fi - \let\item = \internalBitem - \let\itemx = \internalBitemx -} -\def\Etable{\endgraf\afterenvbreak} -\let\Eftable\Etable -\let\Evtable\Etable -\let\Eitemize\Etable -\let\Eenumerate\Etable - -% This is the counter used by @enumerate, which is really @itemize - -\newcount \itemno - -\envdef\itemize{\parsearg\doitemize} - -\def\doitemize#1{% - \aboveenvbreak - \itemmax=\itemindent - \advance\itemmax by -\itemmargin - \advance\leftskip by \itemindent - \exdentamount=\itemindent - \parindent=0pt - \parskip=\smallskipamount - \ifdim\parskip=0pt \parskip=2pt \fi - % - % Try typesetting the item mark that if the document erroneously says - % something like @itemize @samp (intending @table), there's an error - % right away at the @itemize. It's not the best error message in the - % world, but it's better than leaving it to the @item. This means if - % the user wants an empty mark, they have to say @w{} not just @w. - \def\itemcontents{#1}% - \setbox0 = \hbox{\itemcontents}% - % - % @itemize with no arg is equivalent to @itemize @bullet. - \ifx\itemcontents\empty\def\itemcontents{\bullet}\fi - % - \let\item=\itemizeitem -} - -% Definition of @item while inside @itemize and @enumerate. -% -\def\itemizeitem{% - \advance\itemno by 1 % for enumerations - {\let\par=\endgraf \smallbreak}% reasonable place to break - {% - % If the document has an @itemize directly after a section title, a - % \nobreak will be last on the list, and \sectionheading will have - % done a \vskip-\parskip. In that case, we don't want to zero - % parskip, or the item text will crash with the heading. On the - % other hand, when there is normal text preceding the item (as there - % usually is), we do want to zero parskip, or there would be too much - % space. In that case, we won't have a \nobreak before. At least - % that's the theory. - \ifnum\lastpenalty<10000 \parskip=0in \fi - \noindent - \hbox to 0pt{\hss \itemcontents \kern\itemmargin}% - % - \vadjust{\penalty 1200}}% not good to break after first line of item. - \flushcr -} - -% \splitoff TOKENS\endmark defines \first to be the first token in -% TOKENS, and \rest to be the remainder. -% -\def\splitoff#1#2\endmark{\def\first{#1}\def\rest{#2}}% - -% Allow an optional argument of an uppercase letter, lowercase letter, -% or number, to specify the first label in the enumerated list. No -% argument is the same as `1'. -% -\envparseargdef\enumerate{\enumeratey #1 \endenumeratey} -\def\enumeratey #1 #2\endenumeratey{% - % If we were given no argument, pretend we were given `1'. - \def\thearg{#1}% - \ifx\thearg\empty \def\thearg{1}\fi - % - % Detect if the argument is a single token. If so, it might be a - % letter. Otherwise, the only valid thing it can be is a number. - % (We will always have one token, because of the test we just made. - % This is a good thing, since \splitoff doesn't work given nothing at - % all -- the first parameter is undelimited.) - \expandafter\splitoff\thearg\endmark - \ifx\rest\empty - % Only one token in the argument. It could still be anything. - % A ``lowercase letter'' is one whose \lccode is nonzero. - % An ``uppercase letter'' is one whose \lccode is both nonzero, and - % not equal to itself. - % Otherwise, we assume it's a number. - % - % We need the \relax at the end of the \ifnum lines to stop TeX from - % continuing to look for a . - % - \ifnum\lccode\expandafter`\thearg=0\relax - \numericenumerate % a number (we hope) - \else - % It's a letter. - \ifnum\lccode\expandafter`\thearg=\expandafter`\thearg\relax - \lowercaseenumerate % lowercase letter - \else - \uppercaseenumerate % uppercase letter - \fi - \fi - \else - % Multiple tokens in the argument. We hope it's a number. - \numericenumerate - \fi -} - -% An @enumerate whose labels are integers. The starting integer is -% given in \thearg. -% -\def\numericenumerate{% - \itemno = \thearg - \startenumeration{\the\itemno}% -} - -% The starting (lowercase) letter is in \thearg. -\def\lowercaseenumerate{% - \itemno = \expandafter`\thearg - \startenumeration{% - % Be sure we're not beyond the end of the alphabet. - \ifnum\itemno=0 - \errmessage{No more lowercase letters in @enumerate; get a bigger - alphabet}% - \fi - \char\lccode\itemno - }% -} - -% The starting (uppercase) letter is in \thearg. -\def\uppercaseenumerate{% - \itemno = \expandafter`\thearg - \startenumeration{% - % Be sure we're not beyond the end of the alphabet. - \ifnum\itemno=0 - \errmessage{No more uppercase letters in @enumerate; get a bigger - alphabet} - \fi - \char\uccode\itemno - }% -} - -% Call \doitemize, adding a period to the first argument and supplying the -% common last two arguments. Also subtract one from the initial value in -% \itemno, since @item increments \itemno. -% -\def\startenumeration#1{% - \advance\itemno by -1 - \doitemize{#1.}\flushcr -} - -% @alphaenumerate and @capsenumerate are abbreviations for giving an arg -% to @enumerate. -% -\def\alphaenumerate{\enumerate{a}} -\def\capsenumerate{\enumerate{A}} -\def\Ealphaenumerate{\Eenumerate} -\def\Ecapsenumerate{\Eenumerate} - - -% @multitable macros -% Amy Hendrickson, 8/18/94, 3/6/96 -% -% @multitable ... @end multitable will make as many columns as desired. -% Contents of each column will wrap at width given in preamble. Width -% can be specified either with sample text given in a template line, -% or in percent of \hsize, the current width of text on page. - -% Table can continue over pages but will only break between lines. - -% To make preamble: -% -% Either define widths of columns in terms of percent of \hsize: -% @multitable @columnfractions .25 .3 .45 -% @item ... -% -% Numbers following @columnfractions are the percent of the total -% current hsize to be used for each column. You may use as many -% columns as desired. - - -% Or use a template: -% @multitable {Column 1 template} {Column 2 template} {Column 3 template} -% @item ... -% using the widest term desired in each column. - -% Each new table line starts with @item, each subsequent new column -% starts with @tab. Empty columns may be produced by supplying @tab's -% with nothing between them for as many times as empty columns are needed, -% ie, @tab@tab@tab will produce two empty columns. - -% @item, @tab do not need to be on their own lines, but it will not hurt -% if they are. - -% Sample multitable: - -% @multitable {Column 1 template} {Column 2 template} {Column 3 template} -% @item first col stuff @tab second col stuff @tab third col -% @item -% first col stuff -% @tab -% second col stuff -% @tab -% third col -% @item first col stuff @tab second col stuff -% @tab Many paragraphs of text may be used in any column. -% -% They will wrap at the width determined by the template. -% @item@tab@tab This will be in third column. -% @end multitable - -% Default dimensions may be reset by user. -% @multitableparskip is vertical space between paragraphs in table. -% @multitableparindent is paragraph indent in table. -% @multitablecolmargin is horizontal space to be left between columns. -% @multitablelinespace is space to leave between table items, baseline -% to baseline. -% 0pt means it depends on current normal line spacing. -% -\newskip\multitableparskip -\newskip\multitableparindent -\newdimen\multitablecolspace -\newskip\multitablelinespace -\multitableparskip=0pt -\multitableparindent=6pt -\multitablecolspace=12pt -\multitablelinespace=0pt - -% Macros used to set up halign preamble: -% -\let\endsetuptable\relax -\def\xendsetuptable{\endsetuptable} -\let\columnfractions\relax -\def\xcolumnfractions{\columnfractions} -\newif\ifsetpercent - -% #1 is the @columnfraction, usually a decimal number like .5, but might -% be just 1. We just use it, whatever it is. -% -\def\pickupwholefraction#1 {% - \global\advance\colcount by 1 - \expandafter\xdef\csname col\the\colcount\endcsname{#1\hsize}% - \setuptable -} - -\newcount\colcount -\def\setuptable#1{% - \def\firstarg{#1}% - \ifx\firstarg\xendsetuptable - \let\go = \relax - \else - \ifx\firstarg\xcolumnfractions - \global\setpercenttrue - \else - \ifsetpercent - \let\go\pickupwholefraction - \else - \global\advance\colcount by 1 - \setbox0=\hbox{#1\unskip\space}% Add a normal word space as a - % separator; typically that is always in the input, anyway. - \expandafter\xdef\csname col\the\colcount\endcsname{\the\wd0}% - \fi - \fi - \ifx\go\pickupwholefraction - % Put the argument back for the \pickupwholefraction call, so - % we'll always have a period there to be parsed. - \def\go{\pickupwholefraction#1}% - \else - \let\go = \setuptable - \fi% - \fi - \go -} - -% multitable-only commands. -% -% @headitem starts a heading row, which we typeset in bold. -% Assignments have to be global since we are inside the implicit group -% of an alignment entry. \everycr resets \everytab so we don't have to -% undo it ourselves. -\def\headitemfont{\b}% for people to use in the template row; not changeable -\def\headitem{% - \checkenv\multitable - \crcr - \global\everytab={\bf}% can't use \headitemfont since the parsing differs - \the\everytab % for the first item -}% -% -% A \tab used to include \hskip1sp. But then the space in a template -% line is not enough. That is bad. So let's go back to just `&' until -% we again encounter the problem the 1sp was intended to solve. -% --karl, nathan@acm.org, 20apr99. -\def\tab{\checkenv\multitable &\the\everytab}% - -% @multitable ... @end multitable definitions: -% -\newtoks\everytab % insert after every tab. -% -\envdef\multitable{% - \vskip\parskip - \startsavinginserts - % - % @item within a multitable starts a normal row. - % We use \def instead of \let so that if one of the multitable entries - % contains an @itemize, we don't choke on the \item (seen as \crcr aka - % \endtemplate) expanding \doitemize. - \def\item{\crcr}% - % - \tolerance=9500 - \hbadness=9500 - \setmultitablespacing - \parskip=\multitableparskip - \parindent=\multitableparindent - \overfullrule=0pt - \global\colcount=0 - % - \everycr = {% - \noalign{% - \global\everytab={}% - \global\colcount=0 % Reset the column counter. - % Check for saved footnotes, etc. - \checkinserts - % Keeps underfull box messages off when table breaks over pages. - %\filbreak - % Maybe so, but it also creates really weird page breaks when the - % table breaks over pages. Wouldn't \vfil be better? Wait until the - % problem manifests itself, so it can be fixed for real --karl. - }% - }% - % - \parsearg\domultitable -} -\def\domultitable#1{% - % To parse everything between @multitable and @item: - \setuptable#1 \endsetuptable - % - % This preamble sets up a generic column definition, which will - % be used as many times as user calls for columns. - % \vtop will set a single line and will also let text wrap and - % continue for many paragraphs if desired. - \halign\bgroup &% - \global\advance\colcount by 1 - \multistrut - \vtop{% - % Use the current \colcount to find the correct column width: - \hsize=\expandafter\csname col\the\colcount\endcsname - % - % In order to keep entries from bumping into each other - % we will add a \leftskip of \multitablecolspace to all columns after - % the first one. - % - % If a template has been used, we will add \multitablecolspace - % to the width of each template entry. - % - % If the user has set preamble in terms of percent of \hsize we will - % use that dimension as the width of the column, and the \leftskip - % will keep entries from bumping into each other. Table will start at - % left margin and final column will justify at right margin. - % - % Make sure we don't inherit \rightskip from the outer environment. - \rightskip=0pt - \ifnum\colcount=1 - % The first column will be indented with the surrounding text. - \advance\hsize by\leftskip - \else - \ifsetpercent \else - % If user has not set preamble in terms of percent of \hsize - % we will advance \hsize by \multitablecolspace. - \advance\hsize by \multitablecolspace - \fi - % In either case we will make \leftskip=\multitablecolspace: - \leftskip=\multitablecolspace - \fi - % Ignoring space at the beginning and end avoids an occasional spurious - % blank line, when TeX decides to break the line at the space before the - % box from the multistrut, so the strut ends up on a line by itself. - % For example: - % @multitable @columnfractions .11 .89 - % @item @code{#} - % @tab Legal holiday which is valid in major parts of the whole country. - % Is automatically provided with highlighting sequences respectively - % marking characters. - \noindent\ignorespaces##\unskip\multistrut - }\cr -} -\def\Emultitable{% - \crcr - \egroup % end the \halign - \global\setpercentfalse -} - -\def\setmultitablespacing{% - \def\multistrut{\strut}% just use the standard line spacing - % - % Compute \multitablelinespace (if not defined by user) for use in - % \multitableparskip calculation. We used define \multistrut based on - % this, but (ironically) that caused the spacing to be off. - % See bug-texinfo report from Werner Lemberg, 31 Oct 2004 12:52:20 +0100. -\ifdim\multitablelinespace=0pt -\setbox0=\vbox{X}\global\multitablelinespace=\the\baselineskip -\global\advance\multitablelinespace by-\ht0 -\fi -% Test to see if parskip is larger than space between lines of -% table. If not, do nothing. -% If so, set to same dimension as multitablelinespace. -\ifdim\multitableparskip>\multitablelinespace -\global\multitableparskip=\multitablelinespace -\global\advance\multitableparskip-7pt % to keep parskip somewhat smaller - % than skip between lines in the table. -\fi% -\ifdim\multitableparskip=0pt -\global\multitableparskip=\multitablelinespace -\global\advance\multitableparskip-7pt % to keep parskip somewhat smaller - % than skip between lines in the table. -\fi} - - -\message{conditionals,} - -% @iftex, @ifnotdocbook, @ifnothtml, @ifnotinfo, @ifnotplaintext, -% @ifnotxml always succeed. They currently do nothing; we don't -% attempt to check whether the conditionals are properly nested. But we -% have to remember that they are conditionals, so that @end doesn't -% attempt to close an environment group. -% -\def\makecond#1{% - \expandafter\let\csname #1\endcsname = \relax - \expandafter\let\csname iscond.#1\endcsname = 1 -} -\makecond{iftex} -\makecond{ifnotdocbook} -\makecond{ifnothtml} -\makecond{ifnotinfo} -\makecond{ifnotplaintext} -\makecond{ifnotxml} - -% Ignore @ignore, @ifhtml, @ifinfo, and the like. -% -\def\direntry{\doignore{direntry}} -\def\documentdescription{\doignore{documentdescription}} -\def\docbook{\doignore{docbook}} -\def\html{\doignore{html}} -\def\ifdocbook{\doignore{ifdocbook}} -\def\ifhtml{\doignore{ifhtml}} -\def\ifinfo{\doignore{ifinfo}} -\def\ifnottex{\doignore{ifnottex}} -\def\ifplaintext{\doignore{ifplaintext}} -\def\ifxml{\doignore{ifxml}} -\def\ignore{\doignore{ignore}} -\def\menu{\doignore{menu}} -\def\xml{\doignore{xml}} - -% Ignore text until a line `@end #1', keeping track of nested conditionals. -% -% A count to remember the depth of nesting. -\newcount\doignorecount - -\def\doignore#1{\begingroup - % Scan in ``verbatim'' mode: - \obeylines - \catcode`\@ = \other - \catcode`\{ = \other - \catcode`\} = \other - % - % Make sure that spaces turn into tokens that match what \doignoretext wants. - \spaceisspace - % - % Count number of #1's that we've seen. - \doignorecount = 0 - % - % Swallow text until we reach the matching `@end #1'. - \dodoignore{#1}% -} - -{ \catcode`_=11 % We want to use \_STOP_ which cannot appear in texinfo source. - \obeylines % - % - \gdef\dodoignore#1{% - % #1 contains the command name as a string, e.g., `ifinfo'. - % - % Define a command to find the next `@end #1'. - \long\def\doignoretext##1^^M@end #1{% - \doignoretextyyy##1^^M@#1\_STOP_}% - % - % And this command to find another #1 command, at the beginning of a - % line. (Otherwise, we would consider a line `@c @ifset', for - % example, to count as an @ifset for nesting.) - \long\def\doignoretextyyy##1^^M@#1##2\_STOP_{\doignoreyyy{##2}\_STOP_}% - % - % And now expand that command. - \doignoretext ^^M% - }% -} - -\def\doignoreyyy#1{% - \def\temp{#1}% - \ifx\temp\empty % Nothing found. - \let\next\doignoretextzzz - \else % Found a nested condition, ... - \advance\doignorecount by 1 - \let\next\doignoretextyyy % ..., look for another. - % If we're here, #1 ends with ^^M\ifinfo (for example). - \fi - \next #1% the token \_STOP_ is present just after this macro. -} - -% We have to swallow the remaining "\_STOP_". -% -\def\doignoretextzzz#1{% - \ifnum\doignorecount = 0 % We have just found the outermost @end. - \let\next\enddoignore - \else % Still inside a nested condition. - \advance\doignorecount by -1 - \let\next\doignoretext % Look for the next @end. - \fi - \next -} - -% Finish off ignored text. -{ \obeylines% - % Ignore anything after the last `@end #1'; this matters in verbatim - % environments, where otherwise the newline after an ignored conditional - % would result in a blank line in the output. - \gdef\enddoignore#1^^M{\endgroup\ignorespaces}% -} - - -% @set VAR sets the variable VAR to an empty value. -% @set VAR REST-OF-LINE sets VAR to the value REST-OF-LINE. -% -% Since we want to separate VAR from REST-OF-LINE (which might be -% empty), we can't just use \parsearg; we have to insert a space of our -% own to delimit the rest of the line, and then take it out again if we -% didn't need it. -% We rely on the fact that \parsearg sets \catcode`\ =10. -% -\parseargdef\set{\setyyy#1 \endsetyyy} -\def\setyyy#1 #2\endsetyyy{% - {% - \makevalueexpandable - \def\temp{#2}% - \edef\next{\gdef\makecsname{SET#1}}% - \ifx\temp\empty - \next{}% - \else - \setzzz#2\endsetzzz - \fi - }% -} -% Remove the trailing space \setxxx inserted. -\def\setzzz#1 \endsetzzz{\next{#1}} - -% @clear VAR clears (i.e., unsets) the variable VAR. -% -\parseargdef\clear{% - {% - \makevalueexpandable - \global\expandafter\let\csname SET#1\endcsname=\relax - }% -} - -% @value{foo} gets the text saved in variable foo. -\def\value{\begingroup\makevalueexpandable\valuexxx} -\def\valuexxx#1{\expandablevalue{#1}\endgroup} -{ - \catcode`\- = \active \catcode`\_ = \active - % - \gdef\makevalueexpandable{% - \let\value = \expandablevalue - % We don't want these characters active, ... - \catcode`\-=\other \catcode`\_=\other - % ..., but we might end up with active ones in the argument if - % we're called from @code, as @code{@value{foo-bar_}}, though. - % So \let them to their normal equivalents. - \let-\realdash \let_\normalunderscore - } -} - -% We have this subroutine so that we can handle at least some @value's -% properly in indexes (we call \makevalueexpandable in \indexdummies). -% The command has to be fully expandable (if the variable is set), since -% the result winds up in the index file. This means that if the -% variable's value contains other Texinfo commands, it's almost certain -% it will fail (although perhaps we could fix that with sufficient work -% to do a one-level expansion on the result, instead of complete). -% -\def\expandablevalue#1{% - \expandafter\ifx\csname SET#1\endcsname\relax - {[No value for ``#1'']}% - \message{Variable `#1', used in @value, is not set.}% - \else - \csname SET#1\endcsname - \fi -} - -% @ifset VAR ... @end ifset reads the `...' iff VAR has been defined -% with @set. -% -% To get special treatment of `@end ifset,' call \makeond and the redefine. -% -\makecond{ifset} -\def\ifset{\parsearg{\doifset{\let\next=\ifsetfail}}} -\def\doifset#1#2{% - {% - \makevalueexpandable - \let\next=\empty - \expandafter\ifx\csname SET#2\endcsname\relax - #1% If not set, redefine \next. - \fi - \expandafter - }\next -} -\def\ifsetfail{\doignore{ifset}} - -% @ifclear VAR ... @end ifclear reads the `...' iff VAR has never been -% defined with @set, or has been undefined with @clear. -% -% The `\else' inside the `\doifset' parameter is a trick to reuse the -% above code: if the variable is not set, do nothing, if it is set, -% then redefine \next to \ifclearfail. -% -\makecond{ifclear} -\def\ifclear{\parsearg{\doifset{\else \let\next=\ifclearfail}}} -\def\ifclearfail{\doignore{ifclear}} - -% @dircategory CATEGORY -- specify a category of the dir file -% which this file should belong to. Ignore this in TeX. -\let\dircategory=\comment - -% @defininfoenclose. -\let\definfoenclose=\comment - - -\message{indexing,} -% Index generation facilities - -% Define \newwrite to be identical to plain tex's \newwrite -% except not \outer, so it can be used within macros and \if's. -\edef\newwrite{\makecsname{ptexnewwrite}} - -% \newindex {foo} defines an index named foo. -% It automatically defines \fooindex such that -% \fooindex ...rest of line... puts an entry in the index foo. -% It also defines \fooindfile to be the number of the output channel for -% the file that accumulates this index. The file's extension is foo. -% The name of an index should be no more than 2 characters long -% for the sake of vms. -% -\def\newindex#1{% - \iflinks - \expandafter\newwrite \csname#1indfile\endcsname - \openout \csname#1indfile\endcsname \jobname.#1 % Open the file - \fi - \expandafter\xdef\csname#1index\endcsname{% % Define @#1index - \noexpand\doindex{#1}} -} - -% @defindex foo == \newindex{foo} -% -\def\defindex{\parsearg\newindex} - -% Define @defcodeindex, like @defindex except put all entries in @code. -% -\def\defcodeindex{\parsearg\newcodeindex} -% -\def\newcodeindex#1{% - \iflinks - \expandafter\newwrite \csname#1indfile\endcsname - \openout \csname#1indfile\endcsname \jobname.#1 - \fi - \expandafter\xdef\csname#1index\endcsname{% - \noexpand\docodeindex{#1}}% -} - - -% @synindex foo bar makes index foo feed into index bar. -% Do this instead of @defindex foo if you don't want it as a separate index. -% -% @syncodeindex foo bar similar, but put all entries made for index foo -% inside @code. -% -\def\synindex#1 #2 {\dosynindex\doindex{#1}{#2}} -\def\syncodeindex#1 #2 {\dosynindex\docodeindex{#1}{#2}} - -% #1 is \doindex or \docodeindex, #2 the index getting redefined (foo), -% #3 the target index (bar). -\def\dosynindex#1#2#3{% - % Only do \closeout if we haven't already done it, else we'll end up - % closing the target index. - \expandafter \ifx\csname donesynindex#2\endcsname \relax - % The \closeout helps reduce unnecessary open files; the limit on the - % Acorn RISC OS is a mere 16 files. - \expandafter\closeout\csname#2indfile\endcsname - \expandafter\let\csname donesynindex#2\endcsname = 1 - \fi - % redefine \fooindfile: - \expandafter\let\expandafter\temp\expandafter=\csname#3indfile\endcsname - \expandafter\let\csname#2indfile\endcsname=\temp - % redefine \fooindex: - \expandafter\xdef\csname#2index\endcsname{\noexpand#1{#3}}% -} - -% Define \doindex, the driver for all \fooindex macros. -% Argument #1 is generated by the calling \fooindex macro, -% and it is "foo", the name of the index. - -% \doindex just uses \parsearg; it calls \doind for the actual work. -% This is because \doind is more useful to call from other macros. - -% There is also \dosubind {index}{topic}{subtopic} -% which makes an entry in a two-level index such as the operation index. - -\def\doindex#1{\edef\indexname{#1}\parsearg\singleindexer} -\def\singleindexer #1{\doind{\indexname}{#1}} - -% like the previous two, but they put @code around the argument. -\def\docodeindex#1{\edef\indexname{#1}\parsearg\singlecodeindexer} -\def\singlecodeindexer #1{\doind{\indexname}{\code{#1}}} - -% Take care of Texinfo commands that can appear in an index entry. -% Since there are some commands we want to expand, and others we don't, -% we have to laboriously prevent expansion for those that we don't. -% -\def\indexdummies{% - \escapechar = `\\ % use backslash in output files. - \def\@{@}% change to @@ when we switch to @ as escape char in index files. - \def\ {\realbackslash\space }% - % - % Need these unexpandable (because we define \tt as a dummy) - % definitions when @{ or @} appear in index entry text. Also, more - % complicated, when \tex is in effect and \{ is a \delimiter again. - % We can't use \lbracecmd and \rbracecmd because texindex assumes - % braces and backslashes are used only as delimiters. Perhaps we - % should define @lbrace and @rbrace commands a la @comma. - \def\{{{\tt\char123}}% - \def\}{{\tt\char125}}% - % - % I don't entirely understand this, but when an index entry is - % generated from a macro call, the \endinput which \scanmacro inserts - % causes processing to be prematurely terminated. This is, - % apparently, because \indexsorttmp is fully expanded, and \endinput - % is an expandable command. The redefinition below makes \endinput - % disappear altogether for that purpose -- although logging shows that - % processing continues to some further point. On the other hand, it - % seems \endinput does not hurt in the printed index arg, since that - % is still getting written without apparent harm. - % - % Sample source (mac-idx3.tex, reported by Graham Percival to - % help-texinfo, 22may06): - % @macro funindex {WORD} - % @findex xyz - % @end macro - % ... - % @funindex commtest - % - % The above is not enough to reproduce the bug, but it gives the flavor. - % - % Sample whatsit resulting: - % .@write3{\entry{xyz}{@folio }{@code {xyz@endinput }}} - % - % So: - \let\endinput = \empty - % - % Do the redefinitions. - \commondummies -} - -% For the aux and toc files, @ is the escape character. So we want to -% redefine everything using @ as the escape character (instead of -% \realbackslash, still used for index files). When everything uses @, -% this will be simpler. -% -\def\atdummies{% - \def\@{@@}% - \def\ {@ }% - \let\{ = \lbraceatcmd - \let\} = \rbraceatcmd - % - % Do the redefinitions. - \commondummies - \otherbackslash -} - -% Called from \indexdummies and \atdummies. -% -\def\commondummies{% - % - % \definedummyword defines \#1 as \string\#1\space, thus effectively - % preventing its expansion. This is used only for control words, - % not control letters, because the \space would be incorrect for - % control characters, but is needed to separate the control word - % from whatever follows. - % - % For control letters, we have \definedummyletter, which omits the - % space. - % - % These can be used both for control words that take an argument and - % those that do not. If it is followed by {arg} in the input, then - % that will dutifully get written to the index (or wherever). - % - \def\definedummyword ##1{\def##1{\string##1\space}}% - \def\definedummyletter##1{\def##1{\string##1}}% - \let\definedummyaccent\definedummyletter - % - \commondummiesnofonts - % - \definedummyletter\_% - \definedummyletter\-% - % - % Non-English letters. - \definedummyword\AA - \definedummyword\AE - \definedummyword\DH - \definedummyword\L - \definedummyword\O - \definedummyword\OE - \definedummyword\TH - \definedummyword\aa - \definedummyword\ae - \definedummyword\dh - \definedummyword\exclamdown - \definedummyword\l - \definedummyword\o - \definedummyword\oe - \definedummyword\ordf - \definedummyword\ordm - \definedummyword\questiondown - \definedummyword\ss - \definedummyword\th - % - % Although these internal commands shouldn't show up, sometimes they do. - \definedummyword\bf - \definedummyword\gtr - \definedummyword\hat - \definedummyword\less - \definedummyword\sf - \definedummyword\sl - \definedummyword\tclose - \definedummyword\tt - % - \definedummyword\LaTeX - \definedummyword\TeX - % - % Assorted special characters. - \definedummyword\arrow - \definedummyword\bullet - \definedummyword\comma - \definedummyword\copyright - \definedummyword\registeredsymbol - \definedummyword\dots - \definedummyword\enddots - \definedummyword\entrybreak - \definedummyword\equiv - \definedummyword\error - \definedummyword\euro - \definedummyword\expansion - \definedummyword\geq - \definedummyword\guillemetleft - \definedummyword\guillemetright - \definedummyword\guilsinglleft - \definedummyword\guilsinglright - \definedummyword\leq - \definedummyword\minus - \definedummyword\ogonek - \definedummyword\pounds - \definedummyword\point - \definedummyword\print - \definedummyword\quotedblbase - \definedummyword\quotedblleft - \definedummyword\quotedblright - \definedummyword\quoteleft - \definedummyword\quoteright - \definedummyword\quotesinglbase - \definedummyword\result - \definedummyword\textdegree - % - % We want to disable all macros so that they are not expanded by \write. - \macrolist - % - \normalturnoffactive - % - % Handle some cases of @value -- where it does not contain any - % (non-fully-expandable) commands. - \makevalueexpandable -} - -% \commondummiesnofonts: common to \commondummies and \indexnofonts. -% -\def\commondummiesnofonts{% - % Control letters and accents. - \definedummyletter\!% - \definedummyaccent\"% - \definedummyaccent\'% - \definedummyletter\*% - \definedummyaccent\,% - \definedummyletter\.% - \definedummyletter\/% - \definedummyletter\:% - \definedummyaccent\=% - \definedummyletter\?% - \definedummyaccent\^% - \definedummyaccent\`% - \definedummyaccent\~% - \definedummyword\u - \definedummyword\v - \definedummyword\H - \definedummyword\dotaccent - \definedummyword\ogonek - \definedummyword\ringaccent - \definedummyword\tieaccent - \definedummyword\ubaraccent - \definedummyword\udotaccent - \definedummyword\dotless - % - % Texinfo font commands. - \definedummyword\b - \definedummyword\i - \definedummyword\r - \definedummyword\sansserif - \definedummyword\sc - \definedummyword\slanted - \definedummyword\t - % - % Commands that take arguments. - \definedummyword\acronym - \definedummyword\anchor - \definedummyword\cite - \definedummyword\code - \definedummyword\command - \definedummyword\dfn - \definedummyword\dmn - \definedummyword\email - \definedummyword\emph - \definedummyword\env - \definedummyword\file - \definedummyword\indicateurl - \definedummyword\kbd - \definedummyword\key - \definedummyword\math - \definedummyword\option - \definedummyword\pxref - \definedummyword\ref - \definedummyword\samp - \definedummyword\strong - \definedummyword\tie - \definedummyword\uref - \definedummyword\url - \definedummyword\var - \definedummyword\verb - \definedummyword\w - \definedummyword\xref -} - -% \indexnofonts is used when outputting the strings to sort the index -% by, and when constructing control sequence names. It eliminates all -% control sequences and just writes whatever the best ASCII sort string -% would be for a given command (usually its argument). -% -\def\indexnofonts{% - % Accent commands should become @asis. - \def\definedummyaccent##1{\let##1\asis}% - % We can just ignore other control letters. - \def\definedummyletter##1{\let##1\empty}% - % All control words become @asis by default; overrides below. - \let\definedummyword\definedummyaccent - % - \commondummiesnofonts - % - % Don't no-op \tt, since it isn't a user-level command - % and is used in the definitions of the active chars like <, >, |, etc. - % Likewise with the other plain tex font commands. - %\let\tt=\asis - % - \def\ { }% - \def\@{@}% - \def\_{\normalunderscore}% - \def\-{}% @- shouldn't affect sorting - % - % Unfortunately, texindex is not prepared to handle braces in the - % content at all. So for index sorting, we map @{ and @} to strings - % starting with |, since that ASCII character is between ASCII { and }. - \def\{{|a}% - \def\}{|b}% - % - % Non-English letters. - \def\AA{AA}% - \def\AE{AE}% - \def\DH{DZZ}% - \def\L{L}% - \def\OE{OE}% - \def\O{O}% - \def\TH{ZZZ}% - \def\aa{aa}% - \def\ae{ae}% - \def\dh{dzz}% - \def\exclamdown{!}% - \def\l{l}% - \def\oe{oe}% - \def\ordf{a}% - \def\ordm{o}% - \def\o{o}% - \def\questiondown{?}% - \def\ss{ss}% - \def\th{zzz}% - % - \def\LaTeX{LaTeX}% - \def\TeX{TeX}% - % - % Assorted special characters. - % (The following {} will end up in the sort string, but that's ok.) - \def\arrow{->}% - \def\bullet{bullet}% - \def\comma{,}% - \def\copyright{copyright}% - \def\dots{...}% - \def\enddots{...}% - \def\equiv{==}% - \def\error{error}% - \def\euro{euro}% - \def\expansion{==>}% - \def\geq{>=}% - \def\guillemetleft{<<}% - \def\guillemetright{>>}% - \def\guilsinglleft{<}% - \def\guilsinglright{>}% - \def\leq{<=}% - \def\minus{-}% - \def\point{.}% - \def\pounds{pounds}% - \def\print{-|}% - \def\quotedblbase{"}% - \def\quotedblleft{"}% - \def\quotedblright{"}% - \def\quoteleft{`}% - \def\quoteright{'}% - \def\quotesinglbase{,}% - \def\registeredsymbol{R}% - \def\result{=>}% - \def\textdegree{o}% - % - \expandafter\ifx\csname SETtxiindexlquoteignore\endcsname\relax - \else \indexlquoteignore \fi - % - % We need to get rid of all macros, leaving only the arguments (if present). - % Of course this is not nearly correct, but it is the best we can do for now. - % makeinfo does not expand macros in the argument to @deffn, which ends up - % writing an index entry, and texindex isn't prepared for an index sort entry - % that starts with \. - % - % Since macro invocations are followed by braces, we can just redefine them - % to take a single TeX argument. The case of a macro invocation that - % goes to end-of-line is not handled. - % - \macrolist -} - -% Undocumented (for FSFS 2nd ed.): @set txiindexlquoteignore makes us -% ignore left quotes in the sort term. -{\catcode`\`=\active - \gdef\indexlquoteignore{\let`=\empty}} - -\let\indexbackslash=0 %overridden during \printindex. -\let\SETmarginindex=\relax % put index entries in margin (undocumented)? - -% Most index entries go through here, but \dosubind is the general case. -% #1 is the index name, #2 is the entry text. -\def\doind#1#2{\dosubind{#1}{#2}{}} - -% Workhorse for all \fooindexes. -% #1 is name of index, #2 is stuff to put there, #3 is subentry -- -% empty if called from \doind, as we usually are (the main exception -% is with most defuns, which call us directly). -% -\def\dosubind#1#2#3{% - \iflinks - {% - % Store the main index entry text (including the third arg). - \toks0 = {#2}% - % If third arg is present, precede it with a space. - \def\thirdarg{#3}% - \ifx\thirdarg\empty \else - \toks0 = \expandafter{\the\toks0 \space #3}% - \fi - % - \edef\writeto{\csname#1indfile\endcsname}% - % - \safewhatsit\dosubindwrite - }% - \fi -} - -% Write the entry in \toks0 to the index file: -% -\def\dosubindwrite{% - % Put the index entry in the margin if desired. - \ifx\SETmarginindex\relax\else - \insert\margin{\hbox{\vrule height8pt depth3pt width0pt \the\toks0}}% - \fi - % - % Remember, we are within a group. - \indexdummies % Must do this here, since \bf, etc expand at this stage - \def\backslashcurfont{\indexbackslash}% \indexbackslash isn't defined now - % so it will be output as is; and it will print as backslash. - % - % Process the index entry with all font commands turned off, to - % get the string to sort by. - {\indexnofonts - \edef\temp{\the\toks0}% need full expansion - \xdef\indexsorttmp{\temp}% - }% - % - % Set up the complete index entry, with both the sort key and - % the original text, including any font commands. We write - % three arguments to \entry to the .?? file (four in the - % subentry case), texindex reduces to two when writing the .??s - % sorted result. - \edef\temp{% - \write\writeto{% - \string\entry{\indexsorttmp}{\noexpand\folio}{\the\toks0}}% - }% - \temp -} - -% Take care of unwanted page breaks/skips around a whatsit: -% -% If a skip is the last thing on the list now, preserve it -% by backing up by \lastskip, doing the \write, then inserting -% the skip again. Otherwise, the whatsit generated by the -% \write or \pdfdest will make \lastskip zero. The result is that -% sequences like this: -% @end defun -% @tindex whatever -% @defun ... -% will have extra space inserted, because the \medbreak in the -% start of the @defun won't see the skip inserted by the @end of -% the previous defun. -% -% But don't do any of this if we're not in vertical mode. We -% don't want to do a \vskip and prematurely end a paragraph. -% -% Avoid page breaks due to these extra skips, too. -% -% But wait, there is a catch there: -% We'll have to check whether \lastskip is zero skip. \ifdim is not -% sufficient for this purpose, as it ignores stretch and shrink parts -% of the skip. The only way seems to be to check the textual -% representation of the skip. -% -% The following is almost like \def\zeroskipmacro{0.0pt} except that -% the ``p'' and ``t'' characters have catcode \other, not 11 (letter). -% -\edef\zeroskipmacro{\expandafter\the\csname z@skip\endcsname} -% -\newskip\whatsitskip -\newcount\whatsitpenalty -% -% ..., ready, GO: -% -\def\safewhatsit#1{\ifhmode - #1% - \else - % \lastskip and \lastpenalty cannot both be nonzero simultaneously. - \whatsitskip = \lastskip - \edef\lastskipmacro{\the\lastskip}% - \whatsitpenalty = \lastpenalty - % - % If \lastskip is nonzero, that means the last item was a - % skip. And since a skip is discardable, that means this - % -\whatsitskip glue we're inserting is preceded by a - % non-discardable item, therefore it is not a potential - % breakpoint, therefore no \nobreak needed. - \ifx\lastskipmacro\zeroskipmacro - \else - \vskip-\whatsitskip - \fi - % - #1% - % - \ifx\lastskipmacro\zeroskipmacro - % If \lastskip was zero, perhaps the last item was a penalty, and - % perhaps it was >=10000, e.g., a \nobreak. In that case, we want - % to re-insert the same penalty (values >10000 are used for various - % signals); since we just inserted a non-discardable item, any - % following glue (such as a \parskip) would be a breakpoint. For example: - % @deffn deffn-whatever - % @vindex index-whatever - % Description. - % would allow a break between the index-whatever whatsit - % and the "Description." paragraph. - \ifnum\whatsitpenalty>9999 \penalty\whatsitpenalty \fi - \else - % On the other hand, if we had a nonzero \lastskip, - % this make-up glue would be preceded by a non-discardable item - % (the whatsit from the \write), so we must insert a \nobreak. - \nobreak\vskip\whatsitskip - \fi -\fi} - -% The index entry written in the file actually looks like -% \entry {sortstring}{page}{topic} -% or -% \entry {sortstring}{page}{topic}{subtopic} -% The texindex program reads in these files and writes files -% containing these kinds of lines: -% \initial {c} -% before the first topic whose initial is c -% \entry {topic}{pagelist} -% for a topic that is used without subtopics -% \primary {topic} -% for the beginning of a topic that is used with subtopics -% \secondary {subtopic}{pagelist} -% for each subtopic. - -% Define the user-accessible indexing commands -% @findex, @vindex, @kindex, @cindex. - -\def\findex {\fnindex} -\def\kindex {\kyindex} -\def\cindex {\cpindex} -\def\vindex {\vrindex} -\def\tindex {\tpindex} -\def\pindex {\pgindex} - -\def\cindexsub {\begingroup\obeylines\cindexsub} -{\obeylines % -\gdef\cindexsub "#1" #2^^M{\endgroup % -\dosubind{cp}{#2}{#1}}} - -% Define the macros used in formatting output of the sorted index material. - -% @printindex causes a particular index (the ??s file) to get printed. -% It does not print any chapter heading (usually an @unnumbered). -% -\parseargdef\printindex{\begingroup - \dobreak \chapheadingskip{10000}% - % - \smallfonts \rm - \tolerance = 9500 - \plainfrenchspacing - \everypar = {}% don't want the \kern\-parindent from indentation suppression. - % - % See if the index file exists and is nonempty. - % Change catcode of @ here so that if the index file contains - % \initial {@} - % as its first line, TeX doesn't complain about mismatched braces - % (because it thinks @} is a control sequence). - \catcode`\@ = 11 - \openin 1 \jobname.#1s - \ifeof 1 - % \enddoublecolumns gets confused if there is no text in the index, - % and it loses the chapter title and the aux file entries for the - % index. The easiest way to prevent this problem is to make sure - % there is some text. - \putwordIndexNonexistent - \else - % - % If the index file exists but is empty, then \openin leaves \ifeof - % false. We have to make TeX try to read something from the file, so - % it can discover if there is anything in it. - \read 1 to \temp - \ifeof 1 - \putwordIndexIsEmpty - \else - % Index files are almost Texinfo source, but we use \ as the escape - % character. It would be better to use @, but that's too big a change - % to make right now. - \def\indexbackslash{\backslashcurfont}% - \catcode`\\ = 0 - \escapechar = `\\ - \begindoublecolumns - \input \jobname.#1s - \enddoublecolumns - \fi - \fi - \closein 1 -\endgroup} - -% These macros are used by the sorted index file itself. -% Change them to control the appearance of the index. - -\def\initial#1{{% - % Some minor font changes for the special characters. - \let\tentt=\sectt \let\tt=\sectt \let\sf=\sectt - % - % Remove any glue we may have, we'll be inserting our own. - \removelastskip - % - % We like breaks before the index initials, so insert a bonus. - \nobreak - \vskip 0pt plus 3\baselineskip - \penalty 0 - \vskip 0pt plus -3\baselineskip - % - % Typeset the initial. Making this add up to a whole number of - % baselineskips increases the chance of the dots lining up from column - % to column. It still won't often be perfect, because of the stretch - % we need before each entry, but it's better. - % - % No shrink because it confuses \balancecolumns. - \vskip 1.67\baselineskip plus .5\baselineskip - \leftline{\secbf #1}% - % Do our best not to break after the initial. - \nobreak - \vskip .33\baselineskip plus .1\baselineskip -}} - -% \entry typesets a paragraph consisting of the text (#1), dot leaders, and -% then page number (#2) flushed to the right margin. It is used for index -% and table of contents entries. The paragraph is indented by \leftskip. -% -% A straightforward implementation would start like this: -% \def\entry#1#2{... -% But this freezes the catcodes in the argument, and can cause problems to -% @code, which sets - active. This problem was fixed by a kludge--- -% ``-'' was active throughout whole index, but this isn't really right. -% The right solution is to prevent \entry from swallowing the whole text. -% --kasal, 21nov03 -\def\entry{% - \begingroup - % - % Start a new paragraph if necessary, so our assignments below can't - % affect previous text. - \par - % - % Do not fill out the last line with white space. - \parfillskip = 0in - % - % No extra space above this paragraph. - \parskip = 0in - % - % Do not prefer a separate line ending with a hyphen to fewer lines. - \finalhyphendemerits = 0 - % - % \hangindent is only relevant when the entry text and page number - % don't both fit on one line. In that case, bob suggests starting the - % dots pretty far over on the line. Unfortunately, a large - % indentation looks wrong when the entry text itself is broken across - % lines. So we use a small indentation and put up with long leaders. - % - % \hangafter is reset to 1 (which is the value we want) at the start - % of each paragraph, so we need not do anything with that. - \hangindent = 2em - % - % When the entry text needs to be broken, just fill out the first line - % with blank space. - \rightskip = 0pt plus1fil - % - % A bit of stretch before each entry for the benefit of balancing - % columns. - \vskip 0pt plus1pt - % - % When reading the text of entry, convert explicit line breaks - % from @* into spaces. The user might give these in long section - % titles, for instance. - \def\*{\unskip\space\ignorespaces}% - \def\entrybreak{\hfil\break}% - % - % Swallow the left brace of the text (first parameter): - \afterassignment\doentry - \let\temp = -} -\def\entrybreak{\unskip\space\ignorespaces}% -\def\doentry{% - \bgroup % Instead of the swallowed brace. - \noindent - \aftergroup\finishentry - % And now comes the text of the entry. -} -\def\finishentry#1{% - % #1 is the page number. - % - % The following is kludged to not output a line of dots in the index if - % there are no page numbers. The next person who breaks this will be - % cursed by a Unix daemon. - \setbox\boxA = \hbox{#1}% - \ifdim\wd\boxA = 0pt - \ % - \else - % - % If we must, put the page number on a line of its own, and fill out - % this line with blank space. (The \hfil is overwhelmed with the - % fill leaders glue in \indexdotfill if the page number does fit.) - \hfil\penalty50 - \null\nobreak\indexdotfill % Have leaders before the page number. - % - % The `\ ' here is removed by the implicit \unskip that TeX does as - % part of (the primitive) \par. Without it, a spurious underfull - % \hbox ensues. - \ifpdf - \pdfgettoks#1.% - \ \the\toksA - \else - \ #1% - \fi - \fi - \par - \endgroup -} - -% Like plain.tex's \dotfill, except uses up at least 1 em. -\def\indexdotfill{\cleaders - \hbox{$\mathsurround=0pt \mkern1.5mu.\mkern1.5mu$}\hskip 1em plus 1fill} - -\def\primary #1{\line{#1\hfil}} - -\newskip\secondaryindent \secondaryindent=0.5cm -\def\secondary#1#2{{% - \parfillskip=0in - \parskip=0in - \hangindent=1in - \hangafter=1 - \noindent\hskip\secondaryindent\hbox{#1}\indexdotfill - \ifpdf - \pdfgettoks#2.\ \the\toksA % The page number ends the paragraph. - \else - #2 - \fi - \par -}} - -% Define two-column mode, which we use to typeset indexes. -% Adapted from the TeXbook, page 416, which is to say, -% the manmac.tex format used to print the TeXbook itself. -\catcode`\@=11 - -\newbox\partialpage -\newdimen\doublecolumnhsize - -\def\begindoublecolumns{\begingroup % ended by \enddoublecolumns - % Grab any single-column material above us. - \output = {% - % - % Here is a possibility not foreseen in manmac: if we accumulate a - % whole lot of material, we might end up calling this \output - % routine twice in a row (see the doublecol-lose test, which is - % essentially a couple of indexes with @setchapternewpage off). In - % that case we just ship out what is in \partialpage with the normal - % output routine. Generally, \partialpage will be empty when this - % runs and this will be a no-op. See the indexspread.tex test case. - \ifvoid\partialpage \else - \onepageout{\pagecontents\partialpage}% - \fi - % - \global\setbox\partialpage = \vbox{% - % Unvbox the main output page. - \unvbox\PAGE - \kern-\topskip \kern\baselineskip - }% - }% - \eject % run that output routine to set \partialpage - % - % Use the double-column output routine for subsequent pages. - \output = {\doublecolumnout}% - % - % Change the page size parameters. We could do this once outside this - % routine, in each of @smallbook, @afourpaper, and the default 8.5x11 - % format, but then we repeat the same computation. Repeating a couple - % of assignments once per index is clearly meaningless for the - % execution time, so we may as well do it in one place. - % - % First we halve the line length, less a little for the gutter between - % the columns. We compute the gutter based on the line length, so it - % changes automatically with the paper format. The magic constant - % below is chosen so that the gutter has the same value (well, +-<1pt) - % as it did when we hard-coded it. - % - % We put the result in a separate register, \doublecolumhsize, so we - % can restore it in \pagesofar, after \hsize itself has (potentially) - % been clobbered. - % - \doublecolumnhsize = \hsize - \advance\doublecolumnhsize by -.04154\hsize - \divide\doublecolumnhsize by 2 - \hsize = \doublecolumnhsize - % - % Double the \vsize as well. (We don't need a separate register here, - % since nobody clobbers \vsize.) - \vsize = 2\vsize -} - -% The double-column output routine for all double-column pages except -% the last. -% -\def\doublecolumnout{% - \splittopskip=\topskip \splitmaxdepth=\maxdepth - % Get the available space for the double columns -- the normal - % (undoubled) page height minus any material left over from the - % previous page. - \dimen@ = \vsize - \divide\dimen@ by 2 - \advance\dimen@ by -\ht\partialpage - % - % box0 will be the left-hand column, box2 the right. - \setbox0=\vsplit255 to\dimen@ \setbox2=\vsplit255 to\dimen@ - \onepageout\pagesofar - \unvbox255 - \penalty\outputpenalty -} -% -% Re-output the contents of the output page -- any previous material, -% followed by the two boxes we just split, in box0 and box2. -\def\pagesofar{% - \unvbox\partialpage - % - \hsize = \doublecolumnhsize - \wd0=\hsize \wd2=\hsize - \hbox to\pagewidth{\box0\hfil\box2}% -} -% -% All done with double columns. -\def\enddoublecolumns{% - % The following penalty ensures that the page builder is exercised - % _before_ we change the output routine. This is necessary in the - % following situation: - % - % The last section of the index consists only of a single entry. - % Before this section, \pagetotal is less than \pagegoal, so no - % break occurs before the last section starts. However, the last - % section, consisting of \initial and the single \entry, does not - % fit on the page and has to be broken off. Without the following - % penalty the page builder will not be exercised until \eject - % below, and by that time we'll already have changed the output - % routine to the \balancecolumns version, so the next-to-last - % double-column page will be processed with \balancecolumns, which - % is wrong: The two columns will go to the main vertical list, with - % the broken-off section in the recent contributions. As soon as - % the output routine finishes, TeX starts reconsidering the page - % break. The two columns and the broken-off section both fit on the - % page, because the two columns now take up only half of the page - % goal. When TeX sees \eject from below which follows the final - % section, it invokes the new output routine that we've set after - % \balancecolumns below; \onepageout will try to fit the two columns - % and the final section into the vbox of \pageheight (see - % \pagebody), causing an overfull box. - % - % Note that glue won't work here, because glue does not exercise the - % page builder, unlike penalties (see The TeXbook, pp. 280-281). - \penalty0 - % - \output = {% - % Split the last of the double-column material. Leave it on the - % current page, no automatic page break. - \balancecolumns - % - % If we end up splitting too much material for the current page, - % though, there will be another page break right after this \output - % invocation ends. Having called \balancecolumns once, we do not - % want to call it again. Therefore, reset \output to its normal - % definition right away. (We hope \balancecolumns will never be - % called on to balance too much material, but if it is, this makes - % the output somewhat more palatable.) - \global\output = {\onepageout{\pagecontents\PAGE}}% - }% - \eject - \endgroup % started in \begindoublecolumns - % - % \pagegoal was set to the doubled \vsize above, since we restarted - % the current page. We're now back to normal single-column - % typesetting, so reset \pagegoal to the normal \vsize (after the - % \endgroup where \vsize got restored). - \pagegoal = \vsize -} -% -% Called at the end of the double column material. -\def\balancecolumns{% - \setbox0 = \vbox{\unvbox255}% like \box255 but more efficient, see p.120. - \dimen@ = \ht0 - \advance\dimen@ by \topskip - \advance\dimen@ by-\baselineskip - \divide\dimen@ by 2 % target to split to - %debug\message{final 2-column material height=\the\ht0, target=\the\dimen@.}% - \splittopskip = \topskip - % Loop until we get a decent breakpoint. - {% - \vbadness = 10000 - \loop - \global\setbox3 = \copy0 - \global\setbox1 = \vsplit3 to \dimen@ - \ifdim\ht3>\dimen@ - \global\advance\dimen@ by 1pt - \repeat - }% - %debug\message{split to \the\dimen@, column heights: \the\ht1, \the\ht3.}% - \setbox0=\vbox to\dimen@{\unvbox1}% - \setbox2=\vbox to\dimen@{\unvbox3}% - % - \pagesofar -} -\catcode`\@ = \other - - -\message{sectioning,} -% Chapters, sections, etc. - -% Let's start with @part. -\outer\parseargdef\part{\partzzz{#1}} -\def\partzzz#1{% - \chapoddpage - \null - \vskip.3\vsize % move it down on the page a bit - \begingroup - \noindent \titlefonts\rmisbold #1\par % the text - \let\lastnode=\empty % no node to associate with - \writetocentry{part}{#1}{}% but put it in the toc - \headingsoff % no headline or footline on the part page - \chapoddpage - \endgroup -} - -% \unnumberedno is an oxymoron. But we count the unnumbered -% sections so that we can refer to them unambiguously in the pdf -% outlines by their "section number". We avoid collisions with chapter -% numbers by starting them at 10000. (If a document ever has 10000 -% chapters, we're in trouble anyway, I'm sure.) -\newcount\unnumberedno \unnumberedno = 10000 -\newcount\chapno -\newcount\secno \secno=0 -\newcount\subsecno \subsecno=0 -\newcount\subsubsecno \subsubsecno=0 - -% This counter is funny since it counts through charcodes of letters A, B, ... -\newcount\appendixno \appendixno = `\@ -% -% \def\appendixletter{\char\the\appendixno} -% We do the following ugly conditional instead of the above simple -% construct for the sake of pdftex, which needs the actual -% letter in the expansion, not just typeset. -% -\def\appendixletter{% - \ifnum\appendixno=`A A% - \else\ifnum\appendixno=`B B% - \else\ifnum\appendixno=`C C% - \else\ifnum\appendixno=`D D% - \else\ifnum\appendixno=`E E% - \else\ifnum\appendixno=`F F% - \else\ifnum\appendixno=`G G% - \else\ifnum\appendixno=`H H% - \else\ifnum\appendixno=`I I% - \else\ifnum\appendixno=`J J% - \else\ifnum\appendixno=`K K% - \else\ifnum\appendixno=`L L% - \else\ifnum\appendixno=`M M% - \else\ifnum\appendixno=`N N% - \else\ifnum\appendixno=`O O% - \else\ifnum\appendixno=`P P% - \else\ifnum\appendixno=`Q Q% - \else\ifnum\appendixno=`R R% - \else\ifnum\appendixno=`S S% - \else\ifnum\appendixno=`T T% - \else\ifnum\appendixno=`U U% - \else\ifnum\appendixno=`V V% - \else\ifnum\appendixno=`W W% - \else\ifnum\appendixno=`X X% - \else\ifnum\appendixno=`Y Y% - \else\ifnum\appendixno=`Z Z% - % The \the is necessary, despite appearances, because \appendixletter is - % expanded while writing the .toc file. \char\appendixno is not - % expandable, thus it is written literally, thus all appendixes come out - % with the same letter (or @) in the toc without it. - \else\char\the\appendixno - \fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi - \fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi\fi} - -% Each @chapter defines these (using marks) as the number+name, number -% and name of the chapter. Page headings and footings can use -% these. @section does likewise. -\def\thischapter{} -\def\thischapternum{} -\def\thischaptername{} -\def\thissection{} -\def\thissectionnum{} -\def\thissectionname{} - -\newcount\absseclevel % used to calculate proper heading level -\newcount\secbase\secbase=0 % @raisesections/@lowersections modify this count - -% @raisesections: treat @section as chapter, @subsection as section, etc. -\def\raisesections{\global\advance\secbase by -1} -\let\up=\raisesections % original BFox name - -% @lowersections: treat @chapter as section, @section as subsection, etc. -\def\lowersections{\global\advance\secbase by 1} -\let\down=\lowersections % original BFox name - -% we only have subsub. -\chardef\maxseclevel = 3 -% -% A numbered section within an unnumbered changes to unnumbered too. -% To achieve this, remember the "biggest" unnum. sec. we are currently in: -\chardef\unnlevel = \maxseclevel -% -% Trace whether the current chapter is an appendix or not: -% \chapheadtype is "N" or "A", unnumbered chapters are ignored. -\def\chapheadtype{N} - -% Choose a heading macro -% #1 is heading type -% #2 is heading level -% #3 is text for heading -\def\genhead#1#2#3{% - % Compute the abs. sec. level: - \absseclevel=#2 - \advance\absseclevel by \secbase - % Make sure \absseclevel doesn't fall outside the range: - \ifnum \absseclevel < 0 - \absseclevel = 0 - \else - \ifnum \absseclevel > 3 - \absseclevel = 3 - \fi - \fi - % The heading type: - \def\headtype{#1}% - \if \headtype U% - \ifnum \absseclevel < \unnlevel - \chardef\unnlevel = \absseclevel - \fi - \else - % Check for appendix sections: - \ifnum \absseclevel = 0 - \edef\chapheadtype{\headtype}% - \else - \if \headtype A\if \chapheadtype N% - \errmessage{@appendix... within a non-appendix chapter}% - \fi\fi - \fi - % Check for numbered within unnumbered: - \ifnum \absseclevel > \unnlevel - \def\headtype{U}% - \else - \chardef\unnlevel = 3 - \fi - \fi - % Now print the heading: - \if \headtype U% - \ifcase\absseclevel - \unnumberedzzz{#3}% - \or \unnumberedseczzz{#3}% - \or \unnumberedsubseczzz{#3}% - \or \unnumberedsubsubseczzz{#3}% - \fi - \else - \if \headtype A% - \ifcase\absseclevel - \appendixzzz{#3}% - \or \appendixsectionzzz{#3}% - \or \appendixsubseczzz{#3}% - \or \appendixsubsubseczzz{#3}% - \fi - \else - \ifcase\absseclevel - \chapterzzz{#3}% - \or \seczzz{#3}% - \or \numberedsubseczzz{#3}% - \or \numberedsubsubseczzz{#3}% - \fi - \fi - \fi - \suppressfirstparagraphindent -} - -% an interface: -\def\numhead{\genhead N} -\def\apphead{\genhead A} -\def\unnmhead{\genhead U} - -% @chapter, @appendix, @unnumbered. Increment top-level counter, reset -% all lower-level sectioning counters to zero. -% -% Also set \chaplevelprefix, which we prepend to @float sequence numbers -% (e.g., figures), q.v. By default (before any chapter), that is empty. -\let\chaplevelprefix = \empty -% -\outer\parseargdef\chapter{\numhead0{#1}} % normally numhead0 calls chapterzzz -\def\chapterzzz#1{% - % section resetting is \global in case the chapter is in a group, such - % as an @include file. - \global\secno=0 \global\subsecno=0 \global\subsubsecno=0 - \global\advance\chapno by 1 - % - % Used for \float. - \gdef\chaplevelprefix{\the\chapno.}% - \resetallfloatnos - % - % \putwordChapter can contain complex things in translations. - \toks0=\expandafter{\putwordChapter}% - \message{\the\toks0 \space \the\chapno}% - % - % Write the actual heading. - \chapmacro{#1}{Ynumbered}{\the\chapno}% - % - % So @section and the like are numbered underneath this chapter. - \global\let\section = \numberedsec - \global\let\subsection = \numberedsubsec - \global\let\subsubsection = \numberedsubsubsec -} - -\outer\parseargdef\appendix{\apphead0{#1}} % normally calls appendixzzz -% -\def\appendixzzz#1{% - \global\secno=0 \global\subsecno=0 \global\subsubsecno=0 - \global\advance\appendixno by 1 - \gdef\chaplevelprefix{\appendixletter.}% - \resetallfloatnos - % - % \putwordAppendix can contain complex things in translations. - \toks0=\expandafter{\putwordAppendix}% - \message{\the\toks0 \space \appendixletter}% - % - \chapmacro{#1}{Yappendix}{\appendixletter}% - % - \global\let\section = \appendixsec - \global\let\subsection = \appendixsubsec - \global\let\subsubsection = \appendixsubsubsec -} - -% normally unnmhead0 calls unnumberedzzz: -\outer\parseargdef\unnumbered{\unnmhead0{#1}} -\def\unnumberedzzz#1{% - \global\secno=0 \global\subsecno=0 \global\subsubsecno=0 - \global\advance\unnumberedno by 1 - % - % Since an unnumbered has no number, no prefix for figures. - \global\let\chaplevelprefix = \empty - \resetallfloatnos - % - % This used to be simply \message{#1}, but TeX fully expands the - % argument to \message. Therefore, if #1 contained @-commands, TeX - % expanded them. For example, in `@unnumbered The @cite{Book}', TeX - % expanded @cite (which turns out to cause errors because \cite is meant - % to be executed, not expanded). - % - % Anyway, we don't want the fully-expanded definition of @cite to appear - % as a result of the \message, we just want `@cite' itself. We use - % \the to achieve this: TeX expands \the only once, - % simply yielding the contents of . (We also do this for - % the toc entries.) - \toks0 = {#1}% - \message{(\the\toks0)}% - % - \chapmacro{#1}{Ynothing}{\the\unnumberedno}% - % - \global\let\section = \unnumberedsec - \global\let\subsection = \unnumberedsubsec - \global\let\subsubsection = \unnumberedsubsubsec -} - -% @centerchap is like @unnumbered, but the heading is centered. -\outer\parseargdef\centerchap{% - % Well, we could do the following in a group, but that would break - % an assumption that \chapmacro is called at the outermost level. - % Thus we are safer this way: --kasal, 24feb04 - \let\centerparametersmaybe = \centerparameters - \unnmhead0{#1}% - \let\centerparametersmaybe = \relax -} - -% @top is like @unnumbered. -\let\top\unnumbered - -% Sections. -% -\outer\parseargdef\numberedsec{\numhead1{#1}} % normally calls seczzz -\def\seczzz#1{% - \global\subsecno=0 \global\subsubsecno=0 \global\advance\secno by 1 - \sectionheading{#1}{sec}{Ynumbered}{\the\chapno.\the\secno}% -} - -% normally calls appendixsectionzzz: -\outer\parseargdef\appendixsection{\apphead1{#1}} -\def\appendixsectionzzz#1{% - \global\subsecno=0 \global\subsubsecno=0 \global\advance\secno by 1 - \sectionheading{#1}{sec}{Yappendix}{\appendixletter.\the\secno}% -} -\let\appendixsec\appendixsection - -% normally calls unnumberedseczzz: -\outer\parseargdef\unnumberedsec{\unnmhead1{#1}} -\def\unnumberedseczzz#1{% - \global\subsecno=0 \global\subsubsecno=0 \global\advance\secno by 1 - \sectionheading{#1}{sec}{Ynothing}{\the\unnumberedno.\the\secno}% -} - -% Subsections. -% -% normally calls numberedsubseczzz: -\outer\parseargdef\numberedsubsec{\numhead2{#1}} -\def\numberedsubseczzz#1{% - \global\subsubsecno=0 \global\advance\subsecno by 1 - \sectionheading{#1}{subsec}{Ynumbered}{\the\chapno.\the\secno.\the\subsecno}% -} - -% normally calls appendixsubseczzz: -\outer\parseargdef\appendixsubsec{\apphead2{#1}} -\def\appendixsubseczzz#1{% - \global\subsubsecno=0 \global\advance\subsecno by 1 - \sectionheading{#1}{subsec}{Yappendix}% - {\appendixletter.\the\secno.\the\subsecno}% -} - -% normally calls unnumberedsubseczzz: -\outer\parseargdef\unnumberedsubsec{\unnmhead2{#1}} -\def\unnumberedsubseczzz#1{% - \global\subsubsecno=0 \global\advance\subsecno by 1 - \sectionheading{#1}{subsec}{Ynothing}% - {\the\unnumberedno.\the\secno.\the\subsecno}% -} - -% Subsubsections. -% -% normally numberedsubsubseczzz: -\outer\parseargdef\numberedsubsubsec{\numhead3{#1}} -\def\numberedsubsubseczzz#1{% - \global\advance\subsubsecno by 1 - \sectionheading{#1}{subsubsec}{Ynumbered}% - {\the\chapno.\the\secno.\the\subsecno.\the\subsubsecno}% -} - -% normally appendixsubsubseczzz: -\outer\parseargdef\appendixsubsubsec{\apphead3{#1}} -\def\appendixsubsubseczzz#1{% - \global\advance\subsubsecno by 1 - \sectionheading{#1}{subsubsec}{Yappendix}% - {\appendixletter.\the\secno.\the\subsecno.\the\subsubsecno}% -} - -% normally unnumberedsubsubseczzz: -\outer\parseargdef\unnumberedsubsubsec{\unnmhead3{#1}} -\def\unnumberedsubsubseczzz#1{% - \global\advance\subsubsecno by 1 - \sectionheading{#1}{subsubsec}{Ynothing}% - {\the\unnumberedno.\the\secno.\the\subsecno.\the\subsubsecno}% -} - -% These macros control what the section commands do, according -% to what kind of chapter we are in (ordinary, appendix, or unnumbered). -% Define them by default for a numbered chapter. -\let\section = \numberedsec -\let\subsection = \numberedsubsec -\let\subsubsection = \numberedsubsubsec - -% Define @majorheading, @heading and @subheading - -% NOTE on use of \vbox for chapter headings, section headings, and such: -% 1) We use \vbox rather than the earlier \line to permit -% overlong headings to fold. -% 2) \hyphenpenalty is set to 10000 because hyphenation in a -% heading is obnoxious; this forbids it. -% 3) Likewise, headings look best if no \parindent is used, and -% if justification is not attempted. Hence \raggedright. - -\def\majorheading{% - {\advance\chapheadingskip by 10pt \chapbreak }% - \parsearg\chapheadingzzz -} - -\def\chapheading{\chapbreak \parsearg\chapheadingzzz} -\def\chapheadingzzz#1{% - {\chapfonts \vbox{\hyphenpenalty=10000\tolerance=5000 - \parindent=0pt\ptexraggedright - \rmisbold #1\hfill}}% - \bigskip \par\penalty 200\relax - \suppressfirstparagraphindent -} - -% @heading, @subheading, @subsubheading. -\parseargdef\heading{\sectionheading{#1}{sec}{Yomitfromtoc}{} - \suppressfirstparagraphindent} -\parseargdef\subheading{\sectionheading{#1}{subsec}{Yomitfromtoc}{} - \suppressfirstparagraphindent} -\parseargdef\subsubheading{\sectionheading{#1}{subsubsec}{Yomitfromtoc}{} - \suppressfirstparagraphindent} - -% These macros generate a chapter, section, etc. heading only -% (including whitespace, linebreaking, etc. around it), -% given all the information in convenient, parsed form. - -% Args are the skip and penalty (usually negative) -\def\dobreak#1#2{\par\ifdim\lastskip<#1\removelastskip\penalty#2\vskip#1\fi} - -% Parameter controlling skip before chapter headings (if needed) -\newskip\chapheadingskip - -% Define plain chapter starts, and page on/off switching for it. -\def\chapbreak{\dobreak \chapheadingskip {-4000}} -\def\chappager{\par\vfill\supereject} -% Because \domark is called before \chapoddpage, the filler page will -% get the headings for the next chapter, which is wrong. But we don't -% care -- we just disable all headings on the filler page. -\def\chapoddpage{% - \chappager - \ifodd\pageno \else - \begingroup - \headingsoff - \null - \chappager - \endgroup - \fi -} - -\def\setchapternewpage #1 {\csname CHAPPAG#1\endcsname} - -\def\CHAPPAGoff{% -\global\let\contentsalignmacro = \chappager -\global\let\pchapsepmacro=\chapbreak -\global\let\pagealignmacro=\chappager} - -\def\CHAPPAGon{% -\global\let\contentsalignmacro = \chappager -\global\let\pchapsepmacro=\chappager -\global\let\pagealignmacro=\chappager -\global\def\HEADINGSon{\HEADINGSsingle}} - -\def\CHAPPAGodd{% -\global\let\contentsalignmacro = \chapoddpage -\global\let\pchapsepmacro=\chapoddpage -\global\let\pagealignmacro=\chapoddpage -\global\def\HEADINGSon{\HEADINGSdouble}} - -\CHAPPAGon - -% Chapter opening. -% -% #1 is the text, #2 is the section type (Ynumbered, Ynothing, -% Yappendix, Yomitfromtoc), #3 the chapter number. -% -% To test against our argument. -\def\Ynothingkeyword{Ynothing} -\def\Yomitfromtockeyword{Yomitfromtoc} -\def\Yappendixkeyword{Yappendix} -% -\def\chapmacro#1#2#3{% - % Insert the first mark before the heading break (see notes for \domark). - \let\prevchapterdefs=\lastchapterdefs - \let\prevsectiondefs=\lastsectiondefs - \gdef\lastsectiondefs{\gdef\thissectionname{}\gdef\thissectionnum{}% - \gdef\thissection{}}% - % - \def\temptype{#2}% - \ifx\temptype\Ynothingkeyword - \gdef\lastchapterdefs{\gdef\thischaptername{#1}\gdef\thischapternum{}% - \gdef\thischapter{\thischaptername}}% - \else\ifx\temptype\Yomitfromtockeyword - \gdef\lastchapterdefs{\gdef\thischaptername{#1}\gdef\thischapternum{}% - \gdef\thischapter{}}% - \else\ifx\temptype\Yappendixkeyword - \toks0={#1}% - \xdef\lastchapterdefs{% - \gdef\noexpand\thischaptername{\the\toks0}% - \gdef\noexpand\thischapternum{\appendixletter}% - % \noexpand\putwordAppendix avoids expanding indigestible - % commands in some of the translations. - \gdef\noexpand\thischapter{\noexpand\putwordAppendix{} - \noexpand\thischapternum: - \noexpand\thischaptername}% - }% - \else - \toks0={#1}% - \xdef\lastchapterdefs{% - \gdef\noexpand\thischaptername{\the\toks0}% - \gdef\noexpand\thischapternum{\the\chapno}% - % \noexpand\putwordChapter avoids expanding indigestible - % commands in some of the translations. - \gdef\noexpand\thischapter{\noexpand\putwordChapter{} - \noexpand\thischapternum: - \noexpand\thischaptername}% - }% - \fi\fi\fi - % - % Output the mark. Pass it through \safewhatsit, to take care of - % the preceding space. - \safewhatsit\domark - % - % Insert the chapter heading break. - \pchapsepmacro - % - % Now the second mark, after the heading break. No break points - % between here and the heading. - \let\prevchapterdefs=\lastchapterdefs - \let\prevsectiondefs=\lastsectiondefs - \domark - % - {% - \chapfonts \rmisbold - % - % Have to define \lastsection before calling \donoderef, because the - % xref code eventually uses it. On the other hand, it has to be called - % after \pchapsepmacro, or the headline will change too soon. - \gdef\lastsection{#1}% - % - % Only insert the separating space if we have a chapter/appendix - % number, and don't print the unnumbered ``number''. - \ifx\temptype\Ynothingkeyword - \setbox0 = \hbox{}% - \def\toctype{unnchap}% - \else\ifx\temptype\Yomitfromtockeyword - \setbox0 = \hbox{}% contents like unnumbered, but no toc entry - \def\toctype{omit}% - \else\ifx\temptype\Yappendixkeyword - \setbox0 = \hbox{\putwordAppendix{} #3\enspace}% - \def\toctype{app}% - \else - \setbox0 = \hbox{#3\enspace}% - \def\toctype{numchap}% - \fi\fi\fi - % - % Write the toc entry for this chapter. Must come before the - % \donoderef, because we include the current node name in the toc - % entry, and \donoderef resets it to empty. - \writetocentry{\toctype}{#1}{#3}% - % - % For pdftex, we have to write out the node definition (aka, make - % the pdfdest) after any page break, but before the actual text has - % been typeset. If the destination for the pdf outline is after the - % text, then jumping from the outline may wind up with the text not - % being visible, for instance under high magnification. - \donoderef{#2}% - % - % Typeset the actual heading. - \nobreak % Avoid page breaks at the interline glue. - \vbox{\hyphenpenalty=10000 \tolerance=5000 \parindent=0pt \ptexraggedright - \hangindent=\wd0 \centerparametersmaybe - \unhbox0 #1\par}% - }% - \nobreak\bigskip % no page break after a chapter title - \nobreak -} - -% @centerchap -- centered and unnumbered. -\let\centerparametersmaybe = \relax -\def\centerparameters{% - \advance\rightskip by 3\rightskip - \leftskip = \rightskip - \parfillskip = 0pt -} - - -% I don't think this chapter style is supported any more, so I'm not -% updating it with the new noderef stuff. We'll see. --karl, 11aug03. -% -\def\setchapterstyle #1 {\csname CHAPF#1\endcsname} -% -\def\unnchfopen #1{% -\chapoddpage {\chapfonts \vbox{\hyphenpenalty=10000\tolerance=5000 - \parindent=0pt\ptexraggedright - \rmisbold #1\hfill}}\bigskip \par\nobreak -} -\def\chfopen #1#2{\chapoddpage {\chapfonts -\vbox to 3in{\vfil \hbox to\hsize{\hfil #2} \hbox to\hsize{\hfil #1} \vfil}}% -\par\penalty 5000 % -} -\def\centerchfopen #1{% -\chapoddpage {\chapfonts \vbox{\hyphenpenalty=10000\tolerance=5000 - \parindent=0pt - \hfill {\rmisbold #1}\hfill}}\bigskip \par\nobreak -} -\def\CHAPFopen{% - \global\let\chapmacro=\chfopen - \global\let\centerchapmacro=\centerchfopen} - - -% Section titles. These macros combine the section number parts and -% call the generic \sectionheading to do the printing. -% -\newskip\secheadingskip -\def\secheadingbreak{\dobreak \secheadingskip{-1000}} - -% Subsection titles. -\newskip\subsecheadingskip -\def\subsecheadingbreak{\dobreak \subsecheadingskip{-500}} - -% Subsubsection titles. -\def\subsubsecheadingskip{\subsecheadingskip} -\def\subsubsecheadingbreak{\subsecheadingbreak} - - -% Print any size, any type, section title. -% -% #1 is the text, #2 is the section level (sec/subsec/subsubsec), #3 is -% the section type for xrefs (Ynumbered, Ynothing, Yappendix), #4 is the -% section number. -% -\def\seckeyword{sec} -% -\def\sectionheading#1#2#3#4{% - {% - \checkenv{}% should not be in an environment. - % - % Switch to the right set of fonts. - \csname #2fonts\endcsname \rmisbold - % - \def\sectionlevel{#2}% - \def\temptype{#3}% - % - % Insert first mark before the heading break (see notes for \domark). - \let\prevsectiondefs=\lastsectiondefs - \ifx\temptype\Ynothingkeyword - \ifx\sectionlevel\seckeyword - \gdef\lastsectiondefs{\gdef\thissectionname{#1}\gdef\thissectionnum{}% - \gdef\thissection{\thissectionname}}% - \fi - \else\ifx\temptype\Yomitfromtockeyword - % Don't redefine \thissection. - \else\ifx\temptype\Yappendixkeyword - \ifx\sectionlevel\seckeyword - \toks0={#1}% - \xdef\lastsectiondefs{% - \gdef\noexpand\thissectionname{\the\toks0}% - \gdef\noexpand\thissectionnum{#4}% - % \noexpand\putwordSection avoids expanding indigestible - % commands in some of the translations. - \gdef\noexpand\thissection{\noexpand\putwordSection{} - \noexpand\thissectionnum: - \noexpand\thissectionname}% - }% - \fi - \else - \ifx\sectionlevel\seckeyword - \toks0={#1}% - \xdef\lastsectiondefs{% - \gdef\noexpand\thissectionname{\the\toks0}% - \gdef\noexpand\thissectionnum{#4}% - % \noexpand\putwordSection avoids expanding indigestible - % commands in some of the translations. - \gdef\noexpand\thissection{\noexpand\putwordSection{} - \noexpand\thissectionnum: - \noexpand\thissectionname}% - }% - \fi - \fi\fi\fi - % - % Go into vertical mode. Usually we'll already be there, but we - % don't want the following whatsit to end up in a preceding paragraph - % if the document didn't happen to have a blank line. - \par - % - % Output the mark. Pass it through \safewhatsit, to take care of - % the preceding space. - \safewhatsit\domark - % - % Insert space above the heading. - \csname #2headingbreak\endcsname - % - % Now the second mark, after the heading break. No break points - % between here and the heading. - \let\prevsectiondefs=\lastsectiondefs - \domark - % - % Only insert the space after the number if we have a section number. - \ifx\temptype\Ynothingkeyword - \setbox0 = \hbox{}% - \def\toctype{unn}% - \gdef\lastsection{#1}% - \else\ifx\temptype\Yomitfromtockeyword - % for @headings -- no section number, don't include in toc, - % and don't redefine \lastsection. - \setbox0 = \hbox{}% - \def\toctype{omit}% - \let\sectionlevel=\empty - \else\ifx\temptype\Yappendixkeyword - \setbox0 = \hbox{#4\enspace}% - \def\toctype{app}% - \gdef\lastsection{#1}% - \else - \setbox0 = \hbox{#4\enspace}% - \def\toctype{num}% - \gdef\lastsection{#1}% - \fi\fi\fi - % - % Write the toc entry (before \donoderef). See comments in \chapmacro. - \writetocentry{\toctype\sectionlevel}{#1}{#4}% - % - % Write the node reference (= pdf destination for pdftex). - % Again, see comments in \chapmacro. - \donoderef{#3}% - % - % Interline glue will be inserted when the vbox is completed. - % That glue will be a valid breakpoint for the page, since it'll be - % preceded by a whatsit (usually from the \donoderef, or from the - % \writetocentry if there was no node). We don't want to allow that - % break, since then the whatsits could end up on page n while the - % section is on page n+1, thus toc/etc. are wrong. Debian bug 276000. - \nobreak - % - % Output the actual section heading. - \vbox{\hyphenpenalty=10000 \tolerance=5000 \parindent=0pt \ptexraggedright - \hangindent=\wd0 % zero if no section number - \unhbox0 #1}% - }% - % Add extra space after the heading -- half of whatever came above it. - % Don't allow stretch, though. - \kern .5 \csname #2headingskip\endcsname - % - % Do not let the kern be a potential breakpoint, as it would be if it - % was followed by glue. - \nobreak - % - % We'll almost certainly start a paragraph next, so don't let that - % glue accumulate. (Not a breakpoint because it's preceded by a - % discardable item.) However, when a paragraph is not started next - % (\startdefun, \cartouche, \center, etc.), this needs to be wiped out - % or the negative glue will cause weirdly wrong output, typically - % obscuring the section heading with something else. - \vskip-\parskip - % - % This is so the last item on the main vertical list is a known - % \penalty > 10000, so \startdefun, etc., can recognize the situation - % and do the needful. - \penalty 10001 -} - - -\message{toc,} -% Table of contents. -\newwrite\tocfile - -% Write an entry to the toc file, opening it if necessary. -% Called from @chapter, etc. -% -% Example usage: \writetocentry{sec}{Section Name}{\the\chapno.\the\secno} -% We append the current node name (if any) and page number as additional -% arguments for the \{chap,sec,...}entry macros which will eventually -% read this. The node name is used in the pdf outlines as the -% destination to jump to. -% -% We open the .toc file for writing here instead of at @setfilename (or -% any other fixed time) so that @contents can be anywhere in the document. -% But if #1 is `omit', then we don't do anything. This is used for the -% table of contents chapter openings themselves. -% -\newif\iftocfileopened -\def\omitkeyword{omit}% -% -\def\writetocentry#1#2#3{% - \edef\writetoctype{#1}% - \ifx\writetoctype\omitkeyword \else - \iftocfileopened\else - \immediate\openout\tocfile = \jobname.toc - \global\tocfileopenedtrue - \fi - % - \iflinks - {\atdummies - \edef\temp{% - \write\tocfile{@#1entry{#2}{#3}{\lastnode}{\noexpand\folio}}}% - \temp - }% - \fi - \fi - % - % Tell \shipout to create a pdf destination on each page, if we're - % writing pdf. These are used in the table of contents. We can't - % just write one on every page because the title pages are numbered - % 1 and 2 (the page numbers aren't printed), and so are the first - % two pages of the document. Thus, we'd have two destinations named - % `1', and two named `2'. - \ifpdf \global\pdfmakepagedesttrue \fi -} - - -% These characters do not print properly in the Computer Modern roman -% fonts, so we must take special care. This is more or less redundant -% with the Texinfo input format setup at the end of this file. -% -\def\activecatcodes{% - \catcode`\"=\active - \catcode`\$=\active - \catcode`\<=\active - \catcode`\>=\active - \catcode`\\=\active - \catcode`\^=\active - \catcode`\_=\active - \catcode`\|=\active - \catcode`\~=\active -} - - -% Read the toc file, which is essentially Texinfo input. -\def\readtocfile{% - \setupdatafile - \activecatcodes - \input \tocreadfilename -} - -\newskip\contentsrightmargin \contentsrightmargin=1in -\newcount\savepageno -\newcount\lastnegativepageno \lastnegativepageno = -1 - -% Prepare to read what we've written to \tocfile. -% -\def\startcontents#1{% - % If @setchapternewpage on, and @headings double, the contents should - % start on an odd page, unlike chapters. Thus, we maintain - % \contentsalignmacro in parallel with \pagealignmacro. - % From: Torbjorn Granlund - \contentsalignmacro - \immediate\closeout\tocfile - % - % Don't need to put `Contents' or `Short Contents' in the headline. - % It is abundantly clear what they are. - \chapmacro{#1}{Yomitfromtoc}{}% - % - \savepageno = \pageno - \begingroup % Set up to handle contents files properly. - \raggedbottom % Worry more about breakpoints than the bottom. - \advance\hsize by -\contentsrightmargin % Don't use the full line length. - % - % Roman numerals for page numbers. - \ifnum \pageno>0 \global\pageno = \lastnegativepageno \fi -} - -% redefined for the two-volume lispref. We always output on -% \jobname.toc even if this is redefined. -% -\def\tocreadfilename{\jobname.toc} - -% Normal (long) toc. -% -\def\contents{% - \startcontents{\putwordTOC}% - \openin 1 \tocreadfilename\space - \ifeof 1 \else - \readtocfile - \fi - \vfill \eject - \contentsalignmacro % in case @setchapternewpage odd is in effect - \ifeof 1 \else - \pdfmakeoutlines - \fi - \closein 1 - \endgroup - \lastnegativepageno = \pageno - \global\pageno = \savepageno -} - -% And just the chapters. -\def\summarycontents{% - \startcontents{\putwordShortTOC}% - % - \let\partentry = \shortpartentry - \let\numchapentry = \shortchapentry - \let\appentry = \shortchapentry - \let\unnchapentry = \shortunnchapentry - % We want a true roman here for the page numbers. - \secfonts - \let\rm=\shortcontrm \let\bf=\shortcontbf - \let\sl=\shortcontsl \let\tt=\shortconttt - \rm - \hyphenpenalty = 10000 - \advance\baselineskip by 1pt % Open it up a little. - \def\numsecentry##1##2##3##4{} - \let\appsecentry = \numsecentry - \let\unnsecentry = \numsecentry - \let\numsubsecentry = \numsecentry - \let\appsubsecentry = \numsecentry - \let\unnsubsecentry = \numsecentry - \let\numsubsubsecentry = \numsecentry - \let\appsubsubsecentry = \numsecentry - \let\unnsubsubsecentry = \numsecentry - \openin 1 \tocreadfilename\space - \ifeof 1 \else - \readtocfile - \fi - \closein 1 - \vfill \eject - \contentsalignmacro % in case @setchapternewpage odd is in effect - \endgroup - \lastnegativepageno = \pageno - \global\pageno = \savepageno -} -\let\shortcontents = \summarycontents - -% Typeset the label for a chapter or appendix for the short contents. -% The arg is, e.g., `A' for an appendix, or `3' for a chapter. -% -\def\shortchaplabel#1{% - % This space should be enough, since a single number is .5em, and the - % widest letter (M) is 1em, at least in the Computer Modern fonts. - % But use \hss just in case. - % (This space doesn't include the extra space that gets added after - % the label; that gets put in by \shortchapentry above.) - % - % We'd like to right-justify chapter numbers, but that looks strange - % with appendix letters. And right-justifying numbers and - % left-justifying letters looks strange when there is less than 10 - % chapters. Have to read the whole toc once to know how many chapters - % there are before deciding ... - \hbox to 1em{#1\hss}% -} - -% These macros generate individual entries in the table of contents. -% The first argument is the chapter or section name. -% The last argument is the page number. -% The arguments in between are the chapter number, section number, ... - -% Parts, in the main contents. Replace the part number, which doesn't -% exist, with an empty box. Let's hope all the numbers have the same width. -% Also ignore the page number, which is conventionally not printed. -\def\numeralbox{\setbox0=\hbox{8}\hbox to \wd0{\hfil}} -\def\partentry#1#2#3#4{\dochapentry{\numeralbox\labelspace#1}{}} -% -% Parts, in the short toc. -\def\shortpartentry#1#2#3#4{% - \penalty-300 - \vskip.5\baselineskip plus.15\baselineskip minus.1\baselineskip - \shortchapentry{{\bf #1}}{\numeralbox}{}{}% -} - -% Chapters, in the main contents. -\def\numchapentry#1#2#3#4{\dochapentry{#2\labelspace#1}{#4}} -% -% Chapters, in the short toc. -% See comments in \dochapentry re vbox and related settings. -\def\shortchapentry#1#2#3#4{% - \tocentry{\shortchaplabel{#2}\labelspace #1}{\doshortpageno\bgroup#4\egroup}% -} - -% Appendices, in the main contents. -% Need the word Appendix, and a fixed-size box. -% -\def\appendixbox#1{% - % We use M since it's probably the widest letter. - \setbox0 = \hbox{\putwordAppendix{} M}% - \hbox to \wd0{\putwordAppendix{} #1\hss}} -% -\def\appentry#1#2#3#4{\dochapentry{\appendixbox{#2}\labelspace#1}{#4}} - -% Unnumbered chapters. -\def\unnchapentry#1#2#3#4{\dochapentry{#1}{#4}} -\def\shortunnchapentry#1#2#3#4{\tocentry{#1}{\doshortpageno\bgroup#4\egroup}} - -% Sections. -\def\numsecentry#1#2#3#4{\dosecentry{#2\labelspace#1}{#4}} -\let\appsecentry=\numsecentry -\def\unnsecentry#1#2#3#4{\dosecentry{#1}{#4}} - -% Subsections. -\def\numsubsecentry#1#2#3#4{\dosubsecentry{#2\labelspace#1}{#4}} -\let\appsubsecentry=\numsubsecentry -\def\unnsubsecentry#1#2#3#4{\dosubsecentry{#1}{#4}} - -% And subsubsections. -\def\numsubsubsecentry#1#2#3#4{\dosubsubsecentry{#2\labelspace#1}{#4}} -\let\appsubsubsecentry=\numsubsubsecentry -\def\unnsubsubsecentry#1#2#3#4{\dosubsubsecentry{#1}{#4}} - -% This parameter controls the indentation of the various levels. -% Same as \defaultparindent. -\newdimen\tocindent \tocindent = 15pt - -% Now for the actual typesetting. In all these, #1 is the text and #2 is the -% page number. -% -% If the toc has to be broken over pages, we want it to be at chapters -% if at all possible; hence the \penalty. -\def\dochapentry#1#2{% - \penalty-300 \vskip1\baselineskip plus.33\baselineskip minus.25\baselineskip - \begingroup - \chapentryfonts - \tocentry{#1}{\dopageno\bgroup#2\egroup}% - \endgroup - \nobreak\vskip .25\baselineskip plus.1\baselineskip -} - -\def\dosecentry#1#2{\begingroup - \secentryfonts \leftskip=\tocindent - \tocentry{#1}{\dopageno\bgroup#2\egroup}% -\endgroup} - -\def\dosubsecentry#1#2{\begingroup - \subsecentryfonts \leftskip=2\tocindent - \tocentry{#1}{\dopageno\bgroup#2\egroup}% -\endgroup} - -\def\dosubsubsecentry#1#2{\begingroup - \subsubsecentryfonts \leftskip=3\tocindent - \tocentry{#1}{\dopageno\bgroup#2\egroup}% -\endgroup} - -% We use the same \entry macro as for the index entries. -\let\tocentry = \entry - -% Space between chapter (or whatever) number and the title. -\def\labelspace{\hskip1em \relax} - -\def\dopageno#1{{\rm #1}} -\def\doshortpageno#1{{\rm #1}} - -\def\chapentryfonts{\secfonts \rm} -\def\secentryfonts{\textfonts} -\def\subsecentryfonts{\textfonts} -\def\subsubsecentryfonts{\textfonts} - - -\message{environments,} -% @foo ... @end foo. - -% @tex ... @end tex escapes into raw TeX temporarily. -% One exception: @ is still an escape character, so that @end tex works. -% But \@ or @@ will get a plain @ character. - -\envdef\tex{% - \setupmarkupstyle{tex}% - \catcode `\\=0 \catcode `\{=1 \catcode `\}=2 - \catcode `\$=3 \catcode `\&=4 \catcode `\#=6 - \catcode `\^=7 \catcode `\_=8 \catcode `\~=\active \let~=\tie - \catcode `\%=14 - \catcode `\+=\other - \catcode `\"=\other - \catcode `\|=\other - \catcode `\<=\other - \catcode `\>=\other - \catcode`\`=\other - \catcode`\'=\other - \escapechar=`\\ - % - % ' is active in math mode (mathcode"8000). So reset it, and all our - % other math active characters (just in case), to plain's definitions. - \mathactive - % - \let\b=\ptexb - \let\bullet=\ptexbullet - \let\c=\ptexc - \let\,=\ptexcomma - \let\.=\ptexdot - \let\dots=\ptexdots - \let\equiv=\ptexequiv - \let\!=\ptexexclam - \let\i=\ptexi - \let\indent=\ptexindent - \let\noindent=\ptexnoindent - \let\{=\ptexlbrace - \let\+=\tabalign - \let\}=\ptexrbrace - \let\/=\ptexslash - \let\*=\ptexstar - \let\t=\ptext - \expandafter \let\csname top\endcsname=\ptextop % outer - \let\frenchspacing=\plainfrenchspacing - % - \def\endldots{\mathinner{\ldots\ldots\ldots\ldots}}% - \def\enddots{\relax\ifmmode\endldots\else$\mathsurround=0pt \endldots\,$\fi}% - \def\@{@}% -} -% There is no need to define \Etex. - -% Define @lisp ... @end lisp. -% @lisp environment forms a group so it can rebind things, -% including the definition of @end lisp (which normally is erroneous). - -% Amount to narrow the margins by for @lisp. -\newskip\lispnarrowing \lispnarrowing=0.4in - -% This is the definition that ^^M gets inside @lisp, @example, and other -% such environments. \null is better than a space, since it doesn't -% have any width. -\def\lisppar{\null\endgraf} - -% This space is always present above and below environments. -\newskip\envskipamount \envskipamount = 0pt - -% Make spacing and below environment symmetrical. We use \parskip here -% to help in doing that, since in @example-like environments \parskip -% is reset to zero; thus the \afterenvbreak inserts no space -- but the -% start of the next paragraph will insert \parskip. -% -\def\aboveenvbreak{{% - % =10000 instead of <10000 because of a special case in \itemzzz and - % \sectionheading, q.v. - \ifnum \lastpenalty=10000 \else - \advance\envskipamount by \parskip - \endgraf - \ifdim\lastskip<\envskipamount - \removelastskip - % it's not a good place to break if the last penalty was \nobreak - % or better ... - \ifnum\lastpenalty<10000 \penalty-50 \fi - \vskip\envskipamount - \fi - \fi -}} - -\let\afterenvbreak = \aboveenvbreak - -% \nonarrowing is a flag. If "set", @lisp etc don't narrow margins; it will -% also clear it, so that its embedded environments do the narrowing again. -\let\nonarrowing=\relax - -% @cartouche ... @end cartouche: draw rectangle w/rounded corners around -% environment contents. -\font\circle=lcircle10 -\newdimen\circthick -\newdimen\cartouter\newdimen\cartinner -\newskip\normbskip\newskip\normpskip\newskip\normlskip -\circthick=\fontdimen8\circle -% -\def\ctl{{\circle\char'013\hskip -6pt}}% 6pt from pl file: 1/2charwidth -\def\ctr{{\hskip 6pt\circle\char'010}} -\def\cbl{{\circle\char'012\hskip -6pt}} -\def\cbr{{\hskip 6pt\circle\char'011}} -\def\carttop{\hbox to \cartouter{\hskip\lskip - \ctl\leaders\hrule height\circthick\hfil\ctr - \hskip\rskip}} -\def\cartbot{\hbox to \cartouter{\hskip\lskip - \cbl\leaders\hrule height\circthick\hfil\cbr - \hskip\rskip}} -% -\newskip\lskip\newskip\rskip - -\envdef\cartouche{% - \ifhmode\par\fi % can't be in the midst of a paragraph. - \startsavinginserts - \lskip=\leftskip \rskip=\rightskip - \leftskip=0pt\rightskip=0pt % we want these *outside*. - \cartinner=\hsize \advance\cartinner by-\lskip - \advance\cartinner by-\rskip - \cartouter=\hsize - \advance\cartouter by 18.4pt % allow for 3pt kerns on either - % side, and for 6pt waste from - % each corner char, and rule thickness - \normbskip=\baselineskip \normpskip=\parskip \normlskip=\lineskip - % Flag to tell @lisp, etc., not to narrow margin. - \let\nonarrowing = t% - % - % If this cartouche directly follows a sectioning command, we need the - % \parskip glue (backspaced over by default) or the cartouche can - % collide with the section heading. - \ifnum\lastpenalty>10000 \vskip\parskip \penalty\lastpenalty \fi - % - \vbox\bgroup - \baselineskip=0pt\parskip=0pt\lineskip=0pt - \carttop - \hbox\bgroup - \hskip\lskip - \vrule\kern3pt - \vbox\bgroup - \kern3pt - \hsize=\cartinner - \baselineskip=\normbskip - \lineskip=\normlskip - \parskip=\normpskip - \vskip -\parskip - \comment % For explanation, see the end of def\group. -} -\def\Ecartouche{% - \ifhmode\par\fi - \kern3pt - \egroup - \kern3pt\vrule - \hskip\rskip - \egroup - \cartbot - \egroup - \checkinserts -} - - -% This macro is called at the beginning of all the @example variants, -% inside a group. -\newdimen\nonfillparindent -\def\nonfillstart{% - \aboveenvbreak - \hfuzz = 12pt % Don't be fussy - \sepspaces % Make spaces be word-separators rather than space tokens. - \let\par = \lisppar % don't ignore blank lines - \obeylines % each line of input is a line of output - \parskip = 0pt - % Turn off paragraph indentation but redefine \indent to emulate - % the normal \indent. - \nonfillparindent=\parindent - \parindent = 0pt - \let\indent\nonfillindent - % - \emergencystretch = 0pt % don't try to avoid overfull boxes - \ifx\nonarrowing\relax - \advance \leftskip by \lispnarrowing - \exdentamount=\lispnarrowing - \else - \let\nonarrowing = \relax - \fi - \let\exdent=\nofillexdent -} - -\begingroup -\obeyspaces -% We want to swallow spaces (but not other tokens) after the fake -% @indent in our nonfill-environments, where spaces are normally -% active and set to @tie, resulting in them not being ignored after -% @indent. -\gdef\nonfillindent{\futurelet\temp\nonfillindentcheck}% -\gdef\nonfillindentcheck{% -\ifx\temp % -\expandafter\nonfillindentgobble% -\else% -\leavevmode\nonfillindentbox% -\fi% -}% -\endgroup -\def\nonfillindentgobble#1{\nonfillindent} -\def\nonfillindentbox{\hbox to \nonfillparindent{\hss}} - -% If you want all examples etc. small: @set dispenvsize small. -% If you want even small examples the full size: @set dispenvsize nosmall. -% This affects the following displayed environments: -% @example, @display, @format, @lisp -% -\def\smallword{small} -\def\nosmallword{nosmall} -\let\SETdispenvsize\relax -\def\setnormaldispenv{% - \ifx\SETdispenvsize\smallword - % end paragraph for sake of leading, in case document has no blank - % line. This is redundant with what happens in \aboveenvbreak, but - % we need to do it before changing the fonts, and it's inconvenient - % to change the fonts afterward. - \ifnum \lastpenalty=10000 \else \endgraf \fi - \smallexamplefonts \rm - \fi -} -\def\setsmalldispenv{% - \ifx\SETdispenvsize\nosmallword - \else - \ifnum \lastpenalty=10000 \else \endgraf \fi - \smallexamplefonts \rm - \fi -} - -% We often define two environments, @foo and @smallfoo. -% Let's do it in one command. #1 is the env name, #2 the definition. -\def\makedispenvdef#1#2{% - \expandafter\envdef\csname#1\endcsname {\setnormaldispenv #2}% - \expandafter\envdef\csname small#1\endcsname {\setsmalldispenv #2}% - \expandafter\let\csname E#1\endcsname \afterenvbreak - \expandafter\let\csname Esmall#1\endcsname \afterenvbreak -} - -% Define two environment synonyms (#1 and #2) for an environment. -\def\maketwodispenvdef#1#2#3{% - \makedispenvdef{#1}{#3}% - \makedispenvdef{#2}{#3}% -} -% -% @lisp: indented, narrowed, typewriter font; -% @example: same as @lisp. -% -% @smallexample and @smalllisp: use smaller fonts. -% Originally contributed by Pavel@xerox. -% -\maketwodispenvdef{lisp}{example}{% - \nonfillstart - \tt\setupmarkupstyle{example}% - \let\kbdfont = \kbdexamplefont % Allow @kbd to do something special. - \gobble % eat return -} -% @display/@smalldisplay: same as @lisp except keep current font. -% -\makedispenvdef{display}{% - \nonfillstart - \gobble -} - -% @format/@smallformat: same as @display except don't narrow margins. -% -\makedispenvdef{format}{% - \let\nonarrowing = t% - \nonfillstart - \gobble -} - -% @flushleft: same as @format, but doesn't obey \SETdispenvsize. -\envdef\flushleft{% - \let\nonarrowing = t% - \nonfillstart - \gobble -} -\let\Eflushleft = \afterenvbreak - -% @flushright. -% -\envdef\flushright{% - \let\nonarrowing = t% - \nonfillstart - \advance\leftskip by 0pt plus 1fill\relax - \gobble -} -\let\Eflushright = \afterenvbreak - - -% @raggedright does more-or-less normal line breaking but no right -% justification. From plain.tex. -\envdef\raggedright{% - \rightskip0pt plus2em \spaceskip.3333em \xspaceskip.5em\relax -} -\let\Eraggedright\par - -\envdef\raggedleft{% - \parindent=0pt \leftskip0pt plus2em - \spaceskip.3333em \xspaceskip.5em \parfillskip=0pt - \hbadness=10000 % Last line will usually be underfull, so turn off - % badness reporting. -} -\let\Eraggedleft\par - -\envdef\raggedcenter{% - \parindent=0pt \rightskip0pt plus1em \leftskip0pt plus1em - \spaceskip.3333em \xspaceskip.5em \parfillskip=0pt - \hbadness=10000 % Last line will usually be underfull, so turn off - % badness reporting. -} -\let\Eraggedcenter\par - - -% @quotation does normal linebreaking (hence we can't use \nonfillstart) -% and narrows the margins. We keep \parskip nonzero in general, since -% we're doing normal filling. So, when using \aboveenvbreak and -% \afterenvbreak, temporarily make \parskip 0. -% -\makedispenvdef{quotation}{\quotationstart} -% -\def\quotationstart{% - {\parskip=0pt \aboveenvbreak}% because \aboveenvbreak inserts \parskip - \parindent=0pt - % - % @cartouche defines \nonarrowing to inhibit narrowing at next level down. - \ifx\nonarrowing\relax - \advance\leftskip by \lispnarrowing - \advance\rightskip by \lispnarrowing - \exdentamount = \lispnarrowing - \else - \let\nonarrowing = \relax - \fi - \parsearg\quotationlabel -} - -% We have retained a nonzero parskip for the environment, since we're -% doing normal filling. -% -\def\Equotation{% - \par - \ifx\quotationauthor\thisisundefined\else - % indent a bit. - \leftline{\kern 2\leftskip \sl ---\quotationauthor}% - \fi - {\parskip=0pt \afterenvbreak}% -} -\def\Esmallquotation{\Equotation} - -% If we're given an argument, typeset it in bold with a colon after. -\def\quotationlabel#1{% - \def\temp{#1}% - \ifx\temp\empty \else - {\bf #1: }% - \fi -} - - -% LaTeX-like @verbatim...@end verbatim and @verb{...} -% If we want to allow any as delimiter, -% we need the curly braces so that makeinfo sees the @verb command, eg: -% `@verbx...x' would look like the '@verbx' command. --janneke@gnu.org -% -% [Knuth]: Donald Ervin Knuth, 1996. The TeXbook. -% -% [Knuth] p.344; only we need to do the other characters Texinfo sets -% active too. Otherwise, they get lost as the first character on a -% verbatim line. -\def\dospecials{% - \do\ \do\\\do\{\do\}\do\$\do\&% - \do\#\do\^\do\^^K\do\_\do\^^A\do\%\do\~% - \do\<\do\>\do\|\do\@\do+\do\"% - % Don't do the quotes -- if we do, @set txicodequoteundirected and - % @set txicodequotebacktick will not have effect on @verb and - % @verbatim, and ?` and !` ligatures won't get disabled. - %\do\`\do\'% -} -% -% [Knuth] p. 380 -\def\uncatcodespecials{% - \def\do##1{\catcode`##1=\other}\dospecials} -% -% Setup for the @verb command. -% -% Eight spaces for a tab -\begingroup - \catcode`\^^I=\active - \gdef\tabeightspaces{\catcode`\^^I=\active\def^^I{\ \ \ \ \ \ \ \ }} -\endgroup -% -\def\setupverb{% - \tt % easiest (and conventionally used) font for verbatim - \def\par{\leavevmode\endgraf}% - \setupmarkupstyle{verb}% - \tabeightspaces - % Respect line breaks, - % print special symbols as themselves, and - % make each space count - % must do in this order: - \obeylines \uncatcodespecials \sepspaces -} - -% Setup for the @verbatim environment -% -% Real tab expansion. -\newdimen\tabw \setbox0=\hbox{\tt\space} \tabw=8\wd0 % tab amount -% -% We typeset each line of the verbatim in an \hbox, so we can handle -% tabs. The \global is in case the verbatim line starts with an accent, -% or some other command that starts with a begin-group. Otherwise, the -% entire \verbbox would disappear at the corresponding end-group, before -% it is typeset. Meanwhile, we can't have nested verbatim commands -% (can we?), so the \global won't be overwriting itself. -\newbox\verbbox -\def\starttabbox{\global\setbox\verbbox=\hbox\bgroup} -% -\begingroup - \catcode`\^^I=\active - \gdef\tabexpand{% - \catcode`\^^I=\active - \def^^I{\leavevmode\egroup - \dimen\verbbox=\wd\verbbox % the width so far, or since the previous tab - \divide\dimen\verbbox by\tabw - \multiply\dimen\verbbox by\tabw % compute previous multiple of \tabw - \advance\dimen\verbbox by\tabw % advance to next multiple of \tabw - \wd\verbbox=\dimen\verbbox \box\verbbox \starttabbox - }% - } -\endgroup - -% start the verbatim environment. -\def\setupverbatim{% - \let\nonarrowing = t% - \nonfillstart - \tt % easiest (and conventionally used) font for verbatim - % The \leavevmode here is for blank lines. Otherwise, we would - % never \starttabox and the \egroup would end verbatim mode. - \def\par{\leavevmode\egroup\box\verbbox\endgraf}% - \tabexpand - \setupmarkupstyle{verbatim}% - % Respect line breaks, - % print special symbols as themselves, and - % make each space count. - % Must do in this order: - \obeylines \uncatcodespecials \sepspaces - \everypar{\starttabbox}% -} - -% Do the @verb magic: verbatim text is quoted by unique -% delimiter characters. Before first delimiter expect a -% right brace, after last delimiter expect closing brace: -% -% \def\doverb'{'#1'}'{#1} -% -% [Knuth] p. 382; only eat outer {} -\begingroup - \catcode`[=1\catcode`]=2\catcode`\{=\other\catcode`\}=\other - \gdef\doverb{#1[\def\next##1#1}[##1\endgroup]\next] -\endgroup -% -\def\verb{\begingroup\setupverb\doverb} -% -% -% Do the @verbatim magic: define the macro \doverbatim so that -% the (first) argument ends when '@end verbatim' is reached, ie: -% -% \def\doverbatim#1@end verbatim{#1} -% -% For Texinfo it's a lot easier than for LaTeX, -% because texinfo's \verbatim doesn't stop at '\end{verbatim}': -% we need not redefine '\', '{' and '}'. -% -% Inspired by LaTeX's verbatim command set [latex.ltx] -% -\begingroup - \catcode`\ =\active - \obeylines % - % ignore everything up to the first ^^M, that's the newline at the end - % of the @verbatim input line itself. Otherwise we get an extra blank - % line in the output. - \xdef\doverbatim#1^^M#2@end verbatim{#2\noexpand\end\gobble verbatim}% - % We really want {...\end verbatim} in the body of the macro, but - % without the active space; thus we have to use \xdef and \gobble. -\endgroup -% -\envdef\verbatim{% - \setupverbatim\doverbatim -} -\let\Everbatim = \afterenvbreak - - -% @verbatiminclude FILE - insert text of file in verbatim environment. -% -\def\verbatiminclude{\parseargusing\filenamecatcodes\doverbatiminclude} -% -\def\doverbatiminclude#1{% - {% - \makevalueexpandable - \setupverbatim - \indexnofonts % Allow `@@' and other weird things in file names. - \wlog{texinfo.tex: doing @verbatiminclude of #1^^J}% - \input #1 - \afterenvbreak - }% -} - -% @copying ... @end copying. -% Save the text away for @insertcopying later. -% -% We save the uninterpreted tokens, rather than creating a box. -% Saving the text in a box would be much easier, but then all the -% typesetting commands (@smallbook, font changes, etc.) have to be done -% beforehand -- and a) we want @copying to be done first in the source -% file; b) letting users define the frontmatter in as flexible order as -% possible is very desirable. -% -\def\copying{\checkenv{}\begingroup\scanargctxt\docopying} -\def\docopying#1@end copying{\endgroup\def\copyingtext{#1}} -% -\def\insertcopying{% - \begingroup - \parindent = 0pt % paragraph indentation looks wrong on title page - \scanexp\copyingtext - \endgroup -} - - -\message{defuns,} -% @defun etc. - -\newskip\defbodyindent \defbodyindent=.4in -\newskip\defargsindent \defargsindent=50pt -\newskip\deflastargmargin \deflastargmargin=18pt -\newcount\defunpenalty - -% Start the processing of @deffn: -\def\startdefun{% - \ifnum\lastpenalty<10000 - \medbreak - \defunpenalty=10003 % Will keep this @deffn together with the - % following @def command, see below. - \else - % If there are two @def commands in a row, we'll have a \nobreak, - % which is there to keep the function description together with its - % header. But if there's nothing but headers, we need to allow a - % break somewhere. Check specifically for penalty 10002, inserted - % by \printdefunline, instead of 10000, since the sectioning - % commands also insert a nobreak penalty, and we don't want to allow - % a break between a section heading and a defun. - % - % As a further refinement, we avoid "club" headers by signalling - % with penalty of 10003 after the very first @deffn in the - % sequence (see above), and penalty of 10002 after any following - % @def command. - \ifnum\lastpenalty=10002 \penalty2000 \else \defunpenalty=10002 \fi - % - % Similarly, after a section heading, do not allow a break. - % But do insert the glue. - \medskip % preceded by discardable penalty, so not a breakpoint - \fi - % - \parindent=0in - \advance\leftskip by \defbodyindent - \exdentamount=\defbodyindent -} - -\def\dodefunx#1{% - % First, check whether we are in the right environment: - \checkenv#1% - % - % As above, allow line break if we have multiple x headers in a row. - % It's not a great place, though. - \ifnum\lastpenalty=10002 \penalty3000 \else \defunpenalty=10002 \fi - % - % And now, it's time to reuse the body of the original defun: - \expandafter\gobbledefun#1% -} -\def\gobbledefun#1\startdefun{} - -% \printdefunline \deffnheader{text} -% -\def\printdefunline#1#2{% - \begingroup - % call \deffnheader: - #1#2 \endheader - % common ending: - \interlinepenalty = 10000 - \advance\rightskip by 0pt plus 1fil\relax - \endgraf - \nobreak\vskip -\parskip - \penalty\defunpenalty % signal to \startdefun and \dodefunx - % Some of the @defun-type tags do not enable magic parentheses, - % rendering the following check redundant. But we don't optimize. - \checkparencounts - \endgroup -} - -\def\Edefun{\endgraf\medbreak} - -% \makedefun{deffn} creates \deffn, \deffnx and \Edeffn; -% the only thing remaining is to define \deffnheader. -% -\def\makedefun#1{% - \expandafter\let\csname E#1\endcsname = \Edefun - \edef\temp{\noexpand\domakedefun - \makecsname{#1}\makecsname{#1x}\makecsname{#1header}}% - \temp -} - -% \domakedefun \deffn \deffnx \deffnheader -% -% Define \deffn and \deffnx, without parameters. -% \deffnheader has to be defined explicitly. -% -\def\domakedefun#1#2#3{% - \envdef#1{% - \startdefun - \doingtypefnfalse % distinguish typed functions from all else - \parseargusing\activeparens{\printdefunline#3}% - }% - \def#2{\dodefunx#1}% - \def#3% -} - -\newif\ifdoingtypefn % doing typed function? -\newif\ifrettypeownline % typeset return type on its own line? - -% @deftypefnnewline on|off says whether the return type of typed functions -% are printed on their own line. This affects @deftypefn, @deftypefun, -% @deftypeop, and @deftypemethod. -% -\parseargdef\deftypefnnewline{% - \def\temp{#1}% - \ifx\temp\onword - \expandafter\let\csname SETtxideftypefnnl\endcsname - = \empty - \else\ifx\temp\offword - \expandafter\let\csname SETtxideftypefnnl\endcsname - = \relax - \else - \errhelp = \EMsimple - \errmessage{Unknown @txideftypefnnl value `\temp', - must be on|off}% - \fi\fi -} - -% Untyped functions: - -% @deffn category name args -\makedefun{deffn}{\deffngeneral{}} - -% @deffn category class name args -\makedefun{defop}#1 {\defopon{#1\ \putwordon}} - -% \defopon {category on}class name args -\def\defopon#1#2 {\deffngeneral{\putwordon\ \code{#2}}{#1\ \code{#2}} } - -% \deffngeneral {subind}category name args -% -\def\deffngeneral#1#2 #3 #4\endheader{% - % Remember that \dosubind{fn}{foo}{} is equivalent to \doind{fn}{foo}. - \dosubind{fn}{\code{#3}}{#1}% - \defname{#2}{}{#3}\magicamp\defunargs{#4\unskip}% -} - -% Typed functions: - -% @deftypefn category type name args -\makedefun{deftypefn}{\deftypefngeneral{}} - -% @deftypeop category class type name args -\makedefun{deftypeop}#1 {\deftypeopon{#1\ \putwordon}} - -% \deftypeopon {category on}class type name args -\def\deftypeopon#1#2 {\deftypefngeneral{\putwordon\ \code{#2}}{#1\ \code{#2}} } - -% \deftypefngeneral {subind}category type name args -% -\def\deftypefngeneral#1#2 #3 #4 #5\endheader{% - \dosubind{fn}{\code{#4}}{#1}% - \doingtypefntrue - \defname{#2}{#3}{#4}\defunargs{#5\unskip}% -} - -% Typed variables: - -% @deftypevr category type var args -\makedefun{deftypevr}{\deftypecvgeneral{}} - -% @deftypecv category class type var args -\makedefun{deftypecv}#1 {\deftypecvof{#1\ \putwordof}} - -% \deftypecvof {category of}class type var args -\def\deftypecvof#1#2 {\deftypecvgeneral{\putwordof\ \code{#2}}{#1\ \code{#2}} } - -% \deftypecvgeneral {subind}category type var args -% -\def\deftypecvgeneral#1#2 #3 #4 #5\endheader{% - \dosubind{vr}{\code{#4}}{#1}% - \defname{#2}{#3}{#4}\defunargs{#5\unskip}% -} - -% Untyped variables: - -% @defvr category var args -\makedefun{defvr}#1 {\deftypevrheader{#1} {} } - -% @defcv category class var args -\makedefun{defcv}#1 {\defcvof{#1\ \putwordof}} - -% \defcvof {category of}class var args -\def\defcvof#1#2 {\deftypecvof{#1}#2 {} } - -% Types: - -% @deftp category name args -\makedefun{deftp}#1 #2 #3\endheader{% - \doind{tp}{\code{#2}}% - \defname{#1}{}{#2}\defunargs{#3\unskip}% -} - -% Remaining @defun-like shortcuts: -\makedefun{defun}{\deffnheader{\putwordDeffunc} } -\makedefun{defmac}{\deffnheader{\putwordDefmac} } -\makedefun{defspec}{\deffnheader{\putwordDefspec} } -\makedefun{deftypefun}{\deftypefnheader{\putwordDeffunc} } -\makedefun{defvar}{\defvrheader{\putwordDefvar} } -\makedefun{defopt}{\defvrheader{\putwordDefopt} } -\makedefun{deftypevar}{\deftypevrheader{\putwordDefvar} } -\makedefun{defmethod}{\defopon\putwordMethodon} -\makedefun{deftypemethod}{\deftypeopon\putwordMethodon} -\makedefun{defivar}{\defcvof\putwordInstanceVariableof} -\makedefun{deftypeivar}{\deftypecvof\putwordInstanceVariableof} - -% \defname, which formats the name of the @def (not the args). -% #1 is the category, such as "Function". -% #2 is the return type, if any. -% #3 is the function name. -% -% We are followed by (but not passed) the arguments, if any. -% -\def\defname#1#2#3{% - \par - % Get the values of \leftskip and \rightskip as they were outside the @def... - \advance\leftskip by -\defbodyindent - % - % Determine if we are typesetting the return type of a typed function - % on a line by itself. - \rettypeownlinefalse - \ifdoingtypefn % doing a typed function specifically? - % then check user option for putting return type on its own line: - \expandafter\ifx\csname SETtxideftypefnnl\endcsname\relax \else - \rettypeownlinetrue - \fi - \fi - % - % How we'll format the category name. Putting it in brackets helps - % distinguish it from the body text that may end up on the next line - % just below it. - \def\temp{#1}% - \setbox0=\hbox{\kern\deflastargmargin \ifx\temp\empty\else [\rm\temp]\fi} - % - % Figure out line sizes for the paragraph shape. We'll always have at - % least two. - \tempnum = 2 - % - % The first line needs space for \box0; but if \rightskip is nonzero, - % we need only space for the part of \box0 which exceeds it: - \dimen0=\hsize \advance\dimen0 by -\wd0 \advance\dimen0 by \rightskip - % - % If doing a return type on its own line, we'll have another line. - \ifrettypeownline - \advance\tempnum by 1 - \def\maybeshapeline{0in \hsize}% - \else - \def\maybeshapeline{}% - \fi - % - % The continuations: - \dimen2=\hsize \advance\dimen2 by -\defargsindent - % - % The final paragraph shape: - \parshape \tempnum 0in \dimen0 \maybeshapeline \defargsindent \dimen2 - % - % Put the category name at the right margin. - \noindent - \hbox to 0pt{% - \hfil\box0 \kern-\hsize - % \hsize has to be shortened this way: - \kern\leftskip - % Intentionally do not respect \rightskip, since we need the space. - }% - % - % Allow all lines to be underfull without complaint: - \tolerance=10000 \hbadness=10000 - \exdentamount=\defbodyindent - {% - % defun fonts. We use typewriter by default (used to be bold) because: - % . we're printing identifiers, they should be in tt in principle. - % . in languages with many accents, such as Czech or French, it's - % common to leave accents off identifiers. The result looks ok in - % tt, but exceedingly strange in rm. - % . we don't want -- and --- to be treated as ligatures. - % . this still does not fix the ?` and !` ligatures, but so far no - % one has made identifiers using them :). - \df \tt - \def\temp{#2}% text of the return type - \ifx\temp\empty\else - \tclose{\temp}% typeset the return type - \ifrettypeownline - % put return type on its own line; prohibit line break following: - \hfil\vadjust{\nobreak}\break - \else - \space % type on same line, so just followed by a space - \fi - \fi % no return type - #3% output function name - }% - {\rm\enskip}% hskip 0.5 em of \tenrm - % - \boldbrax - % arguments will be output next, if any. -} - -% Print arguments in slanted roman (not ttsl), inconsistently with using -% tt for the name. This is because literal text is sometimes needed in -% the argument list (groff manual), and ttsl and tt are not very -% distinguishable. Prevent hyphenation at `-' chars. -% -\def\defunargs#1{% - % use sl by default (not ttsl), - % tt for the names. - \df \sl \hyphenchar\font=0 - % - % On the other hand, if an argument has two dashes (for instance), we - % want a way to get ttsl. Let's try @var for that. - \def\var##1{{\setupmarkupstyle{var}\ttslanted{##1}}}% - #1% - \sl\hyphenchar\font=45 -} - -% We want ()&[] to print specially on the defun line. -% -\def\activeparens{% - \catcode`\(=\active \catcode`\)=\active - \catcode`\[=\active \catcode`\]=\active - \catcode`\&=\active -} - -% Make control sequences which act like normal parenthesis chars. -\let\lparen = ( \let\rparen = ) - -% Be sure that we always have a definition for `(', etc. For example, -% if the fn name has parens in it, \boldbrax will not be in effect yet, -% so TeX would otherwise complain about undefined control sequence. -{ - \activeparens - \global\let(=\lparen \global\let)=\rparen - \global\let[=\lbrack \global\let]=\rbrack - \global\let& = \& - - \gdef\boldbrax{\let(=\opnr\let)=\clnr\let[=\lbrb\let]=\rbrb} - \gdef\magicamp{\let&=\amprm} -} - -\newcount\parencount - -% If we encounter &foo, then turn on ()-hacking afterwards -\newif\ifampseen -\def\amprm#1 {\ampseentrue{\bf\ }} - -\def\parenfont{% - \ifampseen - % At the first level, print parens in roman, - % otherwise use the default font. - \ifnum \parencount=1 \rm \fi - \else - % The \sf parens (in \boldbrax) actually are a little bolder than - % the contained text. This is especially needed for [ and ] . - \sf - \fi -} -\def\infirstlevel#1{% - \ifampseen - \ifnum\parencount=1 - #1% - \fi - \fi -} -\def\bfafterword#1 {#1 \bf} - -\def\opnr{% - \global\advance\parencount by 1 - {\parenfont(}% - \infirstlevel \bfafterword -} -\def\clnr{% - {\parenfont)}% - \infirstlevel \sl - \global\advance\parencount by -1 -} - -\newcount\brackcount -\def\lbrb{% - \global\advance\brackcount by 1 - {\bf[}% -} -\def\rbrb{% - {\bf]}% - \global\advance\brackcount by -1 -} - -\def\checkparencounts{% - \ifnum\parencount=0 \else \badparencount \fi - \ifnum\brackcount=0 \else \badbrackcount \fi -} -% these should not use \errmessage; the glibc manual, at least, actually -% has such constructs (when documenting function pointers). -\def\badparencount{% - \message{Warning: unbalanced parentheses in @def...}% - \global\parencount=0 -} -\def\badbrackcount{% - \message{Warning: unbalanced square brackets in @def...}% - \global\brackcount=0 -} - - -\message{macros,} -% @macro. - -% To do this right we need a feature of e-TeX, \scantokens, -% which we arrange to emulate with a temporary file in ordinary TeX. -\ifx\eTeXversion\thisisundefined - \newwrite\macscribble - \def\scantokens#1{% - \toks0={#1}% - \immediate\openout\macscribble=\jobname.tmp - \immediate\write\macscribble{\the\toks0}% - \immediate\closeout\macscribble - \input \jobname.tmp - } -\fi - -\def\scanmacro#1{\begingroup - \newlinechar`\^^M - \let\xeatspaces\eatspaces - % - % Undo catcode changes of \startcontents and \doprintindex - % When called from @insertcopying or (short)caption, we need active - % backslash to get it printed correctly. Previously, we had - % \catcode`\\=\other instead. We'll see whether a problem appears - % with macro expansion. --kasal, 19aug04 - \catcode`\@=0 \catcode`\\=\active \escapechar=`\@ - % - % ... and for \example: - \spaceisspace - % - % The \empty here causes a following catcode 5 newline to be eaten as - % part of reading whitespace after a control sequence. It does not - % eat a catcode 13 newline. There's no good way to handle the two - % cases (untried: maybe e-TeX's \everyeof could help, though plain TeX - % would then have different behavior). See the Macro Details node in - % the manual for the workaround we recommend for macros and - % line-oriented commands. - % - \scantokens{#1\empty}% -\endgroup} - -\def\scanexp#1{% - \edef\temp{\noexpand\scanmacro{#1}}% - \temp -} - -\newcount\paramno % Count of parameters -\newtoks\macname % Macro name -\newif\ifrecursive % Is it recursive? - -% List of all defined macros in the form -% \definedummyword\macro1\definedummyword\macro2... -% Currently is also contains all @aliases; the list can be split -% if there is a need. -\def\macrolist{} - -% Add the macro to \macrolist -\def\addtomacrolist#1{\expandafter \addtomacrolistxxx \csname#1\endcsname} -\def\addtomacrolistxxx#1{% - \toks0 = \expandafter{\macrolist\definedummyword#1}% - \xdef\macrolist{\the\toks0}% -} - -% Utility routines. -% This does \let #1 = #2, with \csnames; that is, -% \let \csname#1\endcsname = \csname#2\endcsname -% (except of course we have to play expansion games). -% -\def\cslet#1#2{% - \expandafter\let - \csname#1\expandafter\endcsname - \csname#2\endcsname -} - -% Trim leading and trailing spaces off a string. -% Concepts from aro-bend problem 15 (see CTAN). -{\catcode`\@=11 -\gdef\eatspaces #1{\expandafter\trim@\expandafter{#1 }} -\gdef\trim@ #1{\trim@@ @#1 @ #1 @ @@} -\gdef\trim@@ #1@ #2@ #3@@{\trim@@@\empty #2 @} -\def\unbrace#1{#1} -\unbrace{\gdef\trim@@@ #1 } #2@{#1} -} - -% Trim a single trailing ^^M off a string. -{\catcode`\^^M=\other \catcode`\Q=3% -\gdef\eatcr #1{\eatcra #1Q^^MQ}% -\gdef\eatcra#1^^MQ{\eatcrb#1Q}% -\gdef\eatcrb#1Q#2Q{#1}% -} - -% Macro bodies are absorbed as an argument in a context where -% all characters are catcode 10, 11 or 12, except \ which is active -% (as in normal texinfo). It is necessary to change the definition of \ -% to recognize macro arguments; this is the job of \mbodybackslash. -% -% Non-ASCII encodings make 8-bit characters active, so un-activate -% them to avoid their expansion. Must do this non-globally, to -% confine the change to the current group. -% -% It's necessary to have hard CRs when the macro is executed. This is -% done by making ^^M (\endlinechar) catcode 12 when reading the macro -% body, and then making it the \newlinechar in \scanmacro. -% -\def\scanctxt{% used as subroutine - \catcode`\"=\other - \catcode`\+=\other - \catcode`\<=\other - \catcode`\>=\other - \catcode`\@=\other - \catcode`\^=\other - \catcode`\_=\other - \catcode`\|=\other - \catcode`\~=\other - \ifx\declaredencoding\ascii \else \setnonasciicharscatcodenonglobal\other \fi -} - -\def\scanargctxt{% used for copying and captions, not macros. - \scanctxt - \catcode`\\=\other - \catcode`\^^M=\other -} - -\def\macrobodyctxt{% used for @macro definitions - \scanctxt - \catcode`\{=\other - \catcode`\}=\other - \catcode`\^^M=\other - \usembodybackslash -} - -\def\macroargctxt{% used when scanning invocations - \scanctxt - \catcode`\\=0 -} -% why catcode 0 for \ in the above? To recognize \\ \{ \} as "escapes" -% for the single characters \ { }. Thus, we end up with the "commands" -% that would be written @\ @{ @} in a Texinfo document. -% -% We already have @{ and @}. For @\, we define it here, and only for -% this purpose, to produce a typewriter backslash (so, the @\ that we -% define for @math can't be used with @macro calls): -% -\def\\{\normalbackslash}% -% -% We would like to do this for \, too, since that is what makeinfo does. -% But it is not possible, because Texinfo already has a command @, for a -% cedilla accent. Documents must use @comma{} instead. -% -% \anythingelse will almost certainly be an error of some kind. - - -% \mbodybackslash is the definition of \ in @macro bodies. -% It maps \foo\ => \csname macarg.foo\endcsname => #N -% where N is the macro parameter number. -% We define \csname macarg.\endcsname to be \realbackslash, so -% \\ in macro replacement text gets you a backslash. -% -{\catcode`@=0 @catcode`@\=@active - @gdef@usembodybackslash{@let\=@mbodybackslash} - @gdef@mbodybackslash#1\{@csname macarg.#1@endcsname} -} -\expandafter\def\csname macarg.\endcsname{\realbackslash} - -\def\margbackslash#1{\char`\#1 } - -\def\macro{\recursivefalse\parsearg\macroxxx} -\def\rmacro{\recursivetrue\parsearg\macroxxx} - -\def\macroxxx#1{% - \getargs{#1}% now \macname is the macname and \argl the arglist - \ifx\argl\empty % no arguments - \paramno=0\relax - \else - \expandafter\parsemargdef \argl;% - \if\paramno>256\relax - \ifx\eTeXversion\thisisundefined - \errhelp = \EMsimple - \errmessage{You need eTeX to compile a file with macros with more than 256 arguments} - \fi - \fi - \fi - \if1\csname ismacro.\the\macname\endcsname - \message{Warning: redefining \the\macname}% - \else - \expandafter\ifx\csname \the\macname\endcsname \relax - \else \errmessage{Macro name \the\macname\space already defined}\fi - \global\cslet{macsave.\the\macname}{\the\macname}% - \global\expandafter\let\csname ismacro.\the\macname\endcsname=1% - \addtomacrolist{\the\macname}% - \fi - \begingroup \macrobodyctxt - \ifrecursive \expandafter\parsermacbody - \else \expandafter\parsemacbody - \fi} - -\parseargdef\unmacro{% - \if1\csname ismacro.#1\endcsname - \global\cslet{#1}{macsave.#1}% - \global\expandafter\let \csname ismacro.#1\endcsname=0% - % Remove the macro name from \macrolist: - \begingroup - \expandafter\let\csname#1\endcsname \relax - \let\definedummyword\unmacrodo - \xdef\macrolist{\macrolist}% - \endgroup - \else - \errmessage{Macro #1 not defined}% - \fi -} - -% Called by \do from \dounmacro on each macro. The idea is to omit any -% macro definitions that have been changed to \relax. -% -\def\unmacrodo#1{% - \ifx #1\relax - % remove this - \else - \noexpand\definedummyword \noexpand#1% - \fi -} - -% This makes use of the obscure feature that if the last token of a -% is #, then the preceding argument is delimited by -% an opening brace, and that opening brace is not consumed. -\def\getargs#1{\getargsxxx#1{}} -\def\getargsxxx#1#{\getmacname #1 \relax\getmacargs} -\def\getmacname#1 #2\relax{\macname={#1}} -\def\getmacargs#1{\def\argl{#1}} - -% For macro processing make @ a letter so that we can make Texinfo private macro names. -\edef\texiatcatcode{\the\catcode`\@} -\catcode `@=11\relax - -% Parse the optional {params} list. Set up \paramno and \paramlist -% so \defmacro knows what to do. Define \macarg.BLAH for each BLAH -% in the params list to some hook where the argument si to be expanded. If -% there are less than 10 arguments that hook is to be replaced by ##N where N -% is the position in that list, that is to say the macro arguments are to be -% defined `a la TeX in the macro body. -% -% That gets used by \mbodybackslash (above). -% -% We need to get `macro parameter char #' into several definitions. -% The technique used is stolen from LaTeX: let \hash be something -% unexpandable, insert that wherever you need a #, and then redefine -% it to # just before using the token list produced. -% -% The same technique is used to protect \eatspaces till just before -% the macro is used. -% -% If there are 10 or more arguments, a different technique is used, where the -% hook remains in the body, and when macro is to be expanded the body is -% processed again to replace the arguments. -% -% In that case, the hook is \the\toks N-1, and we simply set \toks N-1 to the -% argument N value and then \edef the body (nothing else will expand because of -% the catcode regime underwhich the body was input). -% -% If you compile with TeX (not eTeX), and you have macros with 10 or more -% arguments, you need that no macro has more than 256 arguments, otherwise an -% error is produced. -\def\parsemargdef#1;{% - \paramno=0\def\paramlist{}% - \let\hash\relax - \let\xeatspaces\relax - \parsemargdefxxx#1,;,% - % In case that there are 10 or more arguments we parse again the arguments - % list to set new definitions for the \macarg.BLAH macros corresponding to - % each BLAH argument. It was anyhow needed to parse already once this list - % in order to count the arguments, and as macros with at most 9 arguments - % are by far more frequent than macro with 10 or more arguments, defining - % twice the \macarg.BLAH macros does not cost too much processing power. - \ifnum\paramno<10\relax\else - \paramno0\relax - \parsemmanyargdef@@#1,;,% 10 or more arguments - \fi -} -\def\parsemargdefxxx#1,{% - \if#1;\let\next=\relax - \else \let\next=\parsemargdefxxx - \advance\paramno by 1 - \expandafter\edef\csname macarg.\eatspaces{#1}\endcsname - {\xeatspaces{\hash\the\paramno}}% - \edef\paramlist{\paramlist\hash\the\paramno,}% - \fi\next} - -\def\parsemmanyargdef@@#1,{% - \if#1;\let\next=\relax - \else - \let\next=\parsemmanyargdef@@ - \edef\tempb{\eatspaces{#1}}% - \expandafter\def\expandafter\tempa - \expandafter{\csname macarg.\tempb\endcsname}% - % Note that we need some extra \noexpand\noexpand, this is because we - % don't want \the to be expanded in the \parsermacbody as it uses an - % \xdef . - \expandafter\edef\tempa - {\noexpand\noexpand\noexpand\the\toks\the\paramno}% - \advance\paramno by 1\relax - \fi\next} - -% These two commands read recursive and nonrecursive macro bodies. -% (They're different since rec and nonrec macros end differently.) -% - -\catcode `\@\texiatcatcode -\long\def\parsemacbody#1@end macro% -{\xdef\temp{\eatcr{#1}}\endgroup\defmacro}% -\long\def\parsermacbody#1@end rmacro% -{\xdef\temp{\eatcr{#1}}\endgroup\defmacro}% -\catcode `\@=11\relax - -\let\endargs@\relax -\let\nil@\relax -\def\nilm@{\nil@}% -\long\def\nillm@{\nil@}% - -% This macro is expanded during the Texinfo macro expansion, not during its -% definition. It gets all the arguments values and assigns them to macros -% macarg.ARGNAME -% -% #1 is the macro name -% #2 is the list of argument names -% #3 is the list of argument values -\def\getargvals@#1#2#3{% - \def\macargdeflist@{}% - \def\saveparamlist@{#2}% Need to keep a copy for parameter expansion. - \def\paramlist{#2,\nil@}% - \def\macroname{#1}% - \begingroup - \macroargctxt - \def\argvaluelist{#3,\nil@}% - \def\@tempa{#3}% - \ifx\@tempa\empty - \setemptyargvalues@ - \else - \getargvals@@ - \fi -} - -% -\def\getargvals@@{% - \ifx\paramlist\nilm@ - % Some sanity check needed here that \argvaluelist is also empty. - \ifx\argvaluelist\nillm@ - \else - \errhelp = \EMsimple - \errmessage{Too many arguments in macro `\macroname'!}% - \fi - \let\next\macargexpandinbody@ - \else - \ifx\argvaluelist\nillm@ - % No more arguments values passed to macro. Set remaining named-arg - % macros to empty. - \let\next\setemptyargvalues@ - \else - % pop current arg name into \@tempb - \def\@tempa##1{\pop@{\@tempb}{\paramlist}##1\endargs@}% - \expandafter\@tempa\expandafter{\paramlist}% - % pop current argument value into \@tempc - \def\@tempa##1{\longpop@{\@tempc}{\argvaluelist}##1\endargs@}% - \expandafter\@tempa\expandafter{\argvaluelist}% - % Here \@tempb is the current arg name and \@tempc is the current arg value. - % First place the new argument macro definition into \@tempd - \expandafter\macname\expandafter{\@tempc}% - \expandafter\let\csname macarg.\@tempb\endcsname\relax - \expandafter\def\expandafter\@tempe\expandafter{% - \csname macarg.\@tempb\endcsname}% - \edef\@tempd{\long\def\@tempe{\the\macname}}% - \push@\@tempd\macargdeflist@ - \let\next\getargvals@@ - \fi - \fi - \next -} - -\def\push@#1#2{% - \expandafter\expandafter\expandafter\def - \expandafter\expandafter\expandafter#2% - \expandafter\expandafter\expandafter{% - \expandafter#1#2}% -} - -% Replace arguments by their values in the macro body, and place the result -% in macro \@tempa -\def\macvalstoargs@{% - % To do this we use the property that token registers that are \the'ed - % within an \edef expand only once. So we are going to place all argument - % values into respective token registers. - % - % First we save the token context, and initialize argument numbering. - \begingroup - \paramno0\relax - % Then, for each argument number #N, we place the corresponding argument - % value into a new token list register \toks#N - \expandafter\putargsintokens@\saveparamlist@,;,% - % Then, we expand the body so that argument are replaced by their - % values. The trick for values not to be expanded themselves is that they - % are within tokens and that tokens expand only once in an \edef . - \edef\@tempc{\csname mac.\macroname .body\endcsname}% - % Now we restore the token stack pointer to free the token list registers - % which we have used, but we make sure that expanded body is saved after - % group. - \expandafter - \endgroup - \expandafter\def\expandafter\@tempa\expandafter{\@tempc}% - } - -\def\macargexpandinbody@{% - %% Define the named-macro outside of this group and then close this group. - \expandafter - \endgroup - \macargdeflist@ - % First the replace in body the macro arguments by their values, the result - % is in \@tempa . - \macvalstoargs@ - % Then we point at the \norecurse or \gobble (for recursive) macro value - % with \@tempb . - \expandafter\let\expandafter\@tempb\csname mac.\macroname .recurse\endcsname - % Depending on whether it is recursive or not, we need some tailing - % \egroup . - \ifx\@tempb\gobble - \let\@tempc\relax - \else - \let\@tempc\egroup - \fi - % And now we do the real job: - \edef\@tempd{\noexpand\@tempb{\macroname}\noexpand\scanmacro{\@tempa}\@tempc}% - \@tempd -} - -\def\putargsintokens@#1,{% - \if#1;\let\next\relax - \else - \let\next\putargsintokens@ - % First we allocate the new token list register, and give it a temporary - % alias \@tempb . - \toksdef\@tempb\the\paramno - % Then we place the argument value into that token list register. - \expandafter\let\expandafter\@tempa\csname macarg.#1\endcsname - \expandafter\@tempb\expandafter{\@tempa}% - \advance\paramno by 1\relax - \fi - \next -} - -% Save the token stack pointer into macro #1 -\def\texisavetoksstackpoint#1{\edef#1{\the\@cclvi}} -% Restore the token stack pointer from number in macro #1 -\def\texirestoretoksstackpoint#1{\expandafter\mathchardef\expandafter\@cclvi#1\relax} -% newtoks that can be used non \outer . -\def\texinonouternewtoks{\alloc@ 5\toks \toksdef \@cclvi} - -% Tailing missing arguments are set to empty -\def\setemptyargvalues@{% - \ifx\paramlist\nilm@ - \let\next\macargexpandinbody@ - \else - \expandafter\setemptyargvaluesparser@\paramlist\endargs@ - \let\next\setemptyargvalues@ - \fi - \next -} - -\def\setemptyargvaluesparser@#1,#2\endargs@{% - \expandafter\def\expandafter\@tempa\expandafter{% - \expandafter\def\csname macarg.#1\endcsname{}}% - \push@\@tempa\macargdeflist@ - \def\paramlist{#2}% -} - -% #1 is the element target macro -% #2 is the list macro -% #3,#4\endargs@ is the list value -\def\pop@#1#2#3,#4\endargs@{% - \def#1{#3}% - \def#2{#4}% -} -\long\def\longpop@#1#2#3,#4\endargs@{% - \long\def#1{#3}% - \long\def#2{#4}% -} - -% This defines a Texinfo @macro. There are eight cases: recursive and -% nonrecursive macros of zero, one, up to nine, and many arguments. -% Much magic with \expandafter here. -% \xdef is used so that macro definitions will survive the file -% they're defined in; @include reads the file inside a group. -% -\def\defmacro{% - \let\hash=##% convert placeholders to macro parameter chars - \ifrecursive - \ifcase\paramno - % 0 - \expandafter\xdef\csname\the\macname\endcsname{% - \noexpand\scanmacro{\temp}}% - \or % 1 - \expandafter\xdef\csname\the\macname\endcsname{% - \bgroup\noexpand\macroargctxt - \noexpand\braceorline - \expandafter\noexpand\csname\the\macname xxx\endcsname}% - \expandafter\xdef\csname\the\macname xxx\endcsname##1{% - \egroup\noexpand\scanmacro{\temp}}% - \else - \ifnum\paramno<10\relax % at most 9 - \expandafter\xdef\csname\the\macname\endcsname{% - \bgroup\noexpand\macroargctxt - \noexpand\csname\the\macname xx\endcsname}% - \expandafter\xdef\csname\the\macname xx\endcsname##1{% - \expandafter\noexpand\csname\the\macname xxx\endcsname ##1,}% - \expandafter\expandafter - \expandafter\xdef - \expandafter\expandafter - \csname\the\macname xxx\endcsname - \paramlist{\egroup\noexpand\scanmacro{\temp}}% - \else % 10 or more - \expandafter\xdef\csname\the\macname\endcsname{% - \noexpand\getargvals@{\the\macname}{\argl}% - }% - \global\expandafter\let\csname mac.\the\macname .body\endcsname\temp - \global\expandafter\let\csname mac.\the\macname .recurse\endcsname\gobble - \fi - \fi - \else - \ifcase\paramno - % 0 - \expandafter\xdef\csname\the\macname\endcsname{% - \noexpand\norecurse{\the\macname}% - \noexpand\scanmacro{\temp}\egroup}% - \or % 1 - \expandafter\xdef\csname\the\macname\endcsname{% - \bgroup\noexpand\macroargctxt - \noexpand\braceorline - \expandafter\noexpand\csname\the\macname xxx\endcsname}% - \expandafter\xdef\csname\the\macname xxx\endcsname##1{% - \egroup - \noexpand\norecurse{\the\macname}% - \noexpand\scanmacro{\temp}\egroup}% - \else % at most 9 - \ifnum\paramno<10\relax - \expandafter\xdef\csname\the\macname\endcsname{% - \bgroup\noexpand\macroargctxt - \expandafter\noexpand\csname\the\macname xx\endcsname}% - \expandafter\xdef\csname\the\macname xx\endcsname##1{% - \expandafter\noexpand\csname\the\macname xxx\endcsname ##1,}% - \expandafter\expandafter - \expandafter\xdef - \expandafter\expandafter - \csname\the\macname xxx\endcsname - \paramlist{% - \egroup - \noexpand\norecurse{\the\macname}% - \noexpand\scanmacro{\temp}\egroup}% - \else % 10 or more: - \expandafter\xdef\csname\the\macname\endcsname{% - \noexpand\getargvals@{\the\macname}{\argl}% - }% - \global\expandafter\let\csname mac.\the\macname .body\endcsname\temp - \global\expandafter\let\csname mac.\the\macname .recurse\endcsname\norecurse - \fi - \fi - \fi} - -\catcode `\@\texiatcatcode\relax - -\def\norecurse#1{\bgroup\cslet{#1}{macsave.#1}} - -% \braceorline decides whether the next nonwhitespace character is a -% {. If so it reads up to the closing }, if not, it reads the whole -% line. Whatever was read is then fed to the next control sequence -% as an argument (by \parsebrace or \parsearg). -% -\def\braceorline#1{\let\macnamexxx=#1\futurelet\nchar\braceorlinexxx} -\def\braceorlinexxx{% - \ifx\nchar\bgroup\else - \expandafter\parsearg - \fi \macnamexxx} - - -% @alias. -% We need some trickery to remove the optional spaces around the equal -% sign. Make them active and then expand them all to nothing. -% -\def\alias{\parseargusing\obeyspaces\aliasxxx} -\def\aliasxxx #1{\aliasyyy#1\relax} -\def\aliasyyy #1=#2\relax{% - {% - \expandafter\let\obeyedspace=\empty - \addtomacrolist{#1}% - \xdef\next{\global\let\makecsname{#1}=\makecsname{#2}}% - }% - \next -} - - -\message{cross references,} - -\newwrite\auxfile -\newif\ifhavexrefs % True if xref values are known. -\newif\ifwarnedxrefs % True if we warned once that they aren't known. - -% @inforef is relatively simple. -\def\inforef #1{\inforefzzz #1,,,,**} -\def\inforefzzz #1,#2,#3,#4**{% - \putwordSee{} \putwordInfo{} \putwordfile{} \file{\ignorespaces #3{}}, - node \samp{\ignorespaces#1{}}} - -% @node's only job in TeX is to define \lastnode, which is used in -% cross-references. The @node line might or might not have commas, and -% might or might not have spaces before the first comma, like: -% @node foo , bar , ... -% We don't want such trailing spaces in the node name. -% -\parseargdef\node{\checkenv{}\donode #1 ,\finishnodeparse} -% -% also remove a trailing comma, in case of something like this: -% @node Help-Cross, , , Cross-refs -\def\donode#1 ,#2\finishnodeparse{\dodonode #1,\finishnodeparse} -\def\dodonode#1,#2\finishnodeparse{\gdef\lastnode{#1}} - -\let\nwnode=\node -\let\lastnode=\empty - -% Write a cross-reference definition for the current node. #1 is the -% type (Ynumbered, Yappendix, Ynothing). -% -\def\donoderef#1{% - \ifx\lastnode\empty\else - \setref{\lastnode}{#1}% - \global\let\lastnode=\empty - \fi -} - -% @anchor{NAME} -- define xref target at arbitrary point. -% -\newcount\savesfregister -% -\def\savesf{\relax \ifhmode \savesfregister=\spacefactor \fi} -\def\restoresf{\relax \ifhmode \spacefactor=\savesfregister \fi} -\def\anchor#1{\savesf \setref{#1}{Ynothing}\restoresf \ignorespaces} - -% \setref{NAME}{SNT} defines a cross-reference point NAME (a node or an -% anchor), which consists of three parts: -% 1) NAME-title - the current sectioning name taken from \lastsection, -% or the anchor name. -% 2) NAME-snt - section number and type, passed as the SNT arg, or -% empty for anchors. -% 3) NAME-pg - the page number. -% -% This is called from \donoderef, \anchor, and \dofloat. In the case of -% floats, there is an additional part, which is not written here: -% 4) NAME-lof - the text as it should appear in a @listoffloats. -% -\def\setref#1#2{% - \pdfmkdest{#1}% - \iflinks - {% - \atdummies % preserve commands, but don't expand them - \edef\writexrdef##1##2{% - \write\auxfile{@xrdef{#1-% #1 of \setref, expanded by the \edef - ##1}{##2}}% these are parameters of \writexrdef - }% - \toks0 = \expandafter{\lastsection}% - \immediate \writexrdef{title}{\the\toks0 }% - \immediate \writexrdef{snt}{\csname #2\endcsname}% \Ynumbered etc. - \safewhatsit{\writexrdef{pg}{\folio}}% will be written later, at \shipout - }% - \fi -} - -% @xrefautosectiontitle on|off says whether @section(ing) names are used -% automatically in xrefs, if the third arg is not explicitly specified. -% This was provided as a "secret" @set xref-automatic-section-title -% variable, now it's official. -% -\parseargdef\xrefautomaticsectiontitle{% - \def\temp{#1}% - \ifx\temp\onword - \expandafter\let\csname SETxref-automatic-section-title\endcsname - = \empty - \else\ifx\temp\offword - \expandafter\let\csname SETxref-automatic-section-title\endcsname - = \relax - \else - \errhelp = \EMsimple - \errmessage{Unknown @xrefautomaticsectiontitle value `\temp', - must be on|off}% - \fi\fi -} - - -% @xref, @pxref, and @ref generate cross-references. For \xrefX, #1 is -% the node name, #2 the name of the Info cross-reference, #3 the printed -% node name, #4 the name of the Info file, #5 the name of the printed -% manual. All but the node name can be omitted. -% -\def\pxref#1{\putwordsee{} \xrefX[#1,,,,,,,]} -\def\xref#1{\putwordSee{} \xrefX[#1,,,,,,,]} -\def\ref#1{\xrefX[#1,,,,,,,]} -% -\newbox\topbox -\newbox\printedrefnamebox -\newbox\printedmanualbox -% -\def\xrefX[#1,#2,#3,#4,#5,#6]{\begingroup - \unsepspaces - % - \def\printedrefname{\ignorespaces #3}% - \setbox\printedrefnamebox = \hbox{\printedrefname\unskip}% - % - \def\printedmanual{\ignorespaces #5}% - \setbox\printedmanualbox = \hbox{\printedmanual\unskip}% - % - % If the printed reference name (arg #3) was not explicitly given in - % the @xref, figure out what we want to use. - \ifdim \wd\printedrefnamebox = 0pt - % No printed node name was explicitly given. - \expandafter\ifx\csname SETxref-automatic-section-title\endcsname \relax - % Not auto section-title: use node name inside the square brackets. - \def\printedrefname{\ignorespaces #1}% - \else - % Auto section-title: use chapter/section title inside - % the square brackets if we have it. - \ifdim \wd\printedmanualbox > 0pt - % It is in another manual, so we don't have it; use node name. - \def\printedrefname{\ignorespaces #1}% - \else - \ifhavexrefs - % We (should) know the real title if we have the xref values. - \def\printedrefname{\refx{#1-title}{}}% - \else - % Otherwise just copy the Info node name. - \def\printedrefname{\ignorespaces #1}% - \fi% - \fi - \fi - \fi - % - % Make link in pdf output. - \ifpdf - {\indexnofonts - \turnoffactive - \makevalueexpandable - % This expands tokens, so do it after making catcode changes, so _ - % etc. don't get their TeX definitions. - \getfilename{#4}% - % - \edef\pdfxrefdest{#1}% - \txiescapepdf\pdfxrefdest - % - \leavevmode - \startlink attr{/Border [0 0 0]}% - \ifnum\filenamelength>0 - goto file{\the\filename.pdf} name{\pdfxrefdest}% - \else - goto name{\pdfmkpgn{\pdfxrefdest}}% - \fi - }% - \setcolor{\linkcolor}% - \fi - % - % Float references are printed completely differently: "Figure 1.2" - % instead of "[somenode], p.3". We distinguish them by the - % LABEL-title being set to a magic string. - {% - % Have to otherify everything special to allow the \csname to - % include an _ in the xref name, etc. - \indexnofonts - \turnoffactive - \expandafter\global\expandafter\let\expandafter\Xthisreftitle - \csname XR#1-title\endcsname - }% - \iffloat\Xthisreftitle - % If the user specified the print name (third arg) to the ref, - % print it instead of our usual "Figure 1.2". - \ifdim\wd\printedrefnamebox = 0pt - \refx{#1-snt}{}% - \else - \printedrefname - \fi - % - % if the user also gave the printed manual name (fifth arg), append - % "in MANUALNAME". - \ifdim \wd\printedmanualbox > 0pt - \space \putwordin{} \cite{\printedmanual}% - \fi - \else - % node/anchor (non-float) references. - % - % If we use \unhbox to print the node names, TeX does not insert - % empty discretionaries after hyphens, which means that it will not - % find a line break at a hyphen in a node names. Since some manuals - % are best written with fairly long node names, containing hyphens, - % this is a loss. Therefore, we give the text of the node name - % again, so it is as if TeX is seeing it for the first time. - % - % Cross-manual reference. Only include the "Section ``foo'' in" if - % the foo is neither missing or Top. Thus, @xref{,,,foo,The Foo Manual} - % outputs simply "see The Foo Manual". - \ifdim \wd\printedmanualbox > 0pt - % What is the 7sp about? The idea is that we also want to omit - % the Section part if we would be printing "Top", since they are - % clearly trying to refer to the whole manual. But, this being - % TeX, we can't easily compare strings while ignoring the possible - % spaces before and after in the input. By adding the arbitrary - % 7sp, we make it much less likely that a real node name would - % happen to have the same width as "Top" (e.g., in a monospaced font). - % I hope it will never happen in practice. - % - % For the same basic reason, we retypeset the "Top" at every - % reference, since the current font is indeterminate. - % - \setbox\topbox = \hbox{Top\kern7sp}% - \setbox2 = \hbox{\ignorespaces \printedrefname \unskip \kern7sp}% - \ifdim \wd2 > 7sp - \ifdim \wd2 = \wd\topbox \else - \putwordSection{} ``\printedrefname'' \putwordin{}\space - \fi - \fi - \cite{\printedmanual}% - \else - % Reference in this manual. - % - % _ (for example) has to be the character _ for the purposes of the - % control sequence corresponding to the node, but it has to expand - % into the usual \leavevmode...\vrule stuff for purposes of - % printing. So we \turnoffactive for the \refx-snt, back on for the - % printing, back off for the \refx-pg. - {\turnoffactive - % Only output a following space if the -snt ref is nonempty; for - % @unnumbered and @anchor, it won't be. - \setbox2 = \hbox{\ignorespaces \refx{#1-snt}{}}% - \ifdim \wd2 > 0pt \refx{#1-snt}\space\fi - }% - % output the `[mynode]' via the macro below so it can be overridden. - \xrefprintnodename\printedrefname - % - % But we always want a comma and a space: - ,\space - % - % output the `page 3'. - \turnoffactive \putwordpage\tie\refx{#1-pg}{}% - \fi - \fi - \endlink -\endgroup} - -% This macro is called from \xrefX for the `[nodename]' part of xref -% output. It's a separate macro only so it can be changed more easily, -% since square brackets don't work well in some documents. Particularly -% one that Bob is working on :). -% -\def\xrefprintnodename#1{[#1]} - -% Things referred to by \setref. -% -\def\Ynothing{} -\def\Yomitfromtoc{} -\def\Ynumbered{% - \ifnum\secno=0 - \putwordChapter@tie \the\chapno - \else \ifnum\subsecno=0 - \putwordSection@tie \the\chapno.\the\secno - \else \ifnum\subsubsecno=0 - \putwordSection@tie \the\chapno.\the\secno.\the\subsecno - \else - \putwordSection@tie \the\chapno.\the\secno.\the\subsecno.\the\subsubsecno - \fi\fi\fi -} -\def\Yappendix{% - \ifnum\secno=0 - \putwordAppendix@tie @char\the\appendixno{}% - \else \ifnum\subsecno=0 - \putwordSection@tie @char\the\appendixno.\the\secno - \else \ifnum\subsubsecno=0 - \putwordSection@tie @char\the\appendixno.\the\secno.\the\subsecno - \else - \putwordSection@tie - @char\the\appendixno.\the\secno.\the\subsecno.\the\subsubsecno - \fi\fi\fi -} - -% Define \refx{NAME}{SUFFIX} to reference a cross-reference string named NAME. -% If its value is nonempty, SUFFIX is output afterward. -% -\def\refx#1#2{% - {% - \indexnofonts - \otherbackslash - \expandafter\global\expandafter\let\expandafter\thisrefX - \csname XR#1\endcsname - }% - \ifx\thisrefX\relax - % If not defined, say something at least. - \angleleft un\-de\-fined\angleright - \iflinks - \ifhavexrefs - {\toks0 = {#1}% avoid expansion of possibly-complex value - \message{\linenumber Undefined cross reference `\the\toks0'.}}% - \else - \ifwarnedxrefs\else - \global\warnedxrefstrue - \message{Cross reference values unknown; you must run TeX again.}% - \fi - \fi - \fi - \else - % It's defined, so just use it. - \thisrefX - \fi - #2% Output the suffix in any case. -} - -% This is the macro invoked by entries in the aux file. Usually it's -% just a \def (we prepend XR to the control sequence name to avoid -% collisions). But if this is a float type, we have more work to do. -% -\def\xrdef#1#2{% - {% The node name might contain 8-bit characters, which in our current - % implementation are changed to commands like @'e. Don't let these - % mess up the control sequence name. - \indexnofonts - \turnoffactive - \xdef\safexrefname{#1}% - }% - % - \expandafter\gdef\csname XR\safexrefname\endcsname{#2}% remember this xref - % - % Was that xref control sequence that we just defined for a float? - \expandafter\iffloat\csname XR\safexrefname\endcsname - % it was a float, and we have the (safe) float type in \iffloattype. - \expandafter\let\expandafter\floatlist - \csname floatlist\iffloattype\endcsname - % - % Is this the first time we've seen this float type? - \expandafter\ifx\floatlist\relax - \toks0 = {\do}% yes, so just \do - \else - % had it before, so preserve previous elements in list. - \toks0 = \expandafter{\floatlist\do}% - \fi - % - % Remember this xref in the control sequence \floatlistFLOATTYPE, - % for later use in \listoffloats. - \expandafter\xdef\csname floatlist\iffloattype\endcsname{\the\toks0 - {\safexrefname}}% - \fi -} - -% Read the last existing aux file, if any. No error if none exists. -% -\def\tryauxfile{% - \openin 1 \jobname.aux - \ifeof 1 \else - \readdatafile{aux}% - \global\havexrefstrue - \fi - \closein 1 -} - -\def\setupdatafile{% - \catcode`\^^@=\other - \catcode`\^^A=\other - \catcode`\^^B=\other - \catcode`\^^C=\other - \catcode`\^^D=\other - \catcode`\^^E=\other - \catcode`\^^F=\other - \catcode`\^^G=\other - \catcode`\^^H=\other - \catcode`\^^K=\other - \catcode`\^^L=\other - \catcode`\^^N=\other - \catcode`\^^P=\other - \catcode`\^^Q=\other - \catcode`\^^R=\other - \catcode`\^^S=\other - \catcode`\^^T=\other - \catcode`\^^U=\other - \catcode`\^^V=\other - \catcode`\^^W=\other - \catcode`\^^X=\other - \catcode`\^^Z=\other - \catcode`\^^[=\other - \catcode`\^^\=\other - \catcode`\^^]=\other - \catcode`\^^^=\other - \catcode`\^^_=\other - % It was suggested to set the catcode of ^ to 7, which would allow ^^e4 etc. - % in xref tags, i.e., node names. But since ^^e4 notation isn't - % supported in the main text, it doesn't seem desirable. Furthermore, - % that is not enough: for node names that actually contain a ^ - % character, we would end up writing a line like this: 'xrdef {'hat - % b-title}{'hat b} and \xrdef does a \csname...\endcsname on the first - % argument, and \hat is not an expandable control sequence. It could - % all be worked out, but why? Either we support ^^ or we don't. - % - % The other change necessary for this was to define \auxhat: - % \def\auxhat{\def^{'hat }}% extra space so ok if followed by letter - % and then to call \auxhat in \setq. - % - \catcode`\^=\other - % - % Special characters. Should be turned off anyway, but... - \catcode`\~=\other - \catcode`\[=\other - \catcode`\]=\other - \catcode`\"=\other - \catcode`\_=\other - \catcode`\|=\other - \catcode`\<=\other - \catcode`\>=\other - \catcode`\$=\other - \catcode`\#=\other - \catcode`\&=\other - \catcode`\%=\other - \catcode`+=\other % avoid \+ for paranoia even though we've turned it off - % - % This is to support \ in node names and titles, since the \ - % characters end up in a \csname. It's easier than - % leaving it active and making its active definition an actual \ - % character. What I don't understand is why it works in the *value* - % of the xrdef. Seems like it should be a catcode12 \, and that - % should not typeset properly. But it works, so I'm moving on for - % now. --karl, 15jan04. - \catcode`\\=\other - % - % Make the characters 128-255 be printing characters. - {% - \count1=128 - \def\loop{% - \catcode\count1=\other - \advance\count1 by 1 - \ifnum \count1<256 \loop \fi - }% - }% - % - % @ is our escape character in .aux files, and we need braces. - \catcode`\{=1 - \catcode`\}=2 - \catcode`\@=0 -} - -\def\readdatafile#1{% -\begingroup - \setupdatafile - \input\jobname.#1 -\endgroup} - - -\message{insertions,} -% including footnotes. - -\newcount \footnoteno - -% The trailing space in the following definition for supereject is -% vital for proper filling; pages come out unaligned when you do a -% pagealignmacro call if that space before the closing brace is -% removed. (Generally, numeric constants should always be followed by a -% space to prevent strange expansion errors.) -\def\supereject{\par\penalty -20000\footnoteno =0 } - -% @footnotestyle is meaningful for Info output only. -\let\footnotestyle=\comment - -{\catcode `\@=11 -% -% Auto-number footnotes. Otherwise like plain. -\gdef\footnote{% - \let\indent=\ptexindent - \let\noindent=\ptexnoindent - \global\advance\footnoteno by \@ne - \edef\thisfootno{$^{\the\footnoteno}$}% - % - % In case the footnote comes at the end of a sentence, preserve the - % extra spacing after we do the footnote number. - \let\@sf\empty - \ifhmode\edef\@sf{\spacefactor\the\spacefactor}\ptexslash\fi - % - % Remove inadvertent blank space before typesetting the footnote number. - \unskip - \thisfootno\@sf - \dofootnote -}% - -% Don't bother with the trickery in plain.tex to not require the -% footnote text as a parameter. Our footnotes don't need to be so general. -% -% Oh yes, they do; otherwise, @ifset (and anything else that uses -% \parseargline) fails inside footnotes because the tokens are fixed when -% the footnote is read. --karl, 16nov96. -% -\gdef\dofootnote{% - \insert\footins\bgroup - % We want to typeset this text as a normal paragraph, even if the - % footnote reference occurs in (for example) a display environment. - % So reset some parameters. - \hsize=\pagewidth - \interlinepenalty\interfootnotelinepenalty - \splittopskip\ht\strutbox % top baseline for broken footnotes - \splitmaxdepth\dp\strutbox - \floatingpenalty\@MM - \leftskip\z@skip - \rightskip\z@skip - \spaceskip\z@skip - \xspaceskip\z@skip - \parindent\defaultparindent - % - \smallfonts \rm - % - % Because we use hanging indentation in footnotes, a @noindent appears - % to exdent this text, so make it be a no-op. makeinfo does not use - % hanging indentation so @noindent can still be needed within footnote - % text after an @example or the like (not that this is good style). - \let\noindent = \relax - % - % Hang the footnote text off the number. Use \everypar in case the - % footnote extends for more than one paragraph. - \everypar = {\hang}% - \textindent{\thisfootno}% - % - % Don't crash into the line above the footnote text. Since this - % expands into a box, it must come within the paragraph, lest it - % provide a place where TeX can split the footnote. - \footstrut - % - % Invoke rest of plain TeX footnote routine. - \futurelet\next\fo@t -} -}%end \catcode `\@=11 - -% In case a @footnote appears in a vbox, save the footnote text and create -% the real \insert just after the vbox finished. Otherwise, the insertion -% would be lost. -% Similarly, if a @footnote appears inside an alignment, save the footnote -% text to a box and make the \insert when a row of the table is finished. -% And the same can be done for other insert classes. --kasal, 16nov03. - -% Replace the \insert primitive by a cheating macro. -% Deeper inside, just make sure that the saved insertions are not spilled -% out prematurely. -% -\def\startsavinginserts{% - \ifx \insert\ptexinsert - \let\insert\saveinsert - \else - \let\checkinserts\relax - \fi -} - -% This \insert replacement works for both \insert\footins{foo} and -% \insert\footins\bgroup foo\egroup, but it doesn't work for \insert27{foo}. -% -\def\saveinsert#1{% - \edef\next{\noexpand\savetobox \makeSAVEname#1}% - \afterassignment\next - % swallow the left brace - \let\temp = -} -\def\makeSAVEname#1{\makecsname{SAVE\expandafter\gobble\string#1}} -\def\savetobox#1{\global\setbox#1 = \vbox\bgroup \unvbox#1} - -\def\checksaveins#1{\ifvoid#1\else \placesaveins#1\fi} - -\def\placesaveins#1{% - \ptexinsert \csname\expandafter\gobblesave\string#1\endcsname - {\box#1}% -} - -% eat @SAVE -- beware, all of them have catcode \other: -{ - \def\dospecials{\do S\do A\do V\do E} \uncatcodespecials % ;-) - \gdef\gobblesave @SAVE{} -} - -% initialization: -\def\newsaveins #1{% - \edef\next{\noexpand\newsaveinsX \makeSAVEname#1}% - \next -} -\def\newsaveinsX #1{% - \csname newbox\endcsname #1% - \expandafter\def\expandafter\checkinserts\expandafter{\checkinserts - \checksaveins #1}% -} - -% initialize: -\let\checkinserts\empty -\newsaveins\footins -\newsaveins\margin - - -% @image. We use the macros from epsf.tex to support this. -% If epsf.tex is not installed and @image is used, we complain. -% -% Check for and read epsf.tex up front. If we read it only at @image -% time, we might be inside a group, and then its definitions would get -% undone and the next image would fail. -\openin 1 = epsf.tex -\ifeof 1 \else - % Do not bother showing banner with epsf.tex v2.7k (available in - % doc/epsf.tex and on ctan). - \def\epsfannounce{\toks0 = }% - \input epsf.tex -\fi -\closein 1 -% -% We will only complain once about lack of epsf.tex. -\newif\ifwarnednoepsf -\newhelp\noepsfhelp{epsf.tex must be installed for images to - work. It is also included in the Texinfo distribution, or you can get - it from ftp://tug.org/tex/epsf.tex.} -% -\def\image#1{% - \ifx\epsfbox\thisisundefined - \ifwarnednoepsf \else - \errhelp = \noepsfhelp - \errmessage{epsf.tex not found, images will be ignored}% - \global\warnednoepsftrue - \fi - \else - \imagexxx #1,,,,,\finish - \fi -} -% -% Arguments to @image: -% #1 is (mandatory) image filename; we tack on .eps extension. -% #2 is (optional) width, #3 is (optional) height. -% #4 is (ignored optional) html alt text. -% #5 is (ignored optional) extension. -% #6 is just the usual extra ignored arg for parsing stuff. -\newif\ifimagevmode -\def\imagexxx#1,#2,#3,#4,#5,#6\finish{\begingroup - \catcode`\^^M = 5 % in case we're inside an example - \normalturnoffactive % allow _ et al. in names - % If the image is by itself, center it. - \ifvmode - \imagevmodetrue - \else \ifx\centersub\centerV - % for @center @image, we need a vbox so we can have our vertical space - \imagevmodetrue - \vbox\bgroup % vbox has better behavior than vtop herev - \fi\fi - % - \ifimagevmode - \nobreak\medskip - % Usually we'll have text after the image which will insert - % \parskip glue, so insert it here too to equalize the space - % above and below. - \nobreak\vskip\parskip - \nobreak - \fi - % - % Leave vertical mode so that indentation from an enclosing - % environment such as @quotation is respected. - % However, if we're at the top level, we don't want the - % normal paragraph indentation. - % On the other hand, if we are in the case of @center @image, we don't - % want to start a paragraph, which will create a hsize-width box and - % eradicate the centering. - \ifx\centersub\centerV\else \noindent \fi - % - % Output the image. - \ifpdf - \dopdfimage{#1}{#2}{#3}% - \else - % \epsfbox itself resets \epsf?size at each figure. - \setbox0 = \hbox{\ignorespaces #2}\ifdim\wd0 > 0pt \epsfxsize=#2\relax \fi - \setbox0 = \hbox{\ignorespaces #3}\ifdim\wd0 > 0pt \epsfysize=#3\relax \fi - \epsfbox{#1.eps}% - \fi - % - \ifimagevmode - \medskip % space after a standalone image - \fi - \ifx\centersub\centerV \egroup \fi -\endgroup} - - -% @float FLOATTYPE,LABEL,LOC ... @end float for displayed figures, tables, -% etc. We don't actually implement floating yet, we always include the -% float "here". But it seemed the best name for the future. -% -\envparseargdef\float{\eatcommaspace\eatcommaspace\dofloat#1, , ,\finish} - -% There may be a space before second and/or third parameter; delete it. -\def\eatcommaspace#1, {#1,} - -% #1 is the optional FLOATTYPE, the text label for this float, typically -% "Figure", "Table", "Example", etc. Can't contain commas. If omitted, -% this float will not be numbered and cannot be referred to. -% -% #2 is the optional xref label. Also must be present for the float to -% be referable. -% -% #3 is the optional positioning argument; for now, it is ignored. It -% will somehow specify the positions allowed to float to (here, top, bottom). -% -% We keep a separate counter for each FLOATTYPE, which we reset at each -% chapter-level command. -\let\resetallfloatnos=\empty -% -\def\dofloat#1,#2,#3,#4\finish{% - \let\thiscaption=\empty - \let\thisshortcaption=\empty - % - % don't lose footnotes inside @float. - % - % BEWARE: when the floats start float, we have to issue warning whenever an - % insert appears inside a float which could possibly float. --kasal, 26may04 - % - \startsavinginserts - % - % We can't be used inside a paragraph. - \par - % - \vtop\bgroup - \def\floattype{#1}% - \def\floatlabel{#2}% - \def\floatloc{#3}% we do nothing with this yet. - % - \ifx\floattype\empty - \let\safefloattype=\empty - \else - {% - % the floattype might have accents or other special characters, - % but we need to use it in a control sequence name. - \indexnofonts - \turnoffactive - \xdef\safefloattype{\floattype}% - }% - \fi - % - % If label is given but no type, we handle that as the empty type. - \ifx\floatlabel\empty \else - % We want each FLOATTYPE to be numbered separately (Figure 1, - % Table 1, Figure 2, ...). (And if no label, no number.) - % - \expandafter\getfloatno\csname\safefloattype floatno\endcsname - \global\advance\floatno by 1 - % - {% - % This magic value for \lastsection is output by \setref as the - % XREFLABEL-title value. \xrefX uses it to distinguish float - % labels (which have a completely different output format) from - % node and anchor labels. And \xrdef uses it to construct the - % lists of floats. - % - \edef\lastsection{\floatmagic=\safefloattype}% - \setref{\floatlabel}{Yfloat}% - }% - \fi - % - % start with \parskip glue, I guess. - \vskip\parskip - % - % Don't suppress indentation if a float happens to start a section. - \restorefirstparagraphindent -} - -% we have these possibilities: -% @float Foo,lbl & @caption{Cap}: Foo 1.1: Cap -% @float Foo,lbl & no caption: Foo 1.1 -% @float Foo & @caption{Cap}: Foo: Cap -% @float Foo & no caption: Foo -% @float ,lbl & Caption{Cap}: 1.1: Cap -% @float ,lbl & no caption: 1.1 -% @float & @caption{Cap}: Cap -% @float & no caption: -% -\def\Efloat{% - \let\floatident = \empty - % - % In all cases, if we have a float type, it comes first. - \ifx\floattype\empty \else \def\floatident{\floattype}\fi - % - % If we have an xref label, the number comes next. - \ifx\floatlabel\empty \else - \ifx\floattype\empty \else % if also had float type, need tie first. - \appendtomacro\floatident{\tie}% - \fi - % the number. - \appendtomacro\floatident{\chaplevelprefix\the\floatno}% - \fi - % - % Start the printed caption with what we've constructed in - % \floatident, but keep it separate; we need \floatident again. - \let\captionline = \floatident - % - \ifx\thiscaption\empty \else - \ifx\floatident\empty \else - \appendtomacro\captionline{: }% had ident, so need a colon between - \fi - % - % caption text. - \appendtomacro\captionline{\scanexp\thiscaption}% - \fi - % - % If we have anything to print, print it, with space before. - % Eventually this needs to become an \insert. - \ifx\captionline\empty \else - \vskip.5\parskip - \captionline - % - % Space below caption. - \vskip\parskip - \fi - % - % If have an xref label, write the list of floats info. Do this - % after the caption, to avoid chance of it being a breakpoint. - \ifx\floatlabel\empty \else - % Write the text that goes in the lof to the aux file as - % \floatlabel-lof. Besides \floatident, we include the short - % caption if specified, else the full caption if specified, else nothing. - {% - \atdummies - % - % since we read the caption text in the macro world, where ^^M - % is turned into a normal character, we have to scan it back, so - % we don't write the literal three characters "^^M" into the aux file. - \scanexp{% - \xdef\noexpand\gtemp{% - \ifx\thisshortcaption\empty - \thiscaption - \else - \thisshortcaption - \fi - }% - }% - \immediate\write\auxfile{@xrdef{\floatlabel-lof}{\floatident - \ifx\gtemp\empty \else : \gtemp \fi}}% - }% - \fi - \egroup % end of \vtop - % - % place the captured inserts - % - % BEWARE: when the floats start floating, we have to issue warning - % whenever an insert appears inside a float which could possibly - % float. --kasal, 26may04 - % - \checkinserts -} - -% Append the tokens #2 to the definition of macro #1, not expanding either. -% -\def\appendtomacro#1#2{% - \expandafter\def\expandafter#1\expandafter{#1#2}% -} - -% @caption, @shortcaption -% -\def\caption{\docaption\thiscaption} -\def\shortcaption{\docaption\thisshortcaption} -\def\docaption{\checkenv\float \bgroup\scanargctxt\defcaption} -\def\defcaption#1#2{\egroup \def#1{#2}} - -% The parameter is the control sequence identifying the counter we are -% going to use. Create it if it doesn't exist and assign it to \floatno. -\def\getfloatno#1{% - \ifx#1\relax - % Haven't seen this figure type before. - \csname newcount\endcsname #1% - % - % Remember to reset this floatno at the next chap. - \expandafter\gdef\expandafter\resetallfloatnos - \expandafter{\resetallfloatnos #1=0 }% - \fi - \let\floatno#1% -} - -% \setref calls this to get the XREFLABEL-snt value. We want an @xref -% to the FLOATLABEL to expand to "Figure 3.1". We call \setref when we -% first read the @float command. -% -\def\Yfloat{\floattype@tie \chaplevelprefix\the\floatno}% - -% Magic string used for the XREFLABEL-title value, so \xrefX can -% distinguish floats from other xref types. -\def\floatmagic{!!float!!} - -% #1 is the control sequence we are passed; we expand into a conditional -% which is true if #1 represents a float ref. That is, the magic -% \lastsection value which we \setref above. -% -\def\iffloat#1{\expandafter\doiffloat#1==\finish} -% -% #1 is (maybe) the \floatmagic string. If so, #2 will be the -% (safe) float type for this float. We set \iffloattype to #2. -% -\def\doiffloat#1=#2=#3\finish{% - \def\temp{#1}% - \def\iffloattype{#2}% - \ifx\temp\floatmagic -} - -% @listoffloats FLOATTYPE - print a list of floats like a table of contents. -% -\parseargdef\listoffloats{% - \def\floattype{#1}% floattype - {% - % the floattype might have accents or other special characters, - % but we need to use it in a control sequence name. - \indexnofonts - \turnoffactive - \xdef\safefloattype{\floattype}% - }% - % - % \xrdef saves the floats as a \do-list in \floatlistSAFEFLOATTYPE. - \expandafter\ifx\csname floatlist\safefloattype\endcsname \relax - \ifhavexrefs - % if the user said @listoffloats foo but never @float foo. - \message{\linenumber No `\safefloattype' floats to list.}% - \fi - \else - \begingroup - \leftskip=\tocindent % indent these entries like a toc - \let\do=\listoffloatsdo - \csname floatlist\safefloattype\endcsname - \endgroup - \fi -} - -% This is called on each entry in a list of floats. We're passed the -% xref label, in the form LABEL-title, which is how we save it in the -% aux file. We strip off the -title and look up \XRLABEL-lof, which -% has the text we're supposed to typeset here. -% -% Figures without xref labels will not be included in the list (since -% they won't appear in the aux file). -% -\def\listoffloatsdo#1{\listoffloatsdoentry#1\finish} -\def\listoffloatsdoentry#1-title\finish{{% - % Can't fully expand XR#1-lof because it can contain anything. Just - % pass the control sequence. On the other hand, XR#1-pg is just the - % page number, and we want to fully expand that so we can get a link - % in pdf output. - \toksA = \expandafter{\csname XR#1-lof\endcsname}% - % - % use the same \entry macro we use to generate the TOC and index. - \edef\writeentry{\noexpand\entry{\the\toksA}{\csname XR#1-pg\endcsname}}% - \writeentry -}} - - -\message{localization,} - -% For single-language documents, @documentlanguage is usually given very -% early, just after @documentencoding. Single argument is the language -% (de) or locale (de_DE) abbreviation. -% -{ - \catcode`\_ = \active - \globaldefs=1 -\parseargdef\documentlanguage{\begingroup - \let_=\normalunderscore % normal _ character for filenames - \tex % read txi-??.tex file in plain TeX. - % Read the file by the name they passed if it exists. - \openin 1 txi-#1.tex - \ifeof 1 - \documentlanguagetrywithoutunderscore{#1_\finish}% - \else - \globaldefs = 1 % everything in the txi-LL files needs to persist - \input txi-#1.tex - \fi - \closein 1 - \endgroup % end raw TeX -\endgroup} -% -% If they passed de_DE, and txi-de_DE.tex doesn't exist, -% try txi-de.tex. -% -\gdef\documentlanguagetrywithoutunderscore#1_#2\finish{% - \openin 1 txi-#1.tex - \ifeof 1 - \errhelp = \nolanghelp - \errmessage{Cannot read language file txi-#1.tex}% - \else - \globaldefs = 1 % everything in the txi-LL files needs to persist - \input txi-#1.tex - \fi - \closein 1 -} -}% end of special _ catcode -% -\newhelp\nolanghelp{The given language definition file cannot be found or -is empty. Maybe you need to install it? Putting it in the current -directory should work if nowhere else does.} - -% This macro is called from txi-??.tex files; the first argument is the -% \language name to set (without the "\lang@" prefix), the second and -% third args are \{left,right}hyphenmin. -% -% The language names to pass are determined when the format is built. -% See the etex.log file created at that time, e.g., -% /usr/local/texlive/2008/texmf-var/web2c/pdftex/etex.log. -% -% With TeX Live 2008, etex now includes hyphenation patterns for all -% available languages. This means we can support hyphenation in -% Texinfo, at least to some extent. (This still doesn't solve the -% accented characters problem.) -% -\catcode`@=11 -\def\txisetlanguage#1#2#3{% - % do not set the language if the name is undefined in the current TeX. - \expandafter\ifx\csname lang@#1\endcsname \relax - \message{no patterns for #1}% - \else - \global\language = \csname lang@#1\endcsname - \fi - % but there is no harm in adjusting the hyphenmin values regardless. - \global\lefthyphenmin = #2\relax - \global\righthyphenmin = #3\relax -} - -% Helpers for encodings. -% Set the catcode of characters 128 through 255 to the specified number. -% -\def\setnonasciicharscatcode#1{% - \count255=128 - \loop\ifnum\count255<256 - \global\catcode\count255=#1\relax - \advance\count255 by 1 - \repeat -} - -\def\setnonasciicharscatcodenonglobal#1{% - \count255=128 - \loop\ifnum\count255<256 - \catcode\count255=#1\relax - \advance\count255 by 1 - \repeat -} - -% @documentencoding sets the definition of non-ASCII characters -% according to the specified encoding. -% -\parseargdef\documentencoding{% - % Encoding being declared for the document. - \def\declaredencoding{\csname #1.enc\endcsname}% - % - % Supported encodings: names converted to tokens in order to be able - % to compare them with \ifx. - \def\ascii{\csname US-ASCII.enc\endcsname}% - \def\latnine{\csname ISO-8859-15.enc\endcsname}% - \def\latone{\csname ISO-8859-1.enc\endcsname}% - \def\lattwo{\csname ISO-8859-2.enc\endcsname}% - \def\utfeight{\csname UTF-8.enc\endcsname}% - % - \ifx \declaredencoding \ascii - \asciichardefs - % - \else \ifx \declaredencoding \lattwo - \setnonasciicharscatcode\active - \lattwochardefs - % - \else \ifx \declaredencoding \latone - \setnonasciicharscatcode\active - \latonechardefs - % - \else \ifx \declaredencoding \latnine - \setnonasciicharscatcode\active - \latninechardefs - % - \else \ifx \declaredencoding \utfeight - \setnonasciicharscatcode\active - \utfeightchardefs - % - \else - \message{Unknown document encoding #1, ignoring.}% - % - \fi % utfeight - \fi % latnine - \fi % latone - \fi % lattwo - \fi % ascii -} - -% A message to be logged when using a character that isn't available -% the default font encoding (OT1). -% -\def\missingcharmsg#1{\message{Character missing in OT1 encoding: #1.}} - -% Take account of \c (plain) vs. \, (Texinfo) difference. -\def\cedilla#1{\ifx\c\ptexc\c{#1}\else\,{#1}\fi} - -% First, make active non-ASCII characters in order for them to be -% correctly categorized when TeX reads the replacement text of -% macros containing the character definitions. -\setnonasciicharscatcode\active -% -% Latin1 (ISO-8859-1) character definitions. -\def\latonechardefs{% - \gdef^^a0{\tie} - \gdef^^a1{\exclamdown} - \gdef^^a2{\missingcharmsg{CENT SIGN}} - \gdef^^a3{{\pounds}} - \gdef^^a4{\missingcharmsg{CURRENCY SIGN}} - \gdef^^a5{\missingcharmsg{YEN SIGN}} - \gdef^^a6{\missingcharmsg{BROKEN BAR}} - \gdef^^a7{\S} - \gdef^^a8{\"{}} - \gdef^^a9{\copyright} - \gdef^^aa{\ordf} - \gdef^^ab{\guillemetleft} - \gdef^^ac{$\lnot$} - \gdef^^ad{\-} - \gdef^^ae{\registeredsymbol} - \gdef^^af{\={}} - % - \gdef^^b0{\textdegree} - \gdef^^b1{$\pm$} - \gdef^^b2{$^2$} - \gdef^^b3{$^3$} - \gdef^^b4{\'{}} - \gdef^^b5{$\mu$} - \gdef^^b6{\P} - % - \gdef^^b7{$^.$} - \gdef^^b8{\cedilla\ } - \gdef^^b9{$^1$} - \gdef^^ba{\ordm} - % - \gdef^^bb{\guillemetright} - \gdef^^bc{$1\over4$} - \gdef^^bd{$1\over2$} - \gdef^^be{$3\over4$} - \gdef^^bf{\questiondown} - % - \gdef^^c0{\`A} - \gdef^^c1{\'A} - \gdef^^c2{\^A} - \gdef^^c3{\~A} - \gdef^^c4{\"A} - \gdef^^c5{\ringaccent A} - \gdef^^c6{\AE} - \gdef^^c7{\cedilla C} - \gdef^^c8{\`E} - \gdef^^c9{\'E} - \gdef^^ca{\^E} - \gdef^^cb{\"E} - \gdef^^cc{\`I} - \gdef^^cd{\'I} - \gdef^^ce{\^I} - \gdef^^cf{\"I} - % - \gdef^^d0{\DH} - \gdef^^d1{\~N} - \gdef^^d2{\`O} - \gdef^^d3{\'O} - \gdef^^d4{\^O} - \gdef^^d5{\~O} - \gdef^^d6{\"O} - \gdef^^d7{$\times$} - \gdef^^d8{\O} - \gdef^^d9{\`U} - \gdef^^da{\'U} - \gdef^^db{\^U} - \gdef^^dc{\"U} - \gdef^^dd{\'Y} - \gdef^^de{\TH} - \gdef^^df{\ss} - % - \gdef^^e0{\`a} - \gdef^^e1{\'a} - \gdef^^e2{\^a} - \gdef^^e3{\~a} - \gdef^^e4{\"a} - \gdef^^e5{\ringaccent a} - \gdef^^e6{\ae} - \gdef^^e7{\cedilla c} - \gdef^^e8{\`e} - \gdef^^e9{\'e} - \gdef^^ea{\^e} - \gdef^^eb{\"e} - \gdef^^ec{\`{\dotless i}} - \gdef^^ed{\'{\dotless i}} - \gdef^^ee{\^{\dotless i}} - \gdef^^ef{\"{\dotless i}} - % - \gdef^^f0{\dh} - \gdef^^f1{\~n} - \gdef^^f2{\`o} - \gdef^^f3{\'o} - \gdef^^f4{\^o} - \gdef^^f5{\~o} - \gdef^^f6{\"o} - \gdef^^f7{$\div$} - \gdef^^f8{\o} - \gdef^^f9{\`u} - \gdef^^fa{\'u} - \gdef^^fb{\^u} - \gdef^^fc{\"u} - \gdef^^fd{\'y} - \gdef^^fe{\th} - \gdef^^ff{\"y} -} - -% Latin9 (ISO-8859-15) encoding character definitions. -\def\latninechardefs{% - % Encoding is almost identical to Latin1. - \latonechardefs - % - \gdef^^a4{\euro} - \gdef^^a6{\v S} - \gdef^^a8{\v s} - \gdef^^b4{\v Z} - \gdef^^b8{\v z} - \gdef^^bc{\OE} - \gdef^^bd{\oe} - \gdef^^be{\"Y} -} - -% Latin2 (ISO-8859-2) character definitions. -\def\lattwochardefs{% - \gdef^^a0{\tie} - \gdef^^a1{\ogonek{A}} - \gdef^^a2{\u{}} - \gdef^^a3{\L} - \gdef^^a4{\missingcharmsg{CURRENCY SIGN}} - \gdef^^a5{\v L} - \gdef^^a6{\'S} - \gdef^^a7{\S} - \gdef^^a8{\"{}} - \gdef^^a9{\v S} - \gdef^^aa{\cedilla S} - \gdef^^ab{\v T} - \gdef^^ac{\'Z} - \gdef^^ad{\-} - \gdef^^ae{\v Z} - \gdef^^af{\dotaccent Z} - % - \gdef^^b0{\textdegree} - \gdef^^b1{\ogonek{a}} - \gdef^^b2{\ogonek{ }} - \gdef^^b3{\l} - \gdef^^b4{\'{}} - \gdef^^b5{\v l} - \gdef^^b6{\'s} - \gdef^^b7{\v{}} - \gdef^^b8{\cedilla\ } - \gdef^^b9{\v s} - \gdef^^ba{\cedilla s} - \gdef^^bb{\v t} - \gdef^^bc{\'z} - \gdef^^bd{\H{}} - \gdef^^be{\v z} - \gdef^^bf{\dotaccent z} - % - \gdef^^c0{\'R} - \gdef^^c1{\'A} - \gdef^^c2{\^A} - \gdef^^c3{\u A} - \gdef^^c4{\"A} - \gdef^^c5{\'L} - \gdef^^c6{\'C} - \gdef^^c7{\cedilla C} - \gdef^^c8{\v C} - \gdef^^c9{\'E} - \gdef^^ca{\ogonek{E}} - \gdef^^cb{\"E} - \gdef^^cc{\v E} - \gdef^^cd{\'I} - \gdef^^ce{\^I} - \gdef^^cf{\v D} - % - \gdef^^d0{\DH} - \gdef^^d1{\'N} - \gdef^^d2{\v N} - \gdef^^d3{\'O} - \gdef^^d4{\^O} - \gdef^^d5{\H O} - \gdef^^d6{\"O} - \gdef^^d7{$\times$} - \gdef^^d8{\v R} - \gdef^^d9{\ringaccent U} - \gdef^^da{\'U} - \gdef^^db{\H U} - \gdef^^dc{\"U} - \gdef^^dd{\'Y} - \gdef^^de{\cedilla T} - \gdef^^df{\ss} - % - \gdef^^e0{\'r} - \gdef^^e1{\'a} - \gdef^^e2{\^a} - \gdef^^e3{\u a} - \gdef^^e4{\"a} - \gdef^^e5{\'l} - \gdef^^e6{\'c} - \gdef^^e7{\cedilla c} - \gdef^^e8{\v c} - \gdef^^e9{\'e} - \gdef^^ea{\ogonek{e}} - \gdef^^eb{\"e} - \gdef^^ec{\v e} - \gdef^^ed{\'{\dotless{i}}} - \gdef^^ee{\^{\dotless{i}}} - \gdef^^ef{\v d} - % - \gdef^^f0{\dh} - \gdef^^f1{\'n} - \gdef^^f2{\v n} - \gdef^^f3{\'o} - \gdef^^f4{\^o} - \gdef^^f5{\H o} - \gdef^^f6{\"o} - \gdef^^f7{$\div$} - \gdef^^f8{\v r} - \gdef^^f9{\ringaccent u} - \gdef^^fa{\'u} - \gdef^^fb{\H u} - \gdef^^fc{\"u} - \gdef^^fd{\'y} - \gdef^^fe{\cedilla t} - \gdef^^ff{\dotaccent{}} -} - -% UTF-8 character definitions. -% -% This code to support UTF-8 is based on LaTeX's utf8.def, with some -% changes for Texinfo conventions. It is included here under the GPL by -% permission from Frank Mittelbach and the LaTeX team. -% -\newcount\countUTFx -\newcount\countUTFy -\newcount\countUTFz - -\gdef\UTFviiiTwoOctets#1#2{\expandafter - \UTFviiiDefined\csname u8:#1\string #2\endcsname} -% -\gdef\UTFviiiThreeOctets#1#2#3{\expandafter - \UTFviiiDefined\csname u8:#1\string #2\string #3\endcsname} -% -\gdef\UTFviiiFourOctets#1#2#3#4{\expandafter - \UTFviiiDefined\csname u8:#1\string #2\string #3\string #4\endcsname} - -\gdef\UTFviiiDefined#1{% - \ifx #1\relax - \message{\linenumber Unicode char \string #1 not defined for Texinfo}% - \else - \expandafter #1% - \fi -} - -\begingroup - \catcode`\~13 - \catcode`\"12 - - \def\UTFviiiLoop{% - \global\catcode\countUTFx\active - \uccode`\~\countUTFx - \uppercase\expandafter{\UTFviiiTmp}% - \advance\countUTFx by 1 - \ifnum\countUTFx < \countUTFy - \expandafter\UTFviiiLoop - \fi} - - \countUTFx = "C2 - \countUTFy = "E0 - \def\UTFviiiTmp{% - \xdef~{\noexpand\UTFviiiTwoOctets\string~}} - \UTFviiiLoop - - \countUTFx = "E0 - \countUTFy = "F0 - \def\UTFviiiTmp{% - \xdef~{\noexpand\UTFviiiThreeOctets\string~}} - \UTFviiiLoop - - \countUTFx = "F0 - \countUTFy = "F4 - \def\UTFviiiTmp{% - \xdef~{\noexpand\UTFviiiFourOctets\string~}} - \UTFviiiLoop -\endgroup - -\begingroup - \catcode`\"=12 - \catcode`\<=12 - \catcode`\.=12 - \catcode`\,=12 - \catcode`\;=12 - \catcode`\!=12 - \catcode`\~=13 - - \gdef\DeclareUnicodeCharacter#1#2{% - \countUTFz = "#1\relax - %\wlog{\space\space defining Unicode char U+#1 (decimal \the\countUTFz)}% - \begingroup - \parseXMLCharref - \def\UTFviiiTwoOctets##1##2{% - \csname u8:##1\string ##2\endcsname}% - \def\UTFviiiThreeOctets##1##2##3{% - \csname u8:##1\string ##2\string ##3\endcsname}% - \def\UTFviiiFourOctets##1##2##3##4{% - \csname u8:##1\string ##2\string ##3\string ##4\endcsname}% - \expandafter\expandafter\expandafter\expandafter - \expandafter\expandafter\expandafter - \gdef\UTFviiiTmp{#2}% - \endgroup} - - \gdef\parseXMLCharref{% - \ifnum\countUTFz < "A0\relax - \errhelp = \EMsimple - \errmessage{Cannot define Unicode char value < 00A0}% - \else\ifnum\countUTFz < "800\relax - \parseUTFviiiA,% - \parseUTFviiiB C\UTFviiiTwoOctets.,% - \else\ifnum\countUTFz < "10000\relax - \parseUTFviiiA;% - \parseUTFviiiA,% - \parseUTFviiiB E\UTFviiiThreeOctets.{,;}% - \else - \parseUTFviiiA;% - \parseUTFviiiA,% - \parseUTFviiiA!% - \parseUTFviiiB F\UTFviiiFourOctets.{!,;}% - \fi\fi\fi - } - - \gdef\parseUTFviiiA#1{% - \countUTFx = \countUTFz - \divide\countUTFz by 64 - \countUTFy = \countUTFz - \multiply\countUTFz by 64 - \advance\countUTFx by -\countUTFz - \advance\countUTFx by 128 - \uccode `#1\countUTFx - \countUTFz = \countUTFy} - - \gdef\parseUTFviiiB#1#2#3#4{% - \advance\countUTFz by "#10\relax - \uccode `#3\countUTFz - \uppercase{\gdef\UTFviiiTmp{#2#3#4}}} -\endgroup - -\def\utfeightchardefs{% - \DeclareUnicodeCharacter{00A0}{\tie} - \DeclareUnicodeCharacter{00A1}{\exclamdown} - \DeclareUnicodeCharacter{00A3}{\pounds} - \DeclareUnicodeCharacter{00A8}{\"{ }} - \DeclareUnicodeCharacter{00A9}{\copyright} - \DeclareUnicodeCharacter{00AA}{\ordf} - \DeclareUnicodeCharacter{00AB}{\guillemetleft} - \DeclareUnicodeCharacter{00AD}{\-} - \DeclareUnicodeCharacter{00AE}{\registeredsymbol} - \DeclareUnicodeCharacter{00AF}{\={ }} - - \DeclareUnicodeCharacter{00B0}{\ringaccent{ }} - \DeclareUnicodeCharacter{00B4}{\'{ }} - \DeclareUnicodeCharacter{00B8}{\cedilla{ }} - \DeclareUnicodeCharacter{00BA}{\ordm} - \DeclareUnicodeCharacter{00BB}{\guillemetright} - \DeclareUnicodeCharacter{00BF}{\questiondown} - - \DeclareUnicodeCharacter{00C0}{\`A} - \DeclareUnicodeCharacter{00C1}{\'A} - \DeclareUnicodeCharacter{00C2}{\^A} - \DeclareUnicodeCharacter{00C3}{\~A} - \DeclareUnicodeCharacter{00C4}{\"A} - \DeclareUnicodeCharacter{00C5}{\AA} - \DeclareUnicodeCharacter{00C6}{\AE} - \DeclareUnicodeCharacter{00C7}{\cedilla{C}} - \DeclareUnicodeCharacter{00C8}{\`E} - \DeclareUnicodeCharacter{00C9}{\'E} - \DeclareUnicodeCharacter{00CA}{\^E} - \DeclareUnicodeCharacter{00CB}{\"E} - \DeclareUnicodeCharacter{00CC}{\`I} - \DeclareUnicodeCharacter{00CD}{\'I} - \DeclareUnicodeCharacter{00CE}{\^I} - \DeclareUnicodeCharacter{00CF}{\"I} - - \DeclareUnicodeCharacter{00D0}{\DH} - \DeclareUnicodeCharacter{00D1}{\~N} - \DeclareUnicodeCharacter{00D2}{\`O} - \DeclareUnicodeCharacter{00D3}{\'O} - \DeclareUnicodeCharacter{00D4}{\^O} - \DeclareUnicodeCharacter{00D5}{\~O} - \DeclareUnicodeCharacter{00D6}{\"O} - \DeclareUnicodeCharacter{00D8}{\O} - \DeclareUnicodeCharacter{00D9}{\`U} - \DeclareUnicodeCharacter{00DA}{\'U} - \DeclareUnicodeCharacter{00DB}{\^U} - \DeclareUnicodeCharacter{00DC}{\"U} - \DeclareUnicodeCharacter{00DD}{\'Y} - \DeclareUnicodeCharacter{00DE}{\TH} - \DeclareUnicodeCharacter{00DF}{\ss} - - \DeclareUnicodeCharacter{00E0}{\`a} - \DeclareUnicodeCharacter{00E1}{\'a} - \DeclareUnicodeCharacter{00E2}{\^a} - \DeclareUnicodeCharacter{00E3}{\~a} - \DeclareUnicodeCharacter{00E4}{\"a} - \DeclareUnicodeCharacter{00E5}{\aa} - \DeclareUnicodeCharacter{00E6}{\ae} - \DeclareUnicodeCharacter{00E7}{\cedilla{c}} - \DeclareUnicodeCharacter{00E8}{\`e} - \DeclareUnicodeCharacter{00E9}{\'e} - \DeclareUnicodeCharacter{00EA}{\^e} - \DeclareUnicodeCharacter{00EB}{\"e} - \DeclareUnicodeCharacter{00EC}{\`{\dotless{i}}} - \DeclareUnicodeCharacter{00ED}{\'{\dotless{i}}} - \DeclareUnicodeCharacter{00EE}{\^{\dotless{i}}} - \DeclareUnicodeCharacter{00EF}{\"{\dotless{i}}} - - \DeclareUnicodeCharacter{00F0}{\dh} - \DeclareUnicodeCharacter{00F1}{\~n} - \DeclareUnicodeCharacter{00F2}{\`o} - \DeclareUnicodeCharacter{00F3}{\'o} - \DeclareUnicodeCharacter{00F4}{\^o} - \DeclareUnicodeCharacter{00F5}{\~o} - \DeclareUnicodeCharacter{00F6}{\"o} - \DeclareUnicodeCharacter{00F8}{\o} - \DeclareUnicodeCharacter{00F9}{\`u} - \DeclareUnicodeCharacter{00FA}{\'u} - \DeclareUnicodeCharacter{00FB}{\^u} - \DeclareUnicodeCharacter{00FC}{\"u} - \DeclareUnicodeCharacter{00FD}{\'y} - \DeclareUnicodeCharacter{00FE}{\th} - \DeclareUnicodeCharacter{00FF}{\"y} - - \DeclareUnicodeCharacter{0100}{\=A} - \DeclareUnicodeCharacter{0101}{\=a} - \DeclareUnicodeCharacter{0102}{\u{A}} - \DeclareUnicodeCharacter{0103}{\u{a}} - \DeclareUnicodeCharacter{0104}{\ogonek{A}} - \DeclareUnicodeCharacter{0105}{\ogonek{a}} - \DeclareUnicodeCharacter{0106}{\'C} - \DeclareUnicodeCharacter{0107}{\'c} - \DeclareUnicodeCharacter{0108}{\^C} - \DeclareUnicodeCharacter{0109}{\^c} - \DeclareUnicodeCharacter{0118}{\ogonek{E}} - \DeclareUnicodeCharacter{0119}{\ogonek{e}} - \DeclareUnicodeCharacter{010A}{\dotaccent{C}} - \DeclareUnicodeCharacter{010B}{\dotaccent{c}} - \DeclareUnicodeCharacter{010C}{\v{C}} - \DeclareUnicodeCharacter{010D}{\v{c}} - \DeclareUnicodeCharacter{010E}{\v{D}} - - \DeclareUnicodeCharacter{0112}{\=E} - \DeclareUnicodeCharacter{0113}{\=e} - \DeclareUnicodeCharacter{0114}{\u{E}} - \DeclareUnicodeCharacter{0115}{\u{e}} - \DeclareUnicodeCharacter{0116}{\dotaccent{E}} - \DeclareUnicodeCharacter{0117}{\dotaccent{e}} - \DeclareUnicodeCharacter{011A}{\v{E}} - \DeclareUnicodeCharacter{011B}{\v{e}} - \DeclareUnicodeCharacter{011C}{\^G} - \DeclareUnicodeCharacter{011D}{\^g} - \DeclareUnicodeCharacter{011E}{\u{G}} - \DeclareUnicodeCharacter{011F}{\u{g}} - - \DeclareUnicodeCharacter{0120}{\dotaccent{G}} - \DeclareUnicodeCharacter{0121}{\dotaccent{g}} - \DeclareUnicodeCharacter{0124}{\^H} - \DeclareUnicodeCharacter{0125}{\^h} - \DeclareUnicodeCharacter{0128}{\~I} - \DeclareUnicodeCharacter{0129}{\~{\dotless{i}}} - \DeclareUnicodeCharacter{012A}{\=I} - \DeclareUnicodeCharacter{012B}{\={\dotless{i}}} - \DeclareUnicodeCharacter{012C}{\u{I}} - \DeclareUnicodeCharacter{012D}{\u{\dotless{i}}} - - \DeclareUnicodeCharacter{0130}{\dotaccent{I}} - \DeclareUnicodeCharacter{0131}{\dotless{i}} - \DeclareUnicodeCharacter{0132}{IJ} - \DeclareUnicodeCharacter{0133}{ij} - \DeclareUnicodeCharacter{0134}{\^J} - \DeclareUnicodeCharacter{0135}{\^{\dotless{j}}} - \DeclareUnicodeCharacter{0139}{\'L} - \DeclareUnicodeCharacter{013A}{\'l} - - \DeclareUnicodeCharacter{0141}{\L} - \DeclareUnicodeCharacter{0142}{\l} - \DeclareUnicodeCharacter{0143}{\'N} - \DeclareUnicodeCharacter{0144}{\'n} - \DeclareUnicodeCharacter{0147}{\v{N}} - \DeclareUnicodeCharacter{0148}{\v{n}} - \DeclareUnicodeCharacter{014C}{\=O} - \DeclareUnicodeCharacter{014D}{\=o} - \DeclareUnicodeCharacter{014E}{\u{O}} - \DeclareUnicodeCharacter{014F}{\u{o}} - - \DeclareUnicodeCharacter{0150}{\H{O}} - \DeclareUnicodeCharacter{0151}{\H{o}} - \DeclareUnicodeCharacter{0152}{\OE} - \DeclareUnicodeCharacter{0153}{\oe} - \DeclareUnicodeCharacter{0154}{\'R} - \DeclareUnicodeCharacter{0155}{\'r} - \DeclareUnicodeCharacter{0158}{\v{R}} - \DeclareUnicodeCharacter{0159}{\v{r}} - \DeclareUnicodeCharacter{015A}{\'S} - \DeclareUnicodeCharacter{015B}{\'s} - \DeclareUnicodeCharacter{015C}{\^S} - \DeclareUnicodeCharacter{015D}{\^s} - \DeclareUnicodeCharacter{015E}{\cedilla{S}} - \DeclareUnicodeCharacter{015F}{\cedilla{s}} - - \DeclareUnicodeCharacter{0160}{\v{S}} - \DeclareUnicodeCharacter{0161}{\v{s}} - \DeclareUnicodeCharacter{0162}{\cedilla{t}} - \DeclareUnicodeCharacter{0163}{\cedilla{T}} - \DeclareUnicodeCharacter{0164}{\v{T}} - - \DeclareUnicodeCharacter{0168}{\~U} - \DeclareUnicodeCharacter{0169}{\~u} - \DeclareUnicodeCharacter{016A}{\=U} - \DeclareUnicodeCharacter{016B}{\=u} - \DeclareUnicodeCharacter{016C}{\u{U}} - \DeclareUnicodeCharacter{016D}{\u{u}} - \DeclareUnicodeCharacter{016E}{\ringaccent{U}} - \DeclareUnicodeCharacter{016F}{\ringaccent{u}} - - \DeclareUnicodeCharacter{0170}{\H{U}} - \DeclareUnicodeCharacter{0171}{\H{u}} - \DeclareUnicodeCharacter{0174}{\^W} - \DeclareUnicodeCharacter{0175}{\^w} - \DeclareUnicodeCharacter{0176}{\^Y} - \DeclareUnicodeCharacter{0177}{\^y} - \DeclareUnicodeCharacter{0178}{\"Y} - \DeclareUnicodeCharacter{0179}{\'Z} - \DeclareUnicodeCharacter{017A}{\'z} - \DeclareUnicodeCharacter{017B}{\dotaccent{Z}} - \DeclareUnicodeCharacter{017C}{\dotaccent{z}} - \DeclareUnicodeCharacter{017D}{\v{Z}} - \DeclareUnicodeCharacter{017E}{\v{z}} - - \DeclareUnicodeCharacter{01C4}{D\v{Z}} - \DeclareUnicodeCharacter{01C5}{D\v{z}} - \DeclareUnicodeCharacter{01C6}{d\v{z}} - \DeclareUnicodeCharacter{01C7}{LJ} - \DeclareUnicodeCharacter{01C8}{Lj} - \DeclareUnicodeCharacter{01C9}{lj} - \DeclareUnicodeCharacter{01CA}{NJ} - \DeclareUnicodeCharacter{01CB}{Nj} - \DeclareUnicodeCharacter{01CC}{nj} - \DeclareUnicodeCharacter{01CD}{\v{A}} - \DeclareUnicodeCharacter{01CE}{\v{a}} - \DeclareUnicodeCharacter{01CF}{\v{I}} - - \DeclareUnicodeCharacter{01D0}{\v{\dotless{i}}} - \DeclareUnicodeCharacter{01D1}{\v{O}} - \DeclareUnicodeCharacter{01D2}{\v{o}} - \DeclareUnicodeCharacter{01D3}{\v{U}} - \DeclareUnicodeCharacter{01D4}{\v{u}} - - \DeclareUnicodeCharacter{01E2}{\={\AE}} - \DeclareUnicodeCharacter{01E3}{\={\ae}} - \DeclareUnicodeCharacter{01E6}{\v{G}} - \DeclareUnicodeCharacter{01E7}{\v{g}} - \DeclareUnicodeCharacter{01E8}{\v{K}} - \DeclareUnicodeCharacter{01E9}{\v{k}} - - \DeclareUnicodeCharacter{01F0}{\v{\dotless{j}}} - \DeclareUnicodeCharacter{01F1}{DZ} - \DeclareUnicodeCharacter{01F2}{Dz} - \DeclareUnicodeCharacter{01F3}{dz} - \DeclareUnicodeCharacter{01F4}{\'G} - \DeclareUnicodeCharacter{01F5}{\'g} - \DeclareUnicodeCharacter{01F8}{\`N} - \DeclareUnicodeCharacter{01F9}{\`n} - \DeclareUnicodeCharacter{01FC}{\'{\AE}} - \DeclareUnicodeCharacter{01FD}{\'{\ae}} - \DeclareUnicodeCharacter{01FE}{\'{\O}} - \DeclareUnicodeCharacter{01FF}{\'{\o}} - - \DeclareUnicodeCharacter{021E}{\v{H}} - \DeclareUnicodeCharacter{021F}{\v{h}} - - \DeclareUnicodeCharacter{0226}{\dotaccent{A}} - \DeclareUnicodeCharacter{0227}{\dotaccent{a}} - \DeclareUnicodeCharacter{0228}{\cedilla{E}} - \DeclareUnicodeCharacter{0229}{\cedilla{e}} - \DeclareUnicodeCharacter{022E}{\dotaccent{O}} - \DeclareUnicodeCharacter{022F}{\dotaccent{o}} - - \DeclareUnicodeCharacter{0232}{\=Y} - \DeclareUnicodeCharacter{0233}{\=y} - \DeclareUnicodeCharacter{0237}{\dotless{j}} - - \DeclareUnicodeCharacter{02DB}{\ogonek{ }} - - \DeclareUnicodeCharacter{1E02}{\dotaccent{B}} - \DeclareUnicodeCharacter{1E03}{\dotaccent{b}} - \DeclareUnicodeCharacter{1E04}{\udotaccent{B}} - \DeclareUnicodeCharacter{1E05}{\udotaccent{b}} - \DeclareUnicodeCharacter{1E06}{\ubaraccent{B}} - \DeclareUnicodeCharacter{1E07}{\ubaraccent{b}} - \DeclareUnicodeCharacter{1E0A}{\dotaccent{D}} - \DeclareUnicodeCharacter{1E0B}{\dotaccent{d}} - \DeclareUnicodeCharacter{1E0C}{\udotaccent{D}} - \DeclareUnicodeCharacter{1E0D}{\udotaccent{d}} - \DeclareUnicodeCharacter{1E0E}{\ubaraccent{D}} - \DeclareUnicodeCharacter{1E0F}{\ubaraccent{d}} - - \DeclareUnicodeCharacter{1E1E}{\dotaccent{F}} - \DeclareUnicodeCharacter{1E1F}{\dotaccent{f}} - - \DeclareUnicodeCharacter{1E20}{\=G} - \DeclareUnicodeCharacter{1E21}{\=g} - \DeclareUnicodeCharacter{1E22}{\dotaccent{H}} - \DeclareUnicodeCharacter{1E23}{\dotaccent{h}} - \DeclareUnicodeCharacter{1E24}{\udotaccent{H}} - \DeclareUnicodeCharacter{1E25}{\udotaccent{h}} - \DeclareUnicodeCharacter{1E26}{\"H} - \DeclareUnicodeCharacter{1E27}{\"h} - - \DeclareUnicodeCharacter{1E30}{\'K} - \DeclareUnicodeCharacter{1E31}{\'k} - \DeclareUnicodeCharacter{1E32}{\udotaccent{K}} - \DeclareUnicodeCharacter{1E33}{\udotaccent{k}} - \DeclareUnicodeCharacter{1E34}{\ubaraccent{K}} - \DeclareUnicodeCharacter{1E35}{\ubaraccent{k}} - \DeclareUnicodeCharacter{1E36}{\udotaccent{L}} - \DeclareUnicodeCharacter{1E37}{\udotaccent{l}} - \DeclareUnicodeCharacter{1E3A}{\ubaraccent{L}} - \DeclareUnicodeCharacter{1E3B}{\ubaraccent{l}} - \DeclareUnicodeCharacter{1E3E}{\'M} - \DeclareUnicodeCharacter{1E3F}{\'m} - - \DeclareUnicodeCharacter{1E40}{\dotaccent{M}} - \DeclareUnicodeCharacter{1E41}{\dotaccent{m}} - \DeclareUnicodeCharacter{1E42}{\udotaccent{M}} - \DeclareUnicodeCharacter{1E43}{\udotaccent{m}} - \DeclareUnicodeCharacter{1E44}{\dotaccent{N}} - \DeclareUnicodeCharacter{1E45}{\dotaccent{n}} - \DeclareUnicodeCharacter{1E46}{\udotaccent{N}} - \DeclareUnicodeCharacter{1E47}{\udotaccent{n}} - \DeclareUnicodeCharacter{1E48}{\ubaraccent{N}} - \DeclareUnicodeCharacter{1E49}{\ubaraccent{n}} - - \DeclareUnicodeCharacter{1E54}{\'P} - \DeclareUnicodeCharacter{1E55}{\'p} - \DeclareUnicodeCharacter{1E56}{\dotaccent{P}} - \DeclareUnicodeCharacter{1E57}{\dotaccent{p}} - \DeclareUnicodeCharacter{1E58}{\dotaccent{R}} - \DeclareUnicodeCharacter{1E59}{\dotaccent{r}} - \DeclareUnicodeCharacter{1E5A}{\udotaccent{R}} - \DeclareUnicodeCharacter{1E5B}{\udotaccent{r}} - \DeclareUnicodeCharacter{1E5E}{\ubaraccent{R}} - \DeclareUnicodeCharacter{1E5F}{\ubaraccent{r}} - - \DeclareUnicodeCharacter{1E60}{\dotaccent{S}} - \DeclareUnicodeCharacter{1E61}{\dotaccent{s}} - \DeclareUnicodeCharacter{1E62}{\udotaccent{S}} - \DeclareUnicodeCharacter{1E63}{\udotaccent{s}} - \DeclareUnicodeCharacter{1E6A}{\dotaccent{T}} - \DeclareUnicodeCharacter{1E6B}{\dotaccent{t}} - \DeclareUnicodeCharacter{1E6C}{\udotaccent{T}} - \DeclareUnicodeCharacter{1E6D}{\udotaccent{t}} - \DeclareUnicodeCharacter{1E6E}{\ubaraccent{T}} - \DeclareUnicodeCharacter{1E6F}{\ubaraccent{t}} - - \DeclareUnicodeCharacter{1E7C}{\~V} - \DeclareUnicodeCharacter{1E7D}{\~v} - \DeclareUnicodeCharacter{1E7E}{\udotaccent{V}} - \DeclareUnicodeCharacter{1E7F}{\udotaccent{v}} - - \DeclareUnicodeCharacter{1E80}{\`W} - \DeclareUnicodeCharacter{1E81}{\`w} - \DeclareUnicodeCharacter{1E82}{\'W} - \DeclareUnicodeCharacter{1E83}{\'w} - \DeclareUnicodeCharacter{1E84}{\"W} - \DeclareUnicodeCharacter{1E85}{\"w} - \DeclareUnicodeCharacter{1E86}{\dotaccent{W}} - \DeclareUnicodeCharacter{1E87}{\dotaccent{w}} - \DeclareUnicodeCharacter{1E88}{\udotaccent{W}} - \DeclareUnicodeCharacter{1E89}{\udotaccent{w}} - \DeclareUnicodeCharacter{1E8A}{\dotaccent{X}} - \DeclareUnicodeCharacter{1E8B}{\dotaccent{x}} - \DeclareUnicodeCharacter{1E8C}{\"X} - \DeclareUnicodeCharacter{1E8D}{\"x} - \DeclareUnicodeCharacter{1E8E}{\dotaccent{Y}} - \DeclareUnicodeCharacter{1E8F}{\dotaccent{y}} - - \DeclareUnicodeCharacter{1E90}{\^Z} - \DeclareUnicodeCharacter{1E91}{\^z} - \DeclareUnicodeCharacter{1E92}{\udotaccent{Z}} - \DeclareUnicodeCharacter{1E93}{\udotaccent{z}} - \DeclareUnicodeCharacter{1E94}{\ubaraccent{Z}} - \DeclareUnicodeCharacter{1E95}{\ubaraccent{z}} - \DeclareUnicodeCharacter{1E96}{\ubaraccent{h}} - \DeclareUnicodeCharacter{1E97}{\"t} - \DeclareUnicodeCharacter{1E98}{\ringaccent{w}} - \DeclareUnicodeCharacter{1E99}{\ringaccent{y}} - - \DeclareUnicodeCharacter{1EA0}{\udotaccent{A}} - \DeclareUnicodeCharacter{1EA1}{\udotaccent{a}} - - \DeclareUnicodeCharacter{1EB8}{\udotaccent{E}} - \DeclareUnicodeCharacter{1EB9}{\udotaccent{e}} - \DeclareUnicodeCharacter{1EBC}{\~E} - \DeclareUnicodeCharacter{1EBD}{\~e} - - \DeclareUnicodeCharacter{1ECA}{\udotaccent{I}} - \DeclareUnicodeCharacter{1ECB}{\udotaccent{i}} - \DeclareUnicodeCharacter{1ECC}{\udotaccent{O}} - \DeclareUnicodeCharacter{1ECD}{\udotaccent{o}} - - \DeclareUnicodeCharacter{1EE4}{\udotaccent{U}} - \DeclareUnicodeCharacter{1EE5}{\udotaccent{u}} - - \DeclareUnicodeCharacter{1EF2}{\`Y} - \DeclareUnicodeCharacter{1EF3}{\`y} - \DeclareUnicodeCharacter{1EF4}{\udotaccent{Y}} - - \DeclareUnicodeCharacter{1EF8}{\~Y} - \DeclareUnicodeCharacter{1EF9}{\~y} - - \DeclareUnicodeCharacter{2013}{--} - \DeclareUnicodeCharacter{2014}{---} - \DeclareUnicodeCharacter{2018}{\quoteleft} - \DeclareUnicodeCharacter{2019}{\quoteright} - \DeclareUnicodeCharacter{201A}{\quotesinglbase} - \DeclareUnicodeCharacter{201C}{\quotedblleft} - \DeclareUnicodeCharacter{201D}{\quotedblright} - \DeclareUnicodeCharacter{201E}{\quotedblbase} - \DeclareUnicodeCharacter{2022}{\bullet} - \DeclareUnicodeCharacter{2026}{\dots} - \DeclareUnicodeCharacter{2039}{\guilsinglleft} - \DeclareUnicodeCharacter{203A}{\guilsinglright} - \DeclareUnicodeCharacter{20AC}{\euro} - - \DeclareUnicodeCharacter{2192}{\expansion} - \DeclareUnicodeCharacter{21D2}{\result} - - \DeclareUnicodeCharacter{2212}{\minus} - \DeclareUnicodeCharacter{2217}{\point} - \DeclareUnicodeCharacter{2261}{\equiv} -}% end of \utfeightchardefs - - -% US-ASCII character definitions. -\def\asciichardefs{% nothing need be done - \relax -} - -% Make non-ASCII characters printable again for compatibility with -% existing Texinfo documents that may use them, even without declaring a -% document encoding. -% -\setnonasciicharscatcode \other - - -\message{formatting,} - -\newdimen\defaultparindent \defaultparindent = 15pt - -\chapheadingskip = 15pt plus 4pt minus 2pt -\secheadingskip = 12pt plus 3pt minus 2pt -\subsecheadingskip = 9pt plus 2pt minus 2pt - -% Prevent underfull vbox error messages. -\vbadness = 10000 - -% Don't be very finicky about underfull hboxes, either. -\hbadness = 6666 - -% Following George Bush, get rid of widows and orphans. -\widowpenalty=10000 -\clubpenalty=10000 - -% Use TeX 3.0's \emergencystretch to help line breaking, but if we're -% using an old version of TeX, don't do anything. We want the amount of -% stretch added to depend on the line length, hence the dependence on -% \hsize. We call this whenever the paper size is set. -% -\def\setemergencystretch{% - \ifx\emergencystretch\thisisundefined - % Allow us to assign to \emergencystretch anyway. - \def\emergencystretch{\dimen0}% - \else - \emergencystretch = .15\hsize - \fi -} - -% Parameters in order: 1) textheight; 2) textwidth; -% 3) voffset; 4) hoffset; 5) binding offset; 6) topskip; -% 7) physical page height; 8) physical page width. -% -% We also call \setleading{\textleading}, so the caller should define -% \textleading. The caller should also set \parskip. -% -\def\internalpagesizes#1#2#3#4#5#6#7#8{% - \voffset = #3\relax - \topskip = #6\relax - \splittopskip = \topskip - % - \vsize = #1\relax - \advance\vsize by \topskip - \outervsize = \vsize - \advance\outervsize by 2\topandbottommargin - \pageheight = \vsize - % - \hsize = #2\relax - \outerhsize = \hsize - \advance\outerhsize by 0.5in - \pagewidth = \hsize - % - \normaloffset = #4\relax - \bindingoffset = #5\relax - % - \ifpdf - \pdfpageheight #7\relax - \pdfpagewidth #8\relax - % if we don't reset these, they will remain at "1 true in" of - % whatever layout pdftex was dumped with. - \pdfhorigin = 1 true in - \pdfvorigin = 1 true in - \fi - % - \setleading{\textleading} - % - \parindent = \defaultparindent - \setemergencystretch -} - -% @letterpaper (the default). -\def\letterpaper{{\globaldefs = 1 - \parskip = 3pt plus 2pt minus 1pt - \textleading = 13.2pt - % - % If page is nothing but text, make it come out even. - \internalpagesizes{607.2pt}{6in}% that's 46 lines - {\voffset}{.25in}% - {\bindingoffset}{36pt}% - {11in}{8.5in}% -}} - -% Use @smallbook to reset parameters for 7x9.25 trim size. -\def\smallbook{{\globaldefs = 1 - \parskip = 2pt plus 1pt - \textleading = 12pt - % - \internalpagesizes{7.5in}{5in}% - {-.2in}{0in}% - {\bindingoffset}{16pt}% - {9.25in}{7in}% - % - \lispnarrowing = 0.3in - \tolerance = 700 - \hfuzz = 1pt - \contentsrightmargin = 0pt - \defbodyindent = .5cm -}} - -% Use @smallerbook to reset parameters for 6x9 trim size. -% (Just testing, parameters still in flux.) -\def\smallerbook{{\globaldefs = 1 - \parskip = 1.5pt plus 1pt - \textleading = 12pt - % - \internalpagesizes{7.4in}{4.8in}% - {-.2in}{-.4in}% - {0pt}{14pt}% - {9in}{6in}% - % - \lispnarrowing = 0.25in - \tolerance = 700 - \hfuzz = 1pt - \contentsrightmargin = 0pt - \defbodyindent = .4cm -}} - -% Use @afourpaper to print on European A4 paper. -\def\afourpaper{{\globaldefs = 1 - \parskip = 3pt plus 2pt minus 1pt - \textleading = 13.2pt - % - % Double-side printing via postscript on Laserjet 4050 - % prints double-sided nicely when \bindingoffset=10mm and \hoffset=-6mm. - % To change the settings for a different printer or situation, adjust - % \normaloffset until the front-side and back-side texts align. Then - % do the same for \bindingoffset. You can set these for testing in - % your texinfo source file like this: - % @tex - % \global\normaloffset = -6mm - % \global\bindingoffset = 10mm - % @end tex - \internalpagesizes{673.2pt}{160mm}% that's 51 lines - {\voffset}{\hoffset}% - {\bindingoffset}{44pt}% - {297mm}{210mm}% - % - \tolerance = 700 - \hfuzz = 1pt - \contentsrightmargin = 0pt - \defbodyindent = 5mm -}} - -% Use @afivepaper to print on European A5 paper. -% From romildo@urano.iceb.ufop.br, 2 July 2000. -% He also recommends making @example and @lisp be small. -\def\afivepaper{{\globaldefs = 1 - \parskip = 2pt plus 1pt minus 0.1pt - \textleading = 12.5pt - % - \internalpagesizes{160mm}{120mm}% - {\voffset}{\hoffset}% - {\bindingoffset}{8pt}% - {210mm}{148mm}% - % - \lispnarrowing = 0.2in - \tolerance = 800 - \hfuzz = 1.2pt - \contentsrightmargin = 0pt - \defbodyindent = 2mm - \tableindent = 12mm -}} - -% A specific text layout, 24x15cm overall, intended for A4 paper. -\def\afourlatex{{\globaldefs = 1 - \afourpaper - \internalpagesizes{237mm}{150mm}% - {\voffset}{4.6mm}% - {\bindingoffset}{7mm}% - {297mm}{210mm}% - % - % Must explicitly reset to 0 because we call \afourpaper. - \globaldefs = 0 -}} - -% Use @afourwide to print on A4 paper in landscape format. -\def\afourwide{{\globaldefs = 1 - \afourpaper - \internalpagesizes{241mm}{165mm}% - {\voffset}{-2.95mm}% - {\bindingoffset}{7mm}% - {297mm}{210mm}% - \globaldefs = 0 -}} - -% @pagesizes TEXTHEIGHT[,TEXTWIDTH] -% Perhaps we should allow setting the margins, \topskip, \parskip, -% and/or leading, also. Or perhaps we should compute them somehow. -% -\parseargdef\pagesizes{\pagesizesyyy #1,,\finish} -\def\pagesizesyyy#1,#2,#3\finish{{% - \setbox0 = \hbox{\ignorespaces #2}\ifdim\wd0 > 0pt \hsize=#2\relax \fi - \globaldefs = 1 - % - \parskip = 3pt plus 2pt minus 1pt - \setleading{\textleading}% - % - \dimen0 = #1\relax - \advance\dimen0 by \voffset - % - \dimen2 = \hsize - \advance\dimen2 by \normaloffset - % - \internalpagesizes{#1}{\hsize}% - {\voffset}{\normaloffset}% - {\bindingoffset}{44pt}% - {\dimen0}{\dimen2}% -}} - -% Set default to letter. -% -\letterpaper - - -\message{and turning on texinfo input format.} - -\def^^L{\par} % remove \outer, so ^L can appear in an @comment - -% DEL is a comment character, in case @c does not suffice. -\catcode`\^^? = 14 - -% Define macros to output various characters with catcode for normal text. -\catcode`\"=\other \def\normaldoublequote{"} -\catcode`\$=\other \def\normaldollar{$}%$ font-lock fix -\catcode`\+=\other \def\normalplus{+} -\catcode`\<=\other \def\normalless{<} -\catcode`\>=\other \def\normalgreater{>} -\catcode`\^=\other \def\normalcaret{^} -\catcode`\_=\other \def\normalunderscore{_} -\catcode`\|=\other \def\normalverticalbar{|} -\catcode`\~=\other \def\normaltilde{~} - -% This macro is used to make a character print one way in \tt -% (where it can probably be output as-is), and another way in other fonts, -% where something hairier probably needs to be done. -% -% #1 is what to print if we are indeed using \tt; #2 is what to print -% otherwise. Since all the Computer Modern typewriter fonts have zero -% interword stretch (and shrink), and it is reasonable to expect all -% typewriter fonts to have this, we can check that font parameter. -% -\def\ifusingtt#1#2{\ifdim \fontdimen3\font=0pt #1\else #2\fi} - -% Same as above, but check for italic font. Actually this also catches -% non-italic slanted fonts since it is impossible to distinguish them from -% italic fonts. But since this is only used by $ and it uses \sl anyway -% this is not a problem. -\def\ifusingit#1#2{\ifdim \fontdimen1\font>0pt #1\else #2\fi} - -% Turn off all special characters except @ -% (and those which the user can use as if they were ordinary). -% Most of these we simply print from the \tt font, but for some, we can -% use math or other variants that look better in normal text. - -\catcode`\"=\active -\def\activedoublequote{{\tt\char34}} -\let"=\activedoublequote -\catcode`\~=\active -\def~{{\tt\char126}} -\chardef\hat=`\^ -\catcode`\^=\active -\def^{{\tt \hat}} - -\catcode`\_=\active -\def_{\ifusingtt\normalunderscore\_} -\let\realunder=_ -% Subroutine for the previous macro. -\def\_{\leavevmode \kern.07em \vbox{\hrule width.3em height.1ex}\kern .07em } - -\catcode`\|=\active -\def|{{\tt\char124}} -\chardef \less=`\< -\catcode`\<=\active -\def<{{\tt \less}} -\chardef \gtr=`\> -\catcode`\>=\active -\def>{{\tt \gtr}} -\catcode`\+=\active -\def+{{\tt \char 43}} -\catcode`\$=\active -\def${\ifusingit{{\sl\$}}\normaldollar}%$ font-lock fix - -% If a .fmt file is being used, characters that might appear in a file -% name cannot be active until we have parsed the command line. -% So turn them off again, and have \everyjob (or @setfilename) turn them on. -% \otherifyactive is called near the end of this file. -\def\otherifyactive{\catcode`+=\other \catcode`\_=\other} - -% Used sometimes to turn off (effectively) the active characters even after -% parsing them. -\def\turnoffactive{% - \normalturnoffactive - \otherbackslash -} - -\catcode`\@=0 - -% \backslashcurfont outputs one backslash character in current font, -% as in \char`\\. -\global\chardef\backslashcurfont=`\\ -\global\let\rawbackslashxx=\backslashcurfont % let existing .??s files work - -% \realbackslash is an actual character `\' with catcode other, and -% \doublebackslash is two of them (for the pdf outlines). -{\catcode`\\=\other @gdef@realbackslash{\} @gdef@doublebackslash{\\}} - -% In texinfo, backslash is an active character; it prints the backslash -% in fixed width font. -\catcode`\\=\active % @ for escape char from now on. - -% The story here is that in math mode, the \char of \backslashcurfont -% ends up printing the roman \ from the math symbol font (because \char -% in math mode uses the \mathcode, and plain.tex sets -% \mathcode`\\="026E). It seems better for @backslashchar{} to always -% print a typewriter backslash, hence we use an explicit \mathchar, -% which is the decimal equivalent of "715c (class 7, e.g., use \fam; -% ignored family value; char position "5C). We can't use " for the -% usual hex value because it has already been made active. -@def@normalbackslash{{@tt @ifmmode @mathchar29020 @else @backslashcurfont @fi}} -@let@backslashchar = @normalbackslash % @backslashchar{} is for user documents. - -% On startup, @fixbackslash assigns: -% @let \ = @normalbackslash -% \rawbackslash defines an active \ to do \backslashcurfont. -% \otherbackslash defines an active \ to be a literal `\' character with -% catcode other. We switch back and forth between these. -@gdef@rawbackslash{@let\=@backslashcurfont} -@gdef@otherbackslash{@let\=@realbackslash} - -% Same as @turnoffactive except outputs \ as {\tt\char`\\} instead of -% the literal character `\'. -% -@def@normalturnoffactive{% - @let"=@normaldoublequote - @let$=@normaldollar %$ font-lock fix - @let+=@normalplus - @let<=@normalless - @let>=@normalgreater - @let\=@normalbackslash - @let^=@normalcaret - @let_=@normalunderscore - @let|=@normalverticalbar - @let~=@normaltilde - @markupsetuplqdefault - @markupsetuprqdefault - @unsepspaces -} - -% Make _ and + \other characters, temporarily. -% This is canceled by @fixbackslash. -@otherifyactive - -% If a .fmt file is being used, we don't want the `\input texinfo' to show up. -% That is what \eatinput is for; after that, the `\' should revert to printing -% a backslash. -% -@gdef@eatinput input texinfo{@fixbackslash} -@global@let\ = @eatinput - -% On the other hand, perhaps the file did not have a `\input texinfo'. Then -% the first `\' in the file would cause an error. This macro tries to fix -% that, assuming it is called before the first `\' could plausibly occur. -% Also turn back on active characters that might appear in the input -% file name, in case not using a pre-dumped format. -% -@gdef@fixbackslash{% - @ifx\@eatinput @let\ = @normalbackslash @fi - @catcode`+=@active - @catcode`@_=@active -} - -% Say @foo, not \foo, in error messages. -@escapechar = `@@ - -% These (along with & and #) are made active for url-breaking, so need -% active definitions as the normal characters. -@def@normaldot{.} -@def@normalquest{?} -@def@normalslash{/} - -% These look ok in all fonts, so just make them not special. -% @hashchar{} gets its own user-level command, because of #line. -@catcode`@& = @other @def@normalamp{&} -@catcode`@# = @other @def@normalhash{#} -@catcode`@% = @other @def@normalpercent{%} - -@let @hashchar = @normalhash - -@c Finally, make ` and ' active, so that txicodequoteundirected and -@c txicodequotebacktick work right in, e.g., @w{@code{`foo'}}. If we -@c don't make ` and ' active, @code will not get them as active chars. -@c Do this last of all since we use ` in the previous @catcode assignments. -@catcode`@'=@active -@catcode`@`=@active -@markupsetuplqdefault -@markupsetuprqdefault - -@c Local variables: -@c eval: (add-hook 'write-file-hooks 'time-stamp) -@c page-delimiter: "^\\\\message" -@c time-stamp-start: "def\\\\texinfoversion{" -@c time-stamp-format: "%:y-%02m-%02d.%02H" -@c time-stamp-end: "}" -@c End: - -@c vim:sw=2: - -@ignore - arch-tag: e1b36e32-c96e-4135-a41a-0b2efa2ea115 -@end ignore diff --git a/doc/version.texi b/doc/version.texi deleted file mode 100644 index a8ff42c6..00000000 --- a/doc/version.texi +++ /dev/null @@ -1,4 +0,0 @@ -@set UPDATED 8 March 2014 -@set UPDATED-MONTH March 2014 -@set EDITION 0.7.1 -@set VERSION 0.7.1 From 23cab1301d93693cb6c1981c87dd1cfb99aa712a Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sat, 15 Mar 2014 16:52:04 +0000 Subject: [PATCH 056/623] Added license to doc files While the documentation is licensed using GFDL, the man source is licensed with verbatim license in a way to avoid problems on Debian: http://www.debian.org/vote/2006/vote_001 --- doc/Makefile.am | 18 ++++++++++++++++++ doc/libhttpserver.3 | 24 ++++++++++++++++++++++++ doc/libhttpserver.md | 10 ++++++++++ 3 files changed, 52 insertions(+) diff --git a/doc/Makefile.am b/doc/Makefile.am index 50df01de..c490c1c1 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -1,2 +1,20 @@ +# +# This file is part of libhttpserver +# Copyright (C) 2011, 2012, 2013, 2014 Sebastiano Merlino +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + man_MANS = libhttpserver.3 EXTRA_DIST = $(man_MANS) diff --git a/doc/libhttpserver.3 b/doc/libhttpserver.3 index 1133cd51..edcea3a9 100644 --- a/doc/libhttpserver.3 +++ b/doc/libhttpserver.3 @@ -1,3 +1,27 @@ +.\" Copyright (c) 2014, Sebastiano Merlino +.\" +.\" %%%LICENSE_START(VERBATIM) +.\" Permission is granted to make and distribute verbatim copies of this +.\" manual provided the copyright notice and this permission notice are +.\" preserved on all copies. +.\" +.\" Permission is granted to copy and distribute modified versions of +.\" this manual under the conditions for verbatim copying, provided that +.\" the entire resulting derived work is distributed under the terms of +.\" a permission notice identical to this one. +.\" +.\" Since the Linux kernel and libraries are constantly changing, this +.\" manual page may be incorrect or out-of-date. The author(s) assume. +.\" no responsibility for errors or omissions, or for damages resulting. +.\" from the use of the information contained herein. The author(s) may. +.\" not have taken the same level of care in the production of this. +.\" manual, which is licensed free of charge, as they might when working. +.\" professionally. +.\" +.\" Formatted or processed versions of this manual, if unaccompanied by +.\" the source, must acknowledge the copyright and authors of this work. +.\" %%%LICENSE_END + .TH LIBHTTPSERVER "3" "02 Mar 2013 "libhttpserver" .SH "NAME" libhttpserver \- C++ library for creating an embedded Rest HTTP server (and more) diff --git a/doc/libhttpserver.md b/doc/libhttpserver.md index e40c19d8..1bb022b7 100644 --- a/doc/libhttpserver.md +++ b/doc/libhttpserver.md @@ -1,3 +1,13 @@ + + The libhttpserver (0.7.1) reference manual ========================================== From 4755344ee5f04024c7c5d6cb239741c081021d24 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sat, 15 Mar 2014 17:00:36 +0000 Subject: [PATCH 057/623] Added LICENSE file It is essentially a mirror of COPYING.LESSER file. I have chosen to insert it since it seems to be a much more popular/understandable choice on github --- LICENSE | 504 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 504 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..5ab7695a --- /dev/null +++ b/LICENSE @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +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 and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + From 73930a0070eb2a0d68cc2bfc561ce97ffdae3666 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sat, 15 Mar 2014 17:06:02 +0000 Subject: [PATCH 058/623] Updated readme adding link to the doc --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 2de44477..6abea4ff 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ ================================= libhttpserver =================================== +[![Build Status](https://travis-ci.org/etr/libhttpserver.png?branch=master)](https://travis-ci.org/etr/libhttpserver) When you see this tree, know that you've came across ZenCoders.org @@ -48,4 +49,5 @@ visit our website www.zencoders.org Author: Sebastiano Merlino -[![Build Status](https://travis-ci.org/etr/libhttpserver.png?branch=master)](https://travis-ci.org/etr/libhttpserver) +A more comprehensive documentation about the library can be found in the doc subdir or +following the link https://github.com/etr/libhttpserver/blob/master/doc/libhttpserver.md From b6fd4bf4408bfef6a256fb80e8bfe15d5270252c Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 23 Mar 2014 01:40:39 +0000 Subject: [PATCH 059/623] Moved comet management in a separate class This helps in reducing the responsibilities of the webserver class. The webserver class moves to uncopyable - it turns out to be so complex to be dangerous as a copyable class and btw it is almost useless to have facilities to copy the entire webserver. --- examples/Test.cpp | 2 +- src/Makefile.am | 4 +- src/comet_manager.cpp | 297 +++++++++++++++++++++++ src/httpserver/details/comet_manager.hpp | 97 ++++++++ src/httpserver/webserver.hpp | 17 +- src/webserver.cpp | 223 ++--------------- 6 files changed, 418 insertions(+), 222 deletions(-) create mode 100644 src/comet_manager.cpp create mode 100644 src/httpserver/details/comet_manager.hpp diff --git a/examples/Test.cpp b/examples/Test.cpp index cb78688a..a260fb04 100644 --- a/examples/Test.cpp +++ b/examples/Test.cpp @@ -116,7 +116,7 @@ void Test::render_PUT(const http_request& r, http_response** res) int main() { // signal(SIGINT, &signal_callback_handler); - webserver ws = create_webserver(8080)/*.max_threads(5)*/; + webserver ws(create_webserver(8080)/*.max_threads(5)*/); ws_ptr = &ws; Test dt = Test(); Test2 dt2 = Test2(); diff --git a/src/Makefile.am b/src/Makefile.am index 26d123d6..dd151f75 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -19,8 +19,8 @@ AM_CPPFLAGS = -I../ -I$(srcdir)/httpserver/ METASOURCES = AUTO lib_LTLIBRARIES = libhttpserver.la -libhttpserver_la_SOURCES = string_utilities.cpp webserver.cpp http_utils.cpp http_endpoint.cpp http_request.cpp http_response.cpp http_resource.cpp -noinst_HEADERS = httpserver/string_utilities.hpp httpserver/details/modded_request.hpp httpserver/details/http_response_ptr.hpp httpserver/details/cache_entry.hpp gettext.h +libhttpserver_la_SOURCES = string_utilities.cpp webserver.cpp http_utils.cpp http_endpoint.cpp http_request.cpp http_response.cpp http_resource.cpp comet_manager.cpp +noinst_HEADERS = httpserver/string_utilities.hpp httpserver/details/modded_request.hpp httpserver/details/http_response_ptr.hpp httpserver/details/cache_entry.hpp httpserver/details/comet_manager.hpp gettext.h nobase_include_HEADERS = httpserver.hpp httpserver/create_webserver.hpp httpserver/webserver.hpp httpserver/http_utils.hpp httpserver/details/http_endpoint.hpp httpserver/http_request.hpp httpserver/http_response.hpp httpserver/http_resource.hpp httpserver/binders.hpp httpserver/event_supplier.hpp httpserver/details/event_tuple.hpp httpserver/details/http_resource_mirror.hpp AM_CXXFLAGS += -fPIC -Wall libhttpserver_la_LIBADD = -lmicrohttpd diff --git a/src/comet_manager.cpp b/src/comet_manager.cpp new file mode 100644 index 00000000..c3b42640 --- /dev/null +++ b/src/comet_manager.cpp @@ -0,0 +1,297 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011, 2012, 2013, 2014 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#include +#include +#include "details/comet_manager.hpp" + +using namespace std; + +namespace httpserver +{ + +namespace details +{ + +comet_manager::comet_manager() +{ + pthread_rwlock_init(&comet_guard, NULL); + pthread_mutex_init(&cleanmux, NULL); + pthread_cond_init(&cleancond, NULL); +} + +comet_manager::~comet_manager() +{ + pthread_rwlock_destroy(&comet_guard); + pthread_mutex_destroy(&cleanmux); + pthread_cond_destroy(&cleancond); +} + +void comet_manager::send_message_to_topic ( + const string& topic, + const string& message, + const httpserver::http::http_utils::start_method_T& start_method +) +{ + pthread_rwlock_wrlock(&comet_guard); + for(set::const_iterator it = q_waitings[topic].begin(); + it != q_waitings[topic].end(); + ++it + ) + { + q_messages[(*it)].push_back(message); + q_signal.insert((*it)); + if(start_method != http::http_utils::INTERNAL_SELECT) + { + pthread_mutex_lock(&q_blocks[(*it)].first); + pthread_cond_signal(&q_blocks[(*it)].second); + pthread_mutex_unlock(&q_blocks[(*it)].first); + } + map::const_iterator itt; + if((itt = q_keepalives.find(*it)) != q_keepalives.end()) + { + struct timeval curtime; + gettimeofday(&curtime, NULL); + q_keepalives[*it] = curtime.tv_sec; + } + } + pthread_rwlock_unlock(&comet_guard); + if(start_method != http::http_utils::INTERNAL_SELECT) + { + pthread_mutex_lock(&cleanmux); + pthread_cond_signal(&cleancond); + pthread_mutex_unlock(&cleanmux); + } +} + +void comet_manager::register_to_topics ( + const vector& topics, + const http::httpserver_ska& connection_id, + int keepalive_secs, + string keepalive_msg, + const httpserver::http::http_utils::start_method_T& start_method +) +{ + pthread_rwlock_wrlock(&comet_guard); + for(vector::const_iterator it = topics.begin(); + it != topics.end(); ++it + ) + q_waitings[*it].insert(connection_id); + if(keepalive_secs != -1) + { + struct timeval curtime; + gettimeofday(&curtime, NULL); + q_keepalives[connection_id] = curtime.tv_sec; + q_keepalives_mem[connection_id] = make_pair( + keepalive_secs, keepalive_msg + ); + } + if(start_method != http::http_utils::INTERNAL_SELECT) + { + pthread_mutex_t m; + pthread_cond_t c; + pthread_mutex_init(&m, NULL); + pthread_cond_init(&c, NULL); + q_blocks[connection_id] = + make_pair(m, c); + } + pthread_rwlock_unlock(&comet_guard); +} + +size_t comet_manager::read_message(const http::httpserver_ska& connection_id, + string& message +) +{ + pthread_rwlock_wrlock(&comet_guard); + deque& t_deq = q_messages[connection_id]; + message.assign(t_deq.front()); + t_deq.pop_front(); + pthread_rwlock_unlock(&comet_guard); + return message.size(); +} + +size_t comet_manager::get_topic_consumers( + const string& topic, + set& consumers +) +{ + pthread_rwlock_rdlock(&comet_guard); + + for(set::const_iterator it = q_waitings[topic].begin(); + it != q_waitings[topic].end(); ++it + ) + { + consumers.insert((*it)); + } + int size = consumers.size(); + pthread_rwlock_unlock(&comet_guard); + return size; +} + +bool comet_manager::pop_signaled(const http::httpserver_ska& consumer, + const httpserver::http::http_utils::start_method_T& start_method +) +{ + if(start_method == http::http_utils::INTERNAL_SELECT) + { + pthread_rwlock_wrlock(&comet_guard); + set::iterator it = q_signal.find(consumer); + if(it != q_signal.end()) + { + if(q_messages[consumer].empty()) + { + q_signal.erase(it); + pthread_rwlock_unlock(&comet_guard); + return false; + } + pthread_rwlock_unlock(&comet_guard); + return true; + } + else + { + pthread_rwlock_unlock(&comet_guard); + return false; + } + } + else + { + pthread_rwlock_rdlock(&comet_guard); + pthread_mutex_lock(&q_blocks[consumer].first); + struct timespec t; + struct timeval curtime; + + { + bool to_unlock = true; + while(q_signal.find(consumer) == q_signal.end()) + { + if(to_unlock) + { + pthread_rwlock_unlock(&comet_guard); + to_unlock = false; + } + gettimeofday(&curtime, NULL); + t.tv_sec = curtime.tv_sec + q_keepalives_mem[consumer].first; + t.tv_nsec = 0; + int rslt = pthread_cond_timedwait(&q_blocks[consumer].second, + &q_blocks[consumer].first, &t + ); + if(rslt == ETIMEDOUT) + { + pthread_rwlock_wrlock(&comet_guard); + send_message_to_consumer(consumer, + q_keepalives_mem[consumer].second, false, start_method + ); + pthread_rwlock_unlock(&comet_guard); + } + } + if(to_unlock) + pthread_rwlock_unlock(&comet_guard); + } + + if(q_messages[consumer].size() == 0) + { + pthread_rwlock_wrlock(&comet_guard); + q_signal.erase(consumer); + pthread_mutex_unlock(&q_blocks[consumer].first); + pthread_rwlock_unlock(&comet_guard); + return false; + } + pthread_rwlock_rdlock(&comet_guard); + pthread_mutex_unlock(&q_blocks[consumer].first); + pthread_rwlock_unlock(&comet_guard); + return true; + } + return false; +} + +void comet_manager::complete_request(const http::httpserver_ska& connection_id) +{ + pthread_rwlock_wrlock(&comet_guard); + q_messages.erase(connection_id); + q_blocks.erase(connection_id); + q_signal.erase(connection_id); + q_keepalives.erase(connection_id); + + typedef map >::iterator conn_it; + for(conn_it it = q_waitings.begin(); it != q_waitings.end(); ++it) + { + it->second.erase(connection_id); + } + pthread_rwlock_unlock(&comet_guard); +} + +void comet_manager::comet_select(unsigned long long* timeout_secs, + unsigned long long* timeout_microsecs, + const httpserver::http::http_utils::start_method_T& start_method +) +{ + pthread_rwlock_wrlock(&comet_guard); + for(map::iterator it = q_keepalives.begin(); it != q_keepalives.end(); ++it) + { + struct timeval curtime; + gettimeofday(&curtime, NULL); + int waited_time = curtime.tv_sec - (*it).second; + if(waited_time >= q_keepalives_mem[(*it).first].first) + { + send_message_to_consumer((*it).first, q_keepalives_mem[(*it).first].second, true, start_method); + } + else + { + unsigned long long to_wait_time = (q_keepalives_mem[(*it).first].first - waited_time); + if(to_wait_time < *timeout_secs) + { + *timeout_secs = to_wait_time; + *timeout_microsecs = 0; + } + } + } + pthread_rwlock_unlock(&comet_guard); +} + +void comet_manager::send_message_to_consumer( + const http::httpserver_ska& connection_id, + const std::string& message, + bool to_lock, + const httpserver::http::http_utils::start_method_T& start_method +) +{ + //This function need to be externally locked on write + q_messages[connection_id].push_back(message); + map::const_iterator it; + if((it = q_keepalives.find(connection_id)) != q_keepalives.end()) + { + struct timeval curtime; + gettimeofday(&curtime, NULL); + q_keepalives[connection_id] = curtime.tv_sec; + } + q_signal.insert(connection_id); + if(start_method != http::http_utils::INTERNAL_SELECT) + { + if(to_lock) + pthread_mutex_lock(&q_blocks[connection_id].first); + pthread_cond_signal(&q_blocks[connection_id].second); + if(to_lock) + pthread_mutex_unlock(&q_blocks[connection_id].first); + } +} + +} //details + +} //httpserver diff --git a/src/httpserver/details/comet_manager.hpp b/src/httpserver/details/comet_manager.hpp new file mode 100644 index 00000000..54d3ac94 --- /dev/null +++ b/src/httpserver/details/comet_manager.hpp @@ -0,0 +1,97 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011, 2012, 2013, 2014 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#include +#include +#include +#include +#include +#include +#include "http_utils.hpp" + +namespace httpserver +{ + +namespace http +{ +struct httpserver_ska; +}; + +namespace details +{ + +class comet_manager +{ + public: + comet_manager(); + + ~comet_manager(); + + void send_message_to_topic(const std::string& topic, + const std::string& message, const httpserver::http::http_utils::start_method_T& start_method + ); + + void send_message_to_consumer(const http::httpserver_ska& connection_id, + const std::string& message, bool to_lock, + const httpserver::http::http_utils::start_method_T& start_method + ); + + void register_to_topics(const std::vector& topics, + const http::httpserver_ska& connection_id, int keepalive_secs, + std::string keepalive_msg, const httpserver::http::http_utils::start_method_T& start_method + ); + + size_t read_message(const http::httpserver_ska& connection_id, + std::string& message + ); + + size_t get_topic_consumers(const std::string& topic, + std::set& consumers + ); + + bool pop_signaled(const http::httpserver_ska& consumer, const httpserver::http::http_utils::start_method_T& start_method); + + void complete_request(const http::httpserver_ska& connection_id); + + void comet_select(unsigned long long* timeout_secs, + unsigned long long* timeout_microsecs, + const httpserver::http::http_utils::start_method_T& start_method + ); + + protected: + comet_manager(const comet_manager&) + { + } + + private: + std::map > q_messages; + std::map > q_waitings; + std::map > q_blocks; + std::set q_signal; + std::map q_keepalives; + std::map > q_keepalives_mem; + pthread_rwlock_t comet_guard; + pthread_mutex_t cleanmux; + pthread_cond_t cleancond; +}; + +} //details + +} //httpserver diff --git a/src/httpserver/webserver.hpp b/src/httpserver/webserver.hpp index b67e4ec3..305cf0d9 100644 --- a/src/httpserver/webserver.hpp +++ b/src/httpserver/webserver.hpp @@ -62,6 +62,7 @@ namespace details { struct daemon_item; struct modded_request; struct cache_entry; + class comet_manager; } /** @@ -182,6 +183,10 @@ class webserver * Method used to kill the webserver waiting for it to terminate **/ void sweet_kill(); + + protected: + webserver& operator=(const webserver& other); + private: const int port; http::http_utils::start_method_T start_method; @@ -218,9 +223,7 @@ class webserver bool single_resource; pthread_mutex_t mutexwait; pthread_rwlock_t runguard; - pthread_mutex_t cleanmux; pthread_cond_t mutexcond; - pthread_cond_t cleancond; render_ptr not_found_resource; render_ptr method_not_allowed_resource; render_ptr method_not_acceptable_resource; @@ -234,20 +237,12 @@ class webserver std::set bans; std::set allowances; - std::map > q_messages; - std::map > q_waitings; - std::map > q_blocks; - std::set q_signal; - std::map q_keepalives; - std::map > q_keepalives_mem; - pthread_rwlock_t comet_guard; - std::vector daemons; std::vector threads; std::map event_suppliers; - webserver& operator=(const webserver& b); + details::comet_manager* internal_comet_manager; static void* select(void* self); static void* cleaner(void* self); diff --git a/src/webserver.cpp b/src/webserver.cpp index 32dd9b4c..e8372506 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -44,6 +44,7 @@ #include "details/http_resource_mirror.hpp" #include "details/event_tuple.hpp" #include "create_webserver.hpp" +#include "details/comet_manager.hpp" #include "webserver.hpp" #include "details/modded_request.hpp" #include "details/cache_entry.hpp" @@ -174,7 +175,8 @@ webserver::webserver(const create_webserver& params): method_not_allowed_resource(params._method_not_allowed_resource), method_not_acceptable_resource(params._method_not_acceptable_resource), internal_error_resource(params._internal_error_resource), - next_to_choose(0) + next_to_choose(0), + internal_comet_manager(new details::comet_manager()) { if(single_resource != 0x0) this->single_resource = true; @@ -185,9 +187,6 @@ webserver::webserver(const create_webserver& params): pthread_rwlock_init(&runguard, NULL); pthread_cond_init(&mutexcond, NULL); pthread_rwlock_init(&cache_guard, NULL); - pthread_rwlock_init(&comet_guard, NULL); - pthread_mutex_init(&cleanmux, NULL); - pthread_cond_init(&cleancond, NULL); } webserver::~webserver() @@ -197,9 +196,7 @@ webserver::~webserver() pthread_rwlock_destroy(&runguard); pthread_rwlock_destroy(&cache_guard); pthread_cond_destroy(&mutexcond); - pthread_rwlock_destroy(&comet_guard); - pthread_mutex_destroy(&cleanmux); - pthread_cond_destroy(&cleancond); + delete internal_comet_manager; } void webserver::sweet_kill() @@ -214,21 +211,8 @@ void webserver::request_completed ( enum MHD_RequestTerminationCode toe ) { - details::modded_request* mr = (struct details::modded_request*) *con_cls; - - pthread_rwlock_wrlock(&mr->ws->comet_guard); - mr->ws->q_messages.erase(mr->dhrs->connection_id); - mr->ws->q_blocks.erase(mr->dhrs->connection_id); - mr->ws->q_signal.erase(mr->dhrs->connection_id); - mr->ws->q_keepalives.erase(mr->dhrs->connection_id); - - typedef std::map >::iterator conn_it; - for(conn_it it = mr->ws->q_waitings.begin(); it != mr->ws->q_waitings.end(); ++it) - { - it->second.erase(mr->dhrs->connection_id); - } - pthread_rwlock_unlock(&mr->ws->comet_guard); - + details::modded_request* mr = static_cast(*con_cls); + mr->ws->internal_comet_manager->complete_request(mr->dhrs->connection_id); if (0x0 != mr) { if(mr->dhrs.res != 0x0 && mr->dhrs->ca != 0x0) @@ -280,8 +264,8 @@ void* webserver::select(void* self) if (MHD_YES != MHD_get_fdset (di->daemon, &rs, &ws, &es, &max)) abort(); /* fatal internal error */ - unsigned MHD_LONG_LONG timeout_microsecs = 0; - unsigned MHD_LONG_LONG timeout_secs = 0; + unsigned long long timeout_microsecs = 0; + unsigned long long timeout_secs = 0; if (!(MHD_get_timeout (di->daemon, &timeout_microsecs) == MHD_YES)) { @@ -327,33 +311,7 @@ void* webserver::select(void* self) } // COMET CONNECTIONS MANAGEMENT - pthread_rwlock_wrlock(&di->ws->comet_guard); - for(std::map::iterator it = di->ws->q_keepalives.begin(); - it != di->ws->q_keepalives.end(); - ++it - ) - { - struct timeval curtime; - gettimeofday(&curtime, NULL); - int waited_time = curtime.tv_sec - (*it).second; - if(waited_time >= di->ws->q_keepalives_mem[(*it).first].first) - di->ws->send_message_to_consumer( - (*it).first, - di->ws->q_keepalives_mem[(*it).first].second - ); - else - { - unsigned MHD_LONG_LONG to_wait_time = - di->ws->q_keepalives_mem[(*it).first].first - waited_time; - - if(to_wait_time < timeout_secs) - { - timeout_secs = to_wait_time; - timeout_microsecs = 0; - } - } - } - pthread_rwlock_unlock(&di->ws->comet_guard); + di->ws->internal_comet_manager->comet_select(&timeout_secs, &timeout_microsecs, di->ws->start_method); timeout_value.tv_sec = timeout_secs; timeout_value.tv_usec = timeout_microsecs; @@ -1341,24 +1299,7 @@ void webserver::send_message_to_consumer( bool to_lock ) { - //This function need to be externally locked on write - q_messages[connection_id].push_back(message); - map::const_iterator it; - if((it = q_keepalives.find(connection_id)) != q_keepalives.end()) - { - struct timeval curtime; - gettimeofday(&curtime, NULL); - q_keepalives[connection_id] = curtime.tv_sec; - } - q_signal.insert(connection_id); - if(start_method != http_utils::INTERNAL_SELECT) - { - if(to_lock) - pthread_mutex_lock(&q_blocks[connection_id].first); - pthread_cond_signal(&q_blocks[connection_id].second); - if(to_lock) - pthread_mutex_unlock(&q_blocks[connection_id].first); - } + internal_comet_manager->send_message_to_consumer(connection_id, message, to_lock, start_method); } void webserver::send_message_to_topic( @@ -1366,35 +1307,7 @@ void webserver::send_message_to_topic( const std::string& message ) { - pthread_rwlock_wrlock(&comet_guard); - for(std::set::const_iterator it = q_waitings[topic].begin(); - it != q_waitings[topic].end(); - ++it - ) - { - q_messages[(*it)].push_back(message); - q_signal.insert((*it)); - if(start_method != http_utils::INTERNAL_SELECT) - { - pthread_mutex_lock(&q_blocks[(*it)].first); - pthread_cond_signal(&q_blocks[(*it)].second); - pthread_mutex_unlock(&q_blocks[(*it)].first); - } - map::const_iterator itt; - if((itt = q_keepalives.find(*it)) != q_keepalives.end()) - { - struct timeval curtime; - gettimeofday(&curtime, NULL); - q_keepalives[*it] = curtime.tv_sec; - } - } - pthread_rwlock_unlock(&comet_guard); - if(start_method != http_utils::INTERNAL_SELECT) - { - pthread_mutex_lock(&cleanmux); - pthread_cond_signal(&cleancond); - pthread_mutex_unlock(&cleanmux); - } + internal_comet_manager->send_message_to_topic(topic, message, start_method); } void webserver::register_to_topics( @@ -1404,42 +1317,14 @@ void webserver::register_to_topics( string keepalive_msg ) { - pthread_rwlock_wrlock(&comet_guard); - for(std::vector::const_iterator it = topics.begin(); - it != topics.end(); ++it - ) - q_waitings[*it].insert(connection_id); - if(keepalive_secs != -1) - { - struct timeval curtime; - gettimeofday(&curtime, NULL); - q_keepalives[connection_id] = curtime.tv_sec; - q_keepalives_mem[connection_id] = make_pair( - keepalive_secs, keepalive_msg - ); - } - if(start_method != http_utils::INTERNAL_SELECT) - { - pthread_mutex_t m; - pthread_cond_t c; - pthread_mutex_init(&m, NULL); - pthread_cond_init(&c, NULL); - q_blocks[connection_id] = - std::make_pair(m, c); - } - pthread_rwlock_unlock(&comet_guard); + internal_comet_manager->register_to_topics(topics, connection_id, keepalive_secs, keepalive_msg, start_method); } size_t webserver::read_message(const httpserver_ska& connection_id, std::string& message ) { - pthread_rwlock_wrlock(&comet_guard); - std::deque& t_deq = q_messages[connection_id]; - message.assign(t_deq.front()); - t_deq.pop_front(); - pthread_rwlock_unlock(&comet_guard); - return message.size(); + return internal_comet_manager->read_message(connection_id, message); } size_t webserver::get_topic_consumers( @@ -1447,90 +1332,12 @@ size_t webserver::get_topic_consumers( std::set& consumers ) { - pthread_rwlock_rdlock(&comet_guard); - - for(std::set::const_iterator it = q_waitings[topic].begin(); - it != q_waitings[topic].end(); ++it - ) - { - consumers.insert((*it)); - } - int size = consumers.size(); - pthread_rwlock_unlock(&comet_guard); - return size; + return internal_comet_manager->get_topic_consumers(topic, consumers); } bool webserver::pop_signaled(const httpserver_ska& consumer) { - if(start_method == http_utils::INTERNAL_SELECT) - { - pthread_rwlock_wrlock(&comet_guard); - std::set::iterator it = q_signal.find(consumer); - if(it != q_signal.end()) - { - if(q_messages[consumer].empty()) - { - q_signal.erase(it); - pthread_rwlock_unlock(&comet_guard); - return false; - } - pthread_rwlock_unlock(&comet_guard); - return true; - } - else - { - pthread_rwlock_unlock(&comet_guard); - return false; - } - } - else - { - pthread_rwlock_rdlock(&comet_guard); - pthread_mutex_lock(&q_blocks[consumer].first); - struct timespec t; - struct timeval curtime; - - { - bool to_unlock = true; - while(q_signal.find(consumer) == q_signal.end()) - { - if(to_unlock) - { - pthread_rwlock_unlock(&comet_guard); - to_unlock = false; - } - gettimeofday(&curtime, NULL); - t.tv_sec = curtime.tv_sec + q_keepalives_mem[consumer].first; - t.tv_nsec = 0; - int rslt = pthread_cond_timedwait(&q_blocks[consumer].second, - &q_blocks[consumer].first, &t - ); - if(rslt == ETIMEDOUT) - { - pthread_rwlock_wrlock(&comet_guard); - send_message_to_consumer(consumer, - q_keepalives_mem[consumer].second, false - ); - pthread_rwlock_unlock(&comet_guard); - } - } - if(to_unlock) - pthread_rwlock_unlock(&comet_guard); - } - - if(q_messages[consumer].size() == 0) - { - pthread_rwlock_wrlock(&comet_guard); - q_signal.erase(consumer); - pthread_mutex_unlock(&q_blocks[consumer].first); - pthread_rwlock_unlock(&comet_guard); - return false; - } - pthread_rwlock_rdlock(&comet_guard); - pthread_mutex_unlock(&q_blocks[consumer].first); - pthread_rwlock_unlock(&comet_guard); - return true; - } + return internal_comet_manager->pop_signaled(consumer, start_method); } http_response* webserver::get_from_cache( From 85400faebc688131fc5047a65784cfebac6e81f8 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 23 Mar 2014 01:58:21 +0000 Subject: [PATCH 060/623] Fixed headers lacking compile protect --- src/httpserver/details/cache_entry.hpp | 4 ++++ src/httpserver/details/comet_manager.hpp | 9 +++++++++ src/httpserver/details/http_response_ptr.hpp | 4 ++++ src/httpserver/details/modded_request.hpp | 4 ++++ 4 files changed, 21 insertions(+) diff --git a/src/httpserver/details/cache_entry.hpp b/src/httpserver/details/cache_entry.hpp index 414293c8..1e1f2442 100644 --- a/src/httpserver/details/cache_entry.hpp +++ b/src/httpserver/details/cache_entry.hpp @@ -18,6 +18,10 @@ USA */ +#if !defined (_HTTPSERVER_HPP_INSIDE_) && !defined (HTTPSERVER_COMPILATION) +#error "Only or can be included directly." +#endif + #ifndef _CACHE_ENTRY_HPP_ #define _CACHE_ENTRY_HPP_ diff --git a/src/httpserver/details/comet_manager.hpp b/src/httpserver/details/comet_manager.hpp index 54d3ac94..71ee5912 100644 --- a/src/httpserver/details/comet_manager.hpp +++ b/src/httpserver/details/comet_manager.hpp @@ -18,6 +18,13 @@ USA */ +#if !defined (_HTTPSERVER_HPP_INSIDE_) && !defined (HTTPSERVER_COMPILATION) +#error "Only or can be included directly." +#endif + +#ifndef _COMET_MANAGER_HPP_ +#define _COMET_MANAGER_HPP_ + #include #include #include @@ -95,3 +102,5 @@ class comet_manager } //details } //httpserver + +#endif //_COMET_MANAGER_HPP_ diff --git a/src/httpserver/details/http_response_ptr.hpp b/src/httpserver/details/http_response_ptr.hpp index 368aaa50..642bc362 100644 --- a/src/httpserver/details/http_response_ptr.hpp +++ b/src/httpserver/details/http_response_ptr.hpp @@ -18,6 +18,10 @@ USA */ +#if !defined (_HTTPSERVER_HPP_INSIDE_) && !defined (HTTPSERVER_COMPILATION) +#error "Only or can be included directly." +#endif + #ifndef _HTTP_RESPONSE_PTR_HPP_ #define _HTTP_RESPONSE_PTR_HPP_ diff --git a/src/httpserver/details/modded_request.hpp b/src/httpserver/details/modded_request.hpp index 998fe386..78ed2151 100644 --- a/src/httpserver/details/modded_request.hpp +++ b/src/httpserver/details/modded_request.hpp @@ -18,6 +18,10 @@ USA */ +#if !defined (_HTTPSERVER_HPP_INSIDE_) && !defined (HTTPSERVER_COMPILATION) +#error "Only or can be included directly." +#endif + #ifndef _MODDED_REQUEST_HPP_ #define _MODDED_REQUEST_HPP_ From ff7883999f653c0f753575ffd701bfd5a66ad95c Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 23 Mar 2014 02:54:20 +0000 Subject: [PATCH 061/623] Avoid to calculate the size of querystring for each request Check non-emptiness using the first string char instead of calculating the size --- src/webserver.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/webserver.cpp b/src/webserver.cpp index e8372506..e68d1ef7 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -779,7 +779,7 @@ size_t unescaper_func(void * cls, struct MHD_Connection *c, char *s) size_t internal_unescaper(void* cls, char* s) { - if(strlen(s) == 0) return 0; + if(s[0] == 0) return 0; webserver* dws = static_cast(cls); if(dws->unescaper != 0x0) From 29e330a32d81c162aa4aa71c8494fad487124a81 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 23 Mar 2014 15:11:04 +0000 Subject: [PATCH 062/623] Added new sections to the documentation Created a section describing the public structures. Added subsection to the compilation instruction regarding optional compilation arguments. --- doc/libhttpserver.md | 49 ++++++++++++++++++++++++++++++++++++++------ 1 file changed, 43 insertions(+), 6 deletions(-) diff --git a/doc/libhttpserver.md b/doc/libhttpserver.md index 1bb022b7..ac2aadb7 100644 --- a/doc/libhttpserver.md +++ b/doc/libhttpserver.md @@ -76,16 +76,53 @@ Additionally, clients can specify resource limits on the overall number of connections, number of connections per IP address and memory used per connection to avoid resource exhaustion. +Requirements +============ +g++ >= 4.1.2 +libmicrohttpd >= 0.9.7 +doxygen (if you want to build code reference) + Compilation =========== libhttpserver uses the standard system where the usual build process involves running -> ./bootstrap -> mkdir build -> cd build -> ../configure -> make -> make install +> ./bootstrap +> mkdir build +> cd build +> ../configure +> make +> make install + +Optional parameters to configure script +--------------------------------------- +A complete list of parameters can be obtained running 'configure --help'. +Here are listed the libhttpserver specific options (the canonical configure options are also supported). + +* --enable-same-directory-build: enable to compile in the same directory. This is heavily discouraged. (def=no) +* --enable-debug: enable debug data generation (def=no) +* --enable-cpp11: enable c++11 std classes (def=no) +* --disable-doxygen-doc: don't generate any doxygen documentation +* --disable-doxygen-dot: don't generate graphics for doxygen documentation +* --disable-doxygen-man: don't generate doxygen manual pages +* --enable-doxygen-rtf: generate doxygen RTF documentation +* --enable-doxygen-xml: generate doxygen XML documentation +* --enable-doxygen-chm: generate doxygen compressed HTML help documentation +* --enable-doxygen-chi: generate doxygen seperate compressed HTML help index file +* --disable-doxygen-html: don't generate doxygen plain HTML documentation +* --enable-doxygen-ps: generate doxygen PostScript documentation +* --enable-doxygen-pdf: generate doxygen PDF documentation + +Constants +========= +W.I.P. + +Structures and classes type definition +====================================== +* http_resource (CPP class): Represents the resource associated with a specific http endpoint. +* http_request (CPP class): Represents the request received by the resource that process it. +* http_response (CPP class): Represents the response sent by the server once the resource finished its work. +* event_supplier (CPP class): Represents a class that supplies events to the webserver. It can be used to trigger the internal select of it. +* webserver (CPP class): Represents the daemon listening on a socket for HTTP traffic. GNU Lesser General Public License ================================= From 0f6beafd8b90690f4c7499ae46160310fa0ad379 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 23 Mar 2014 15:13:14 +0000 Subject: [PATCH 063/623] Solved some formatting issues --- doc/libhttpserver.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/doc/libhttpserver.md b/doc/libhttpserver.md index ac2aadb7..fb627804 100644 --- a/doc/libhttpserver.md +++ b/doc/libhttpserver.md @@ -26,6 +26,7 @@ embedded Rest HTTP server (and more). Contents ======== * Introduction. +* Requirements. * Compilation. * Constants. * Structures and classes type definition. @@ -78,9 +79,9 @@ used per connection to avoid resource exhaustion. Requirements ============ -g++ >= 4.1.2 -libmicrohttpd >= 0.9.7 -doxygen (if you want to build code reference) +* g++ >= 4.1.2 +* libmicrohttpd >= 0.9.7 +* doxygen (if you want to build code reference) Compilation =========== From 6f53906b33e10f02341f58903283e19e420d3f82 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 23 Mar 2014 15:14:22 +0000 Subject: [PATCH 064/623] Solved formatting issues to compile steps --- doc/libhttpserver.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/doc/libhttpserver.md b/doc/libhttpserver.md index fb627804..742013e9 100644 --- a/doc/libhttpserver.md +++ b/doc/libhttpserver.md @@ -87,12 +87,12 @@ Compilation =========== libhttpserver uses the standard system where the usual build process involves running -> ./bootstrap -> mkdir build -> cd build -> ../configure -> make -> make install +> ./bootstrap +> mkdir build +> cd build +> ../configure +> make +> make install Optional parameters to configure script --------------------------------------- From 6a81bfec4fa29f140d7dfadef3b67ea063127782 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 23 Mar 2014 15:24:45 +0000 Subject: [PATCH 065/623] Moved to version 0.7.2 --- ChangeLog | 5 +++++ configure.ac | 2 +- debian/changelog.in | 6 ++++++ 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index c7e6e96c..1758fb2d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +Sat Mar 23 15:22:40 2014 +0100 + Continue the cleanup reducing webserver.cpp responsibilities + Deep work on documentation + Moved to version 0.7.2 + Sat Jan 25 16:31:03 2014 +0100 Cleaned-up webserver.cpp code to extract secondary classes Enforced immutability of webserver class diff --git a/configure.ac b/configure.ac index 60c69911..97c04fe3 100644 --- a/configure.ac +++ b/configure.ac @@ -22,7 +22,7 @@ AC_PREREQ(2.57) m4_define([libhttpserver_MAJOR_VERSION],[0])dnl m4_define([libhttpserver_MINOR_VERSION],[7])dnl -m4_define([libhttpserver_REVISION],[1])dnl +m4_define([libhttpserver_REVISION],[2])dnl m4_define([libhttpserver_PKG_VERSION],[libhttpserver_MAJOR_VERSION.libhttpserver_MINOR_VERSION.libhttpserver_REVISION])dnl m4_define([libhttpserver_LDF_VERSION],[libhttpserver_MAJOR_VERSION:libhttpserver_MINOR_VERSION:libhttpserver_REVISION])dnl AC_INIT([libhttpserver], libhttpserver_PKG_VERSION, [electrictwister2000@gmail.com]) diff --git a/debian/changelog.in b/debian/changelog.in index f1eeedbf..f22ad177 100644 --- a/debian/changelog.in +++ b/debian/changelog.in @@ -1,3 +1,9 @@ +libhttpserver (0.7.2) unstable; urgency=low + * Documentation updates + * Reduced responsibilities of webserver class + + -- Sebastiano Merlino Sat, 23 Mar 2014 15:23:40 +0100 + libhttpserver (0.7.1) unstable; urgency=low * Improved methods constness From 50d9f1bb3e54d33306de4e4e6b190d3a3df2570b Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 23 Mar 2014 15:25:18 +0000 Subject: [PATCH 066/623] Merged libhttpserver.md into readme --- README.md | 1078 +++++++++++++++++++++++++++++++++++++++++- doc/libhttpserver.md | 1073 ----------------------------------------- 2 files changed, 1075 insertions(+), 1076 deletions(-) delete mode 100644 doc/libhttpserver.md diff --git a/README.md b/README.md index 6abea4ff..278e70cd 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,20 @@ -================================= libhttpserver =================================== + + +The libhttpserver (0.7.2) reference manual +========================================== + [![Build Status](https://travis-ci.org/etr/libhttpserver.png?branch=master)](https://travis-ci.org/etr/libhttpserver) +This library has been originally developed under the zencoders flags and this community has always supported me all along this work so I am happy to put the logo on this readme. + When you see this tree, know that you've came across ZenCoders.org with open('ZenCoders. @@ -49,5 +63,1063 @@ visit our website www.zencoders.org Author: Sebastiano Merlino -A more comprehensive documentation about the library can be found in the doc subdir or -following the link https://github.com/etr/libhttpserver/blob/master/doc/libhttpserver.md +Copying +======= +This manual is for libhttpserver, C++ library for creating an +embedded Rest HTTP server (and more). + +> Permission is granted to copy, distribute and/or modify this document +> under the terms of the GNU Free Documentation License, Version 1.3 +> or any later version published by the Free Software Foundation; +> with no Invariant Sections, no Front-Cover Texts, and no Back-Cover +> Texts. A copy of the license is included in the section entitled GNU +> Free Documentation License. + +Contents +======== +* Introduction. +* Requirements. +* Compilation. +* Constants. +* Structures and classes type definition. +* Callback functions definition. +* Create and work with server. +* Registering resources. +* Building responses to requests. +* Whitelists and Blacklists. +* Simple comet semantics. +* Utilizing Authentication. +* Obtaining and modifying status information. + +Appendices +---------- +* GNU-LGPL: The GNU Lesser General Public License says how you can copy and share almost all of libhttpserver. +* GNU-FDL: The GNU Free Documentation License says how you can copy and share the documentation of libhttpserver. + +Introduction +============ +libhttpserver is meant to constitute an easy system to build HTTP +servers with REST fashion. +libhttpserver is based on libmicrohttpd and, like this, it is a +daemon library. +The mission of this library is to support all possible HTTP features +directly and with a simple semantic allowing then the user to concentrate +only on his application and not on HTTP request handling details. + +The library is supposed to work transparently for the client Implementing +the business logic and using the library itself to realize an interface. +If the user wants it must be able to change every behavior of the library +itself through the registration of callbacks. + +Like the api is based on (libmicrohttpd), libhttpserver is able to decode +certain body format a and automatically format them in object oriented +fashion. This is true for query arguments and for *POST* and *PUT* +requests bodies if *application/x-www-form-urlencoded* or +*multipart/form-data* header are passed. + +The header reproduce all the constants defined by libhttpserver. +These maps various constant used by the HTTP protocol that are exported +as a convenience for users of the library. Is is possible for the user +to define their own extensions of the HTTP standard and use those with +libhttpserver. + +All functions are guaranteed to be completely reentrant and +thread-safe (unless differently specified). +Additionally, clients can specify resource limits on the overall +number of connections, number of connections per IP address and memory +used per connection to avoid resource exhaustion. + +Requirements +============ +* g++ >= 4.1.2 +* libmicrohttpd >= 0.9.7 +* doxygen (if you want to build code reference) + +Compilation +=========== +libhttpserver uses the standard system where the usual build process +involves running +> ./bootstrap +> mkdir build +> cd build +> ../configure +> make +> make install + +Optional parameters to configure script +--------------------------------------- +A complete list of parameters can be obtained running 'configure --help'. +Here are listed the libhttpserver specific options (the canonical configure options are also supported). + +* --enable-same-directory-build: enable to compile in the same directory. This is heavily discouraged. (def=no) +* --enable-debug: enable debug data generation (def=no) +* --enable-cpp11: enable c++11 std classes (def=no) +* --disable-doxygen-doc: don't generate any doxygen documentation +* --disable-doxygen-dot: don't generate graphics for doxygen documentation +* --disable-doxygen-man: don't generate doxygen manual pages +* --enable-doxygen-rtf: generate doxygen RTF documentation +* --enable-doxygen-xml: generate doxygen XML documentation +* --enable-doxygen-chm: generate doxygen compressed HTML help documentation +* --enable-doxygen-chi: generate doxygen seperate compressed HTML help index file +* --disable-doxygen-html: don't generate doxygen plain HTML documentation +* --enable-doxygen-ps: generate doxygen PostScript documentation +* --enable-doxygen-pdf: generate doxygen PDF documentation + +Constants +========= +W.I.P. + +Structures and classes type definition +====================================== +* http_resource (CPP class): Represents the resource associated with a specific http endpoint. +* http_request (CPP class): Represents the request received by the resource that process it. +* http_response (CPP class): Represents the response sent by the server once the resource finished its work. +* event_supplier (CPP class): Represents a class that supplies events to the webserver. It can be used to trigger the internal select of it. +* webserver (CPP class): Represents the daemon listening on a socket for HTTP traffic. + +GNU Lesser General Public License +================================= + +Version 2.1, February 1999 + +Copyright © 1991, 1999 Free Software Foundation, Inc. +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +Everyone is permitted to copy and distribute verbatim copies +of this license document, but changing it is not allowed. + +_This is the first released version of the Lesser GPL. It also counts +as the successor of the GNU Library Public License, version 2, hence +the version number 2.1._ + +### Preamble + +The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + +This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + +When we speak of free software, we are referring to freedom of use, +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 and use pieces of +it in new free programs; and that you are informed that you can do +these things. + +To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + +For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + +We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + +To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + +Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + +Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + +When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + +We call this license the “Lesser” General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + +For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + +In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + +Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + +The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +“work based on the library” and a “work that uses the library”. The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + +### TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + +**0.** This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called “this License”). +Each licensee is addressed as “you”. + +A “library” means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + +The “Library”, below, refers to any such software library or work +which has been distributed under these terms. A “work based on the +Library” means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term “modification”.) + +“Source code” for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + +**1.** You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + +You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + +**2.** You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + +* **a)** The modified work must itself be a software library. +* **b)** You must cause the files modified to carry prominent notices +stating that you changed the files and the date of any change. +* **c)** You must cause the whole of the work to be licensed at no +charge to all third parties under the terms of this License. +* **d)** If a facility in the modified Library refers to a function or a +table of data to be supplied by an application program that uses +the facility, other than as an argument passed when the facility +is invoked, then you must make a good faith effort to ensure that, +in the event an application does not supply such function or +table, the facility still operates, and performs whatever part of +its purpose remains meaningful. +(For example, a function in a library to compute square roots has +a purpose that is entirely well-defined independent of the +application. Therefore, Subsection 2d requires that any +application-supplied function or table used by this function must +be optional: if the application does not supply it, the square +root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + +**3.** You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + +Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + +This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + +**4.** You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + +If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + +**5.** A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a “work that uses the Library”. Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + +However, linking a “work that uses the Library” with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a “work that uses the +library”. The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + +When a “work that uses the Library” uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + +If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + +Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + +**6.** As an exception to the Sections above, you may also combine or +link a “work that uses the Library” with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + +You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + +* **a)** Accompany the work with the complete corresponding +machine-readable source code for the Library including whatever +changes were used in the work (which must be distributed under +Sections 1 and 2 above); and, if the work is an executable linked +with the Library, with the complete machine-readable “work that +uses the Library”, as object code and/or source code, so that the +user can modify the Library and then relink to produce a modified +executable containing the modified Library. (It is understood +that the user who changes the contents of definitions files in the +Library will not necessarily be able to recompile the application +to use the modified definitions.) +* **b)** Use a suitable shared library mechanism for linking with the +Library. A suitable mechanism is one that (1) uses at run time a +copy of the library already present on the user's computer system, +rather than copying library functions into the executable, and (2) +will operate properly with a modified version of the library, if +the user installs one, as long as the modified version is +interface-compatible with the version that the work was made with. +* **c)** Accompany the work with a written offer, valid for at +least three years, to give the same user the materials +specified in Subsection 6a, above, for a charge no more +than the cost of performing this distribution. +* **d)** If distribution of the work is made by offering access to copy +from a designated place, offer equivalent access to copy the above +specified materials from the same place. +* **e)** Verify that the user has already received a copy of these +materials or that you have already sent this user a copy. + +For an executable, the required form of the “work that uses the +Library” must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + +It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + +**7.** You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + +* **a)** Accompany the combined library with a copy of the same work +based on the Library, uncombined with any other library +facilities. This must be distributed under the terms of the +Sections above. +* **b)** Give prominent notice with the combined library of the fact +that part of it is a work based on the Library, and explaining +where to find the accompanying uncombined form of the same work. + +**8.** You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + +**9.** You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + +**10.** Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + +**11.** If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + +**12.** If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + +**13.** The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +“any later version”, you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + +**14.** If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + +### NO WARRANTY + +**15.** BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY “AS IS” WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + +**16.** IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + +END OF TERMS AND CONDITIONS + +### How to Apply These Terms to Your New Libraries + +If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + +To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +“copyright” line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a “copyright disclaimer” for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + +GNU Free Documentation License +============================== + +Version 1.3, 3 November 2008 + +Copyright © 2000, 2001, 2002, 2007, 2008 Free Software Foundation, Inc. <> + +Everyone is permitted to copy and distribute verbatim copies +of this license document, but changing it is not allowed. + +### 0. PREAMBLE + +The purpose of this License is to make a manual, textbook, or other +functional and useful document “free” in the sense of freedom: to +assure everyone the effective freedom to copy and redistribute it, +with or without modifying it, either commercially or noncommercially. +Secondarily, this License preserves for the author and publisher a way +to get credit for their work, while not being considered responsible +for modifications made by others. + +This License is a kind of “copyleft”, which means that derivative +works of the document must themselves be free in the same sense. It +complements the GNU General Public License, which is a copyleft +license designed for free software. + +We have designed this License in order to use it for manuals for free +software, because free software needs free documentation: a free +program should come with manuals providing the same freedoms that the +software does. But this License is not limited to software manuals; +it can be used for any textual work, regardless of subject matter or +whether it is published as a printed book. We recommend this License +principally for works whose purpose is instruction or reference. + + +### 1. APPLICABILITY AND DEFINITIONS + +This License applies to any manual or other work, in any medium, that +contains a notice placed by the copyright holder saying it can be +distributed under the terms of this License. Such a notice grants a +world-wide, royalty-free license, unlimited in duration, to use that +work under the conditions stated herein. The “Document”, below, +refers to any such manual or work. Any member of the public is a +licensee, and is addressed as “you”. You accept the license if you +copy, modify or distribute the work in a way requiring permission +under copyright law. + +A “Modified Version” of the Document means any work containing the +Document or a portion of it, either copied verbatim, or with +modifications and/or translated into another language. + +A “Secondary Section” is a named appendix or a front-matter section of +the Document that deals exclusively with the relationship of the +publishers or authors of the Document to the Document's overall +subject (or to related matters) and contains nothing that could fall +directly within that overall subject. (Thus, if the Document is in +part a textbook of mathematics, a Secondary Section may not explain +any mathematics.) The relationship could be a matter of historical +connection with the subject or with related matters, or of legal, +commercial, philosophical, ethical or political position regarding +them. + +The “Invariant Sections” are certain Secondary Sections whose titles +are designated, as being those of Invariant Sections, in the notice +that says that the Document is released under this License. If a +section does not fit the above definition of Secondary then it is not +allowed to be designated as Invariant. The Document may contain zero +Invariant Sections. If the Document does not identify any Invariant +Sections then there are none. + +The “Cover Texts” are certain short passages of text that are listed, +as Front-Cover Texts or Back-Cover Texts, in the notice that says that +the Document is released under this License. A Front-Cover Text may +be at most 5 words, and a Back-Cover Text may be at most 25 words. + +A “Transparent” copy of the Document means a machine-readable copy, +represented in a format whose specification is available to the +general public, that is suitable for revising the document +straightforwardly with generic text editors or (for images composed of +pixels) generic paint programs or (for drawings) some widely available +drawing editor, and that is suitable for input to text formatters or +for automatic translation to a variety of formats suitable for input +to text formatters. A copy made in an otherwise Transparent file +format whose markup, or absence of markup, has been arranged to thwart +or discourage subsequent modification by readers is not Transparent. +An image format is not Transparent if used for any substantial amount +of text. A copy that is not “Transparent” is called “Opaque”. + +Examples of suitable formats for Transparent copies include plain +ASCII without markup, Texinfo input format, LaTeX input format, SGML +or XML using a publicly available DTD, and standard-conforming simple +HTML, PostScript or PDF designed for human modification. Examples of +transparent image formats include PNG, XCF and JPG. Opaque formats +include proprietary formats that can be read and edited only by +proprietary word processors, SGML or XML for which the DTD and/or +processing tools are not generally available, and the +machine-generated HTML, PostScript or PDF produced by some word +processors for output purposes only. + +The “Title Page” means, for a printed book, the title page itself, +plus such following pages as are needed to hold, legibly, the material +this License requires to appear in the title page. For works in +formats which do not have any title page as such, “Title Page” means +the text near the most prominent appearance of the work's title, +preceding the beginning of the body of the text. + +The “publisher” means any person or entity that distributes copies of +the Document to the public. + +A section “Entitled XYZ” means a named subunit of the Document whose +title either is precisely XYZ or contains XYZ in parentheses following +text that translates XYZ in another language. (Here XYZ stands for a +specific section name mentioned below, such as “Acknowledgements”, +“Dedications”, “Endorsements”, or “History”.) To “Preserve the Title” +of such a section when you modify the Document means that it remains a +section “Entitled XYZ” according to this definition. + +The Document may include Warranty Disclaimers next to the notice which +states that this License applies to the Document. These Warranty +Disclaimers are considered to be included by reference in this +License, but only as regards disclaiming warranties: any other +implication that these Warranty Disclaimers may have is void and has +no effect on the meaning of this License. + +### 2. VERBATIM COPYING + +You may copy and distribute the Document in any medium, either +commercially or noncommercially, provided that this License, the +copyright notices, and the license notice saying this License applies +to the Document are reproduced in all copies, and that you add no +other conditions whatsoever to those of this License. You may not use +technical measures to obstruct or control the reading or further +copying of the copies you make or distribute. However, you may accept +compensation in exchange for copies. If you distribute a large enough +number of copies you must also follow the conditions in section 3. + +You may also lend copies, under the same conditions stated above, and +you may publicly display copies. + + +### 3. COPYING IN QUANTITY + +If you publish printed copies (or copies in media that commonly have +printed covers) of the Document, numbering more than 100, and the +Document's license notice requires Cover Texts, you must enclose the +copies in covers that carry, clearly and legibly, all these Cover +Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on +the back cover. Both covers must also clearly and legibly identify +you as the publisher of these copies. The front cover must present +the full title with all words of the title equally prominent and +visible. You may add other material on the covers in addition. +Copying with changes limited to the covers, as long as they preserve +the title of the Document and satisfy these conditions, can be treated +as verbatim copying in other respects. + +If the required texts for either cover are too voluminous to fit +legibly, you should put the first ones listed (as many as fit +reasonably) on the actual cover, and continue the rest onto adjacent +pages. + +If you publish or distribute Opaque copies of the Document numbering +more than 100, you must either include a machine-readable Transparent +copy along with each Opaque copy, or state in or with each Opaque copy +a computer-network location from which the general network-using +public has access to download using public-standard network protocols +a complete Transparent copy of the Document, free of added material. +If you use the latter option, you must take reasonably prudent steps, +when you begin distribution of Opaque copies in quantity, to ensure +that this Transparent copy will remain thus accessible at the stated +location until at least one year after the last time you distribute an +Opaque copy (directly or through your agents or retailers) of that +edition to the public. + +It is requested, but not required, that you contact the authors of the +Document well before redistributing any large number of copies, to +give them a chance to provide you with an updated version of the +Document. + + +### 4. MODIFICATIONS + +You may copy and distribute a Modified Version of the Document under +the conditions of sections 2 and 3 above, provided that you release +the Modified Version under precisely this License, with the Modified +Version filling the role of the Document, thus licensing distribution +and modification of the Modified Version to whoever possesses a copy +of it. In addition, you must do these things in the Modified Version: + +* **A.** Use in the Title Page (and on the covers, if any) a title distinct +from that of the Document, and from those of previous versions +(which should, if there were any, be listed in the History section +of the Document). You may use the same title as a previous version +if the original publisher of that version gives permission. +* **B.** List on the Title Page, as authors, one or more persons or entities +responsible for authorship of the modifications in the Modified +Version, together with at least five of the principal authors of the +Document (all of its principal authors, if it has fewer than five), +unless they release you from this requirement. +* **C.** State on the Title page the name of the publisher of the +Modified Version, as the publisher. +* **D.** Preserve all the copyright notices of the Document. +* **E.** Add an appropriate copyright notice for your modifications +adjacent to the other copyright notices. +* **F.** Include, immediately after the copyright notices, a license notice +giving the public permission to use the Modified Version under the +terms of this License, in the form shown in the Addendum below. +* **G.** Preserve in that license notice the full lists of Invariant Sections +and required Cover Texts given in the Document's license notice. +* **H.** Include an unaltered copy of this License. +* **I.** Preserve the section Entitled “History”, Preserve its Title, and add +to it an item stating at least the title, year, new authors, and +publisher of the Modified Version as given on the Title Page. If +there is no section Entitled “History” in the Document, create one +stating the title, year, authors, and publisher of the Document as +given on its Title Page, then add an item describing the Modified +Version as stated in the previous sentence. +* **J.** Preserve the network location, if any, given in the Document for +public access to a Transparent copy of the Document, and likewise +the network locations given in the Document for previous versions +it was based on. These may be placed in the “History” section. +You may omit a network location for a work that was published at +least four years before the Document itself, or if the original +publisher of the version it refers to gives permission. +* **K.** For any section Entitled “Acknowledgements” or “Dedications”, +Preserve the Title of the section, and preserve in the section all +the substance and tone of each of the contributor acknowledgements +and/or dedications given therein. +* **L.** Preserve all the Invariant Sections of the Document, +unaltered in their text and in their titles. Section numbers +or the equivalent are not considered part of the section titles. +* **M.** Delete any section Entitled “Endorsements”. Such a section +may not be included in the Modified Version. +* **N.** Do not retitle any existing section to be Entitled “Endorsements” +or to conflict in title with any Invariant Section. +* **O.** Preserve any Warranty Disclaimers. + +If the Modified Version includes new front-matter sections or +appendices that qualify as Secondary Sections and contain no material +copied from the Document, you may at your option designate some or all +of these sections as invariant. To do this, add their titles to the +list of Invariant Sections in the Modified Version's license notice. +These titles must be distinct from any other section titles. + +You may add a section Entitled “Endorsements”, provided it contains +nothing but endorsements of your Modified Version by various +parties--for example, statements of peer review or that the text has +been approved by an organization as the authoritative definition of a +standard. + +You may add a passage of up to five words as a Front-Cover Text, and a +passage of up to 25 words as a Back-Cover Text, to the end of the list +of Cover Texts in the Modified Version. Only one passage of +Front-Cover Text and one of Back-Cover Text may be added by (or +through arrangements made by) any one entity. If the Document already +includes a cover text for the same cover, previously added by you or +by arrangement made by the same entity you are acting on behalf of, +you may not add another; but you may replace the old one, on explicit +permission from the previous publisher that added the old one. + +The author(s) and publisher(s) of the Document do not by this License +give permission to use their names for publicity for or to assert or +imply endorsement of any Modified Version. + + +### 5. COMBINING DOCUMENTS + +You may combine the Document with other documents released under this +License, under the terms defined in section 4 above for modified +versions, provided that you include in the combination all of the +Invariant Sections of all of the original documents, unmodified, and +list them all as Invariant Sections of your combined work in its +license notice, and that you preserve all their Warranty Disclaimers. + +The combined work need only contain one copy of this License, and +multiple identical Invariant Sections may be replaced with a single +copy. If there are multiple Invariant Sections with the same name but +different contents, make the title of each such section unique by +adding at the end of it, in parentheses, the name of the original +author or publisher of that section if known, or else a unique number. +Make the same adjustment to the section titles in the list of +Invariant Sections in the license notice of the combined work. + +In the combination, you must combine any sections Entitled “History” +in the various original documents, forming one section Entitled +“History”; likewise combine any sections Entitled “Acknowledgements”, +and any sections Entitled “Dedications”. You must delete all sections +Entitled “Endorsements”. + + +### 6. COLLECTIONS OF DOCUMENTS + +You may make a collection consisting of the Document and other +documents released under this License, and replace the individual +copies of this License in the various documents with a single copy +that is included in the collection, provided that you follow the rules +of this License for verbatim copying of each of the documents in all +other respects. + +You may extract a single document from such a collection, and +distribute it individually under this License, provided you insert a +copy of this License into the extracted document, and follow this +License in all other respects regarding verbatim copying of that +document. + + +### 7. AGGREGATION WITH INDEPENDENT WORKS + +A compilation of the Document or its derivatives with other separate +and independent documents or works, in or on a volume of a storage or +distribution medium, is called an “aggregate” if the copyright +resulting from the compilation is not used to limit the legal rights +of the compilation's users beyond what the individual works permit. +When the Document is included in an aggregate, this License does not +apply to the other works in the aggregate which are not themselves +derivative works of the Document. + +If the Cover Text requirement of section 3 is applicable to these +copies of the Document, then if the Document is less than one half of +the entire aggregate, the Document's Cover Texts may be placed on +covers that bracket the Document within the aggregate, or the +electronic equivalent of covers if the Document is in electronic form. +Otherwise they must appear on printed covers that bracket the whole +aggregate. + + +### 8. TRANSLATION + +Translation is considered a kind of modification, so you may +distribute translations of the Document under the terms of section 4. +Replacing Invariant Sections with translations requires special +permission from their copyright holders, but you may include +translations of some or all Invariant Sections in addition to the +original versions of these Invariant Sections. You may include a +translation of this License, and all the license notices in the +Document, and any Warranty Disclaimers, provided that you also include +the original English version of this License and the original versions +of those notices and disclaimers. In case of a disagreement between +the translation and the original version of this License or a notice +or disclaimer, the original version will prevail. + +If a section in the Document is Entitled “Acknowledgements”, +“Dedications”, or “History”, the requirement (section 4) to Preserve +its Title (section 1) will typically require changing the actual +title. + + +### 9. TERMINATION + +You may not copy, modify, sublicense, or distribute the Document +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense, or distribute it is void, and +will automatically terminate your rights under this License. + +However, if you cease all violation of this License, then your license +from a particular copyright holder is reinstated (a) provisionally, +unless and until the copyright holder explicitly and finally +terminates your license, and (b) permanently, if the copyright holder +fails to notify you of the violation by some reasonable means prior to +60 days after the cessation. + +Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + +Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, receipt of a copy of some or all of the same material does +not give you any rights to use it. + + +### 10. FUTURE REVISIONS OF THIS LICENSE + +The Free Software Foundation may publish new, revised versions of the +GNU Free Documentation 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. See +<>. + +Each version of the License is given a distinguishing version number. +If the Document specifies that a particular numbered version of this +License “or any later version” applies to it, you have the option of +following the terms and conditions either of that specified version or +of any later version that has been published (not as a draft) by the +Free Software Foundation. If the Document does not specify a version +number of this License, you may choose any version ever published (not +as a draft) by the Free Software Foundation. If the Document +specifies that a proxy can decide which future versions of this +License can be used, that proxy's public statement of acceptance of a +version permanently authorizes you to choose that version for the +Document. + +### 11. RELICENSING + +“Massive Multiauthor Collaboration Site” (or “MMC Site”) means any +World Wide Web server that publishes copyrightable works and also +provides prominent facilities for anybody to edit those works. A +public wiki that anybody can edit is an example of such a server. A +“Massive Multiauthor Collaboration” (or “MMC”) contained in the site +means any set of copyrightable works thus published on the MMC site. + +“CC-BY-SA” means the Creative Commons Attribution-Share Alike 3.0 +license published by Creative Commons Corporation, a not-for-profit +corporation with a principal place of business in San Francisco, +California, as well as future copyleft versions of that license +published by that same organization. + +“Incorporate” means to publish or republish a Document, in whole or in +part, as part of another Document. + +An MMC is “eligible for relicensing” if it is licensed under this +License, and if all works that were first published under this License +somewhere other than this MMC, and subsequently incorporated in whole or +in part into the MMC, (1) had no cover texts or invariant sections, and +(2) were thus incorporated prior to November 1, 2008. + +The operator of an MMC Site may republish an MMC contained in the site +under CC-BY-SA on the same site at any time before August 1, 2009, +provided the MMC is eligible for relicensing. + + +## ADDENDUM: How to use this License for your documents + +To use this License in a document you have written, include a copy of +the License in the document and put the following copyright and +license notices just after the title page: + + Copyright (c) YEAR YOUR NAME. + Permission is granted to copy, distribute and/or modify this document + under the terms of the GNU Free Documentation License, Version 1.3 + or any later version published by the Free Software Foundation; + with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. + A copy of the license is included in the section entitled “GNU + Free Documentation License”. + +If you have Invariant Sections, Front-Cover Texts and Back-Cover Texts, +replace the `with...Texts.` line with this: + + with the Invariant Sections being LIST THEIR TITLES, with the + Front-Cover Texts being LIST, and with the Back-Cover Texts being LIST. + +If you have Invariant Sections without Cover Texts, or some other +combination of the three, merge those two alternatives to suit the +situation. + +If your document contains nontrivial examples of program code, we +recommend releasing these examples in parallel under your choice of +free software license, such as the GNU General Public License, +to permit their use in free software. diff --git a/doc/libhttpserver.md b/doc/libhttpserver.md deleted file mode 100644 index 742013e9..00000000 --- a/doc/libhttpserver.md +++ /dev/null @@ -1,1073 +0,0 @@ - - -The libhttpserver (0.7.1) reference manual -========================================== - -Copying -======= -This manual is for libhttpserver, C++ library for creating an -embedded Rest HTTP server (and more). - -> Permission is granted to copy, distribute and/or modify this document -> under the terms of the GNU Free Documentation License, Version 1.3 -> or any later version published by the Free Software Foundation; -> with no Invariant Sections, no Front-Cover Texts, and no Back-Cover -> Texts. A copy of the license is included in the section entitled GNU -> Free Documentation License. - -Contents -======== -* Introduction. -* Requirements. -* Compilation. -* Constants. -* Structures and classes type definition. -* Callback functions definition. -* Create and work with server. -* Registering resources. -* Building responses to requests. -* Whitelists and Blacklists. -* Simple comet semantics. -* Utilizing Authentication. -* Obtaining and modifying status information. - -Appendices ----------- -* GNU-LGPL: The GNU Lesser General Public License says how you can copy and share almost all of libhttpserver. -* GNU-FDL: The GNU Free Documentation License says how you can copy and share the documentation of libhttpserver. - -Introduction -============ -libhttpserver is meant to constitute an easy system to build HTTP -servers with REST fashion. -libhttpserver is based on libmicrohttpd and, like this, it is a -daemon library. -The mission of this library is to support all possible HTTP features -directly and with a simple semantic allowing then the user to concentrate -only on his application and not on HTTP request handling details. - -The library is supposed to work transparently for the client Implementing -the business logic and using the library itself to realize an interface. -If the user wants it must be able to change every behavior of the library -itself through the registration of callbacks. - -Like the api is based on (libmicrohttpd), libhttpserver is able to decode -certain body format a and automatically format them in object oriented -fashion. This is true for query arguments and for *POST* and *PUT* -requests bodies if *application/x-www-form-urlencoded* or -*multipart/form-data* header are passed. - -The header reproduce all the constants defined by libhttpserver. -These maps various constant used by the HTTP protocol that are exported -as a convenience for users of the library. Is is possible for the user -to define their own extensions of the HTTP standard and use those with -libhttpserver. - -All functions are guaranteed to be completely reentrant and -thread-safe (unless differently specified). -Additionally, clients can specify resource limits on the overall -number of connections, number of connections per IP address and memory -used per connection to avoid resource exhaustion. - -Requirements -============ -* g++ >= 4.1.2 -* libmicrohttpd >= 0.9.7 -* doxygen (if you want to build code reference) - -Compilation -=========== -libhttpserver uses the standard system where the usual build process -involves running -> ./bootstrap -> mkdir build -> cd build -> ../configure -> make -> make install - -Optional parameters to configure script ---------------------------------------- -A complete list of parameters can be obtained running 'configure --help'. -Here are listed the libhttpserver specific options (the canonical configure options are also supported). - -* --enable-same-directory-build: enable to compile in the same directory. This is heavily discouraged. (def=no) -* --enable-debug: enable debug data generation (def=no) -* --enable-cpp11: enable c++11 std classes (def=no) -* --disable-doxygen-doc: don't generate any doxygen documentation -* --disable-doxygen-dot: don't generate graphics for doxygen documentation -* --disable-doxygen-man: don't generate doxygen manual pages -* --enable-doxygen-rtf: generate doxygen RTF documentation -* --enable-doxygen-xml: generate doxygen XML documentation -* --enable-doxygen-chm: generate doxygen compressed HTML help documentation -* --enable-doxygen-chi: generate doxygen seperate compressed HTML help index file -* --disable-doxygen-html: don't generate doxygen plain HTML documentation -* --enable-doxygen-ps: generate doxygen PostScript documentation -* --enable-doxygen-pdf: generate doxygen PDF documentation - -Constants -========= -W.I.P. - -Structures and classes type definition -====================================== -* http_resource (CPP class): Represents the resource associated with a specific http endpoint. -* http_request (CPP class): Represents the request received by the resource that process it. -* http_response (CPP class): Represents the response sent by the server once the resource finished its work. -* event_supplier (CPP class): Represents a class that supplies events to the webserver. It can be used to trigger the internal select of it. -* webserver (CPP class): Represents the daemon listening on a socket for HTTP traffic. - -GNU Lesser General Public License -================================= - -Version 2.1, February 1999 - -Copyright © 1991, 1999 Free Software Foundation, Inc. -51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -Everyone is permitted to copy and distribute verbatim copies -of this license document, but changing it is not allowed. - -_This is the first released version of the Lesser GPL. It also counts -as the successor of the GNU Library Public License, version 2, hence -the version number 2.1._ - -### Preamble - -The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -Licenses are intended to guarantee your freedom to share and change -free software--to make sure the software is free for all its users. - -This license, the Lesser General Public License, applies to some -specially designated software packages--typically libraries--of the -Free Software Foundation and other authors who decide to use it. You -can use it too, but we suggest you first think carefully about whether -this license or the ordinary General Public License is the better -strategy to use in any particular case, based on the explanations below. - -When we speak of free software, we are referring to freedom of use, -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 and use pieces of -it in new free programs; and that you are informed that you can do -these things. - -To protect your rights, we need to make restrictions that forbid -distributors to deny you these rights or to ask you to surrender these -rights. These restrictions translate to certain responsibilities for -you if you distribute copies of the library or if you modify it. - -For example, if you distribute copies of the library, whether gratis -or for a fee, you must give the recipients all the rights that we gave -you. You must make sure that they, too, receive or can get the source -code. If you link other code with the library, you must provide -complete object files to the recipients, so that they can relink them -with the library after making changes to the library and recompiling -it. And you must show them these terms so they know their rights. - -We protect your rights with a two-step method: (1) we copyright the -library, and (2) we offer you this license, which gives you legal -permission to copy, distribute and/or modify the library. - -To protect each distributor, we want to make it very clear that -there is no warranty for the free library. Also, if the library is -modified by someone else and passed on, the recipients should know -that what they have is not the original version, so that the original -author's reputation will not be affected by problems that might be -introduced by others. - -Finally, software patents pose a constant threat to the existence of -any free program. We wish to make sure that a company cannot -effectively restrict the users of a free program by obtaining a -restrictive license from a patent holder. Therefore, we insist that -any patent license obtained for a version of the library must be -consistent with the full freedom of use specified in this license. - -Most GNU software, including some libraries, is covered by the -ordinary GNU General Public License. This license, the GNU Lesser -General Public License, applies to certain designated libraries, and -is quite different from the ordinary General Public License. We use -this license for certain libraries in order to permit linking those -libraries into non-free programs. - -When a program is linked with a library, whether statically or using -a shared library, the combination of the two is legally speaking a -combined work, a derivative of the original library. The ordinary -General Public License therefore permits such linking only if the -entire combination fits its criteria of freedom. The Lesser General -Public License permits more lax criteria for linking other code with -the library. - -We call this license the “Lesser” General Public License because it -does Less to protect the user's freedom than the ordinary General -Public License. It also provides other free software developers Less -of an advantage over competing non-free programs. These disadvantages -are the reason we use the ordinary General Public License for many -libraries. However, the Lesser license provides advantages in certain -special circumstances. - -For example, on rare occasions, there may be a special need to -encourage the widest possible use of a certain library, so that it becomes -a de-facto standard. To achieve this, non-free programs must be -allowed to use the library. A more frequent case is that a free -library does the same job as widely used non-free libraries. In this -case, there is little to gain by limiting the free library to free -software only, so we use the Lesser General Public License. - -In other cases, permission to use a particular library in non-free -programs enables a greater number of people to use a large body of -free software. For example, permission to use the GNU C Library in -non-free programs enables many more people to use the whole GNU -operating system, as well as its variant, the GNU/Linux operating -system. - -Although the Lesser General Public License is Less protective of the -users' freedom, it does ensure that the user of a program that is -linked with the Library has the freedom and the wherewithal to run -that program using a modified version of the Library. - -The precise terms and conditions for copying, distribution and -modification follow. Pay close attention to the difference between a -“work based on the library” and a “work that uses the library”. The -former contains code derived from the library, whereas the latter must -be combined with the library in order to run. - -### TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - -**0.** This License Agreement applies to any software library or other -program which contains a notice placed by the copyright holder or -other authorized party saying it may be distributed under the terms of -this Lesser General Public License (also called “this License”). -Each licensee is addressed as “you”. - -A “library” means a collection of software functions and/or data -prepared so as to be conveniently linked with application programs -(which use some of those functions and data) to form executables. - -The “Library”, below, refers to any such software library or work -which has been distributed under these terms. A “work based on the -Library” means either the Library or any derivative work under -copyright law: that is to say, a work containing the Library or a -portion of it, either verbatim or with modifications and/or translated -straightforwardly into another language. (Hereinafter, translation is -included without limitation in the term “modification”.) - -“Source code” for a work means the preferred form of the work for -making modifications to it. For a library, complete source code means -all the source code for all modules it contains, plus any associated -interface definition files, plus the scripts used to control compilation -and installation of the library. - -Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running a program using the Library is not restricted, and output from -such a program is covered only if its contents constitute a work based -on the Library (independent of the use of the Library in a tool for -writing it). Whether that is true depends on what the Library does -and what the program that uses the Library does. - -**1.** You may copy and distribute verbatim copies of the Library's -complete source code as you receive it, in any medium, provided that -you conspicuously and appropriately publish on each copy an -appropriate copyright notice and disclaimer of warranty; keep intact -all the notices that refer to this License and to the absence of any -warranty; and distribute a copy of this License along with the -Library. - -You may charge a fee for the physical act of transferring a copy, -and you may at your option offer warranty protection in exchange for a -fee. - -**2.** You may modify your copy or copies of the Library or any portion -of it, thus forming a work based on the Library, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - -* **a)** The modified work must itself be a software library. -* **b)** You must cause the files modified to carry prominent notices -stating that you changed the files and the date of any change. -* **c)** You must cause the whole of the work to be licensed at no -charge to all third parties under the terms of this License. -* **d)** If a facility in the modified Library refers to a function or a -table of data to be supplied by an application program that uses -the facility, other than as an argument passed when the facility -is invoked, then you must make a good faith effort to ensure that, -in the event an application does not supply such function or -table, the facility still operates, and performs whatever part of -its purpose remains meaningful. -(For example, a function in a library to compute square roots has -a purpose that is entirely well-defined independent of the -application. Therefore, Subsection 2d requires that any -application-supplied function or table used by this function must -be optional: if the application does not supply it, the square -root function must still compute square roots.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Library, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Library, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote -it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Library. - -In addition, mere aggregation of another work not based on the Library -with the Library (or with a work based on the Library) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - -**3.** You may opt to apply the terms of the ordinary GNU General Public -License instead of this License to a given copy of the Library. To do -this, you must alter all the notices that refer to this License, so -that they refer to the ordinary GNU General Public License, version 2, -instead of to this License. (If a newer version than version 2 of the -ordinary GNU General Public License has appeared, then you can specify -that version instead if you wish.) Do not make any other change in -these notices. - -Once this change is made in a given copy, it is irreversible for -that copy, so the ordinary GNU General Public License applies to all -subsequent copies and derivative works made from that copy. - -This option is useful when you wish to copy part of the code of -the Library into a program that is not a library. - -**4.** You may copy and distribute the Library (or a portion or -derivative of it, under Section 2) in object code or executable form -under the terms of Sections 1 and 2 above provided that you accompany -it with the complete corresponding machine-readable source code, which -must be distributed under the terms of Sections 1 and 2 above on a -medium customarily used for software interchange. - -If distribution of object code is made by offering access to copy -from a designated place, then offering equivalent access to copy the -source code from the same place satisfies the requirement to -distribute the source code, even though third parties are not -compelled to copy the source along with the object code. - -**5.** A program that contains no derivative of any portion of the -Library, but is designed to work with the Library by being compiled or -linked with it, is called a “work that uses the Library”. Such a -work, in isolation, is not a derivative work of the Library, and -therefore falls outside the scope of this License. - -However, linking a “work that uses the Library” with the Library -creates an executable that is a derivative of the Library (because it -contains portions of the Library), rather than a “work that uses the -library”. The executable is therefore covered by this License. -Section 6 states terms for distribution of such executables. - -When a “work that uses the Library” uses material from a header file -that is part of the Library, the object code for the work may be a -derivative work of the Library even though the source code is not. -Whether this is true is especially significant if the work can be -linked without the Library, or if the work is itself a library. The -threshold for this to be true is not precisely defined by law. - -If such an object file uses only numerical parameters, data -structure layouts and accessors, and small macros and small inline -functions (ten lines or less in length), then the use of the object -file is unrestricted, regardless of whether it is legally a derivative -work. (Executables containing this object code plus portions of the -Library will still fall under Section 6.) - -Otherwise, if the work is a derivative of the Library, you may -distribute the object code for the work under the terms of Section 6. -Any executables containing that work also fall under Section 6, -whether or not they are linked directly with the Library itself. - -**6.** As an exception to the Sections above, you may also combine or -link a “work that uses the Library” with the Library to produce a -work containing portions of the Library, and distribute that work -under terms of your choice, provided that the terms permit -modification of the work for the customer's own use and reverse -engineering for debugging such modifications. - -You must give prominent notice with each copy of the work that the -Library is used in it and that the Library and its use are covered by -this License. You must supply a copy of this License. If the work -during execution displays copyright notices, you must include the -copyright notice for the Library among them, as well as a reference -directing the user to the copy of this License. Also, you must do one -of these things: - -* **a)** Accompany the work with the complete corresponding -machine-readable source code for the Library including whatever -changes were used in the work (which must be distributed under -Sections 1 and 2 above); and, if the work is an executable linked -with the Library, with the complete machine-readable “work that -uses the Library”, as object code and/or source code, so that the -user can modify the Library and then relink to produce a modified -executable containing the modified Library. (It is understood -that the user who changes the contents of definitions files in the -Library will not necessarily be able to recompile the application -to use the modified definitions.) -* **b)** Use a suitable shared library mechanism for linking with the -Library. A suitable mechanism is one that (1) uses at run time a -copy of the library already present on the user's computer system, -rather than copying library functions into the executable, and (2) -will operate properly with a modified version of the library, if -the user installs one, as long as the modified version is -interface-compatible with the version that the work was made with. -* **c)** Accompany the work with a written offer, valid for at -least three years, to give the same user the materials -specified in Subsection 6a, above, for a charge no more -than the cost of performing this distribution. -* **d)** If distribution of the work is made by offering access to copy -from a designated place, offer equivalent access to copy the above -specified materials from the same place. -* **e)** Verify that the user has already received a copy of these -materials or that you have already sent this user a copy. - -For an executable, the required form of the “work that uses the -Library” must include any data and utility programs needed for -reproducing the executable from it. However, as a special exception, -the materials to be distributed need not include anything that is -normally distributed (in either source or binary form) with the major -components (compiler, kernel, and so on) of the operating system on -which the executable runs, unless that component itself accompanies -the executable. - -It may happen that this requirement contradicts the license -restrictions of other proprietary libraries that do not normally -accompany the operating system. Such a contradiction means you cannot -use both them and the Library together in an executable that you -distribute. - -**7.** You may place library facilities that are a work based on the -Library side-by-side in a single library together with other library -facilities not covered by this License, and distribute such a combined -library, provided that the separate distribution of the work based on -the Library and of the other library facilities is otherwise -permitted, and provided that you do these two things: - -* **a)** Accompany the combined library with a copy of the same work -based on the Library, uncombined with any other library -facilities. This must be distributed under the terms of the -Sections above. -* **b)** Give prominent notice with the combined library of the fact -that part of it is a work based on the Library, and explaining -where to find the accompanying uncombined form of the same work. - -**8.** You may not copy, modify, sublicense, link with, or distribute -the Library except as expressly provided under this License. Any -attempt otherwise to copy, modify, sublicense, link with, or -distribute the Library is void, and will automatically terminate your -rights under this License. However, parties who have received copies, -or rights, from you under this License will not have their licenses -terminated so long as such parties remain in full compliance. - -**9.** You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Library or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Library (or any work based on the -Library), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Library or works based on it. - -**10.** Each time you redistribute the Library (or any work based on the -Library), the recipient automatically receives a license from the -original licensor to copy, distribute, link with or modify the Library -subject to these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties with -this License. - -**11.** If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Library at all. For example, if a patent -license would not permit royalty-free redistribution of the Library by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Library. - -If any portion of this section is held invalid or unenforceable under any -particular circumstance, the balance of the section is intended to apply, -and the section as a whole is intended to apply in other circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - -**12.** If the distribution and/or use of the Library is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Library under this License may add -an explicit geographical distribution limitation excluding those countries, -so that distribution is permitted only in or among countries not thus -excluded. In such case, this License incorporates the limitation as if -written in the body of this License. - -**13.** The Free Software Foundation may publish revised and/or new -versions of the Lesser General Public License from time to time. -Such new versions will be similar in spirit to the present version, -but may differ in detail to address new problems or concerns. - -Each version is given a distinguishing version number. If the Library -specifies a version number of this License which applies to it and -“any later version”, you have the option of following the terms and -conditions either of that version or of any later version published by -the Free Software Foundation. If the Library does not specify a -license version number, you may choose any version ever published by -the Free Software Foundation. - -**14.** If you wish to incorporate parts of the Library into other free -programs whose distribution conditions are incompatible with these, -write to the author to ask for permission. For software which is -copyrighted by the Free Software Foundation, write to the Free -Software Foundation; we sometimes make exceptions for this. Our -decision will be guided by the two goals of preserving the free status -of all derivatives of our free software and of promoting the sharing -and reuse of software generally. - -### NO WARRANTY - -**15.** BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO -WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. -EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR -OTHER PARTIES PROVIDE THE LIBRARY “AS IS” WITHOUT WARRANTY OF ANY -KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE -LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME -THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - -**16.** IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN -WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY -AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU -FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR -CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE -LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING -RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A -FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF -SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGES. - -END OF TERMS AND CONDITIONS - -### How to Apply These Terms to Your New Libraries - -If you develop a new library, and you want it to be of the greatest -possible use to the public, we recommend making it free software that -everyone can redistribute and change. You can do so by permitting -redistribution under these terms (or, alternatively, under the terms of the -ordinary General Public License). - -To apply these terms, attach the following notices to the library. It is -safest to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least the -“copyright” line and a pointer to where the full notice is found. - - - Copyright (C) - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - -Also add information on how to contact you by electronic and paper mail. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a “copyright disclaimer” for the library, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the - library `Frob' (a library for tweaking knobs) written by James Random Hacker. - - , 1 April 1990 - Ty Coon, President of Vice - -That's all there is to it! - -GNU Free Documentation License -============================== - -Version 1.3, 3 November 2008 - -Copyright © 2000, 2001, 2002, 2007, 2008 Free Software Foundation, Inc. <> - -Everyone is permitted to copy and distribute verbatim copies -of this license document, but changing it is not allowed. - -### 0. PREAMBLE - -The purpose of this License is to make a manual, textbook, or other -functional and useful document “free” in the sense of freedom: to -assure everyone the effective freedom to copy and redistribute it, -with or without modifying it, either commercially or noncommercially. -Secondarily, this License preserves for the author and publisher a way -to get credit for their work, while not being considered responsible -for modifications made by others. - -This License is a kind of “copyleft”, which means that derivative -works of the document must themselves be free in the same sense. It -complements the GNU General Public License, which is a copyleft -license designed for free software. - -We have designed this License in order to use it for manuals for free -software, because free software needs free documentation: a free -program should come with manuals providing the same freedoms that the -software does. But this License is not limited to software manuals; -it can be used for any textual work, regardless of subject matter or -whether it is published as a printed book. We recommend this License -principally for works whose purpose is instruction or reference. - - -### 1. APPLICABILITY AND DEFINITIONS - -This License applies to any manual or other work, in any medium, that -contains a notice placed by the copyright holder saying it can be -distributed under the terms of this License. Such a notice grants a -world-wide, royalty-free license, unlimited in duration, to use that -work under the conditions stated herein. The “Document”, below, -refers to any such manual or work. Any member of the public is a -licensee, and is addressed as “you”. You accept the license if you -copy, modify or distribute the work in a way requiring permission -under copyright law. - -A “Modified Version” of the Document means any work containing the -Document or a portion of it, either copied verbatim, or with -modifications and/or translated into another language. - -A “Secondary Section” is a named appendix or a front-matter section of -the Document that deals exclusively with the relationship of the -publishers or authors of the Document to the Document's overall -subject (or to related matters) and contains nothing that could fall -directly within that overall subject. (Thus, if the Document is in -part a textbook of mathematics, a Secondary Section may not explain -any mathematics.) The relationship could be a matter of historical -connection with the subject or with related matters, or of legal, -commercial, philosophical, ethical or political position regarding -them. - -The “Invariant Sections” are certain Secondary Sections whose titles -are designated, as being those of Invariant Sections, in the notice -that says that the Document is released under this License. If a -section does not fit the above definition of Secondary then it is not -allowed to be designated as Invariant. The Document may contain zero -Invariant Sections. If the Document does not identify any Invariant -Sections then there are none. - -The “Cover Texts” are certain short passages of text that are listed, -as Front-Cover Texts or Back-Cover Texts, in the notice that says that -the Document is released under this License. A Front-Cover Text may -be at most 5 words, and a Back-Cover Text may be at most 25 words. - -A “Transparent” copy of the Document means a machine-readable copy, -represented in a format whose specification is available to the -general public, that is suitable for revising the document -straightforwardly with generic text editors or (for images composed of -pixels) generic paint programs or (for drawings) some widely available -drawing editor, and that is suitable for input to text formatters or -for automatic translation to a variety of formats suitable for input -to text formatters. A copy made in an otherwise Transparent file -format whose markup, or absence of markup, has been arranged to thwart -or discourage subsequent modification by readers is not Transparent. -An image format is not Transparent if used for any substantial amount -of text. A copy that is not “Transparent” is called “Opaque”. - -Examples of suitable formats for Transparent copies include plain -ASCII without markup, Texinfo input format, LaTeX input format, SGML -or XML using a publicly available DTD, and standard-conforming simple -HTML, PostScript or PDF designed for human modification. Examples of -transparent image formats include PNG, XCF and JPG. Opaque formats -include proprietary formats that can be read and edited only by -proprietary word processors, SGML or XML for which the DTD and/or -processing tools are not generally available, and the -machine-generated HTML, PostScript or PDF produced by some word -processors for output purposes only. - -The “Title Page” means, for a printed book, the title page itself, -plus such following pages as are needed to hold, legibly, the material -this License requires to appear in the title page. For works in -formats which do not have any title page as such, “Title Page” means -the text near the most prominent appearance of the work's title, -preceding the beginning of the body of the text. - -The “publisher” means any person or entity that distributes copies of -the Document to the public. - -A section “Entitled XYZ” means a named subunit of the Document whose -title either is precisely XYZ or contains XYZ in parentheses following -text that translates XYZ in another language. (Here XYZ stands for a -specific section name mentioned below, such as “Acknowledgements”, -“Dedications”, “Endorsements”, or “History”.) To “Preserve the Title” -of such a section when you modify the Document means that it remains a -section “Entitled XYZ” according to this definition. - -The Document may include Warranty Disclaimers next to the notice which -states that this License applies to the Document. These Warranty -Disclaimers are considered to be included by reference in this -License, but only as regards disclaiming warranties: any other -implication that these Warranty Disclaimers may have is void and has -no effect on the meaning of this License. - -### 2. VERBATIM COPYING - -You may copy and distribute the Document in any medium, either -commercially or noncommercially, provided that this License, the -copyright notices, and the license notice saying this License applies -to the Document are reproduced in all copies, and that you add no -other conditions whatsoever to those of this License. You may not use -technical measures to obstruct or control the reading or further -copying of the copies you make or distribute. However, you may accept -compensation in exchange for copies. If you distribute a large enough -number of copies you must also follow the conditions in section 3. - -You may also lend copies, under the same conditions stated above, and -you may publicly display copies. - - -### 3. COPYING IN QUANTITY - -If you publish printed copies (or copies in media that commonly have -printed covers) of the Document, numbering more than 100, and the -Document's license notice requires Cover Texts, you must enclose the -copies in covers that carry, clearly and legibly, all these Cover -Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on -the back cover. Both covers must also clearly and legibly identify -you as the publisher of these copies. The front cover must present -the full title with all words of the title equally prominent and -visible. You may add other material on the covers in addition. -Copying with changes limited to the covers, as long as they preserve -the title of the Document and satisfy these conditions, can be treated -as verbatim copying in other respects. - -If the required texts for either cover are too voluminous to fit -legibly, you should put the first ones listed (as many as fit -reasonably) on the actual cover, and continue the rest onto adjacent -pages. - -If you publish or distribute Opaque copies of the Document numbering -more than 100, you must either include a machine-readable Transparent -copy along with each Opaque copy, or state in or with each Opaque copy -a computer-network location from which the general network-using -public has access to download using public-standard network protocols -a complete Transparent copy of the Document, free of added material. -If you use the latter option, you must take reasonably prudent steps, -when you begin distribution of Opaque copies in quantity, to ensure -that this Transparent copy will remain thus accessible at the stated -location until at least one year after the last time you distribute an -Opaque copy (directly or through your agents or retailers) of that -edition to the public. - -It is requested, but not required, that you contact the authors of the -Document well before redistributing any large number of copies, to -give them a chance to provide you with an updated version of the -Document. - - -### 4. MODIFICATIONS - -You may copy and distribute a Modified Version of the Document under -the conditions of sections 2 and 3 above, provided that you release -the Modified Version under precisely this License, with the Modified -Version filling the role of the Document, thus licensing distribution -and modification of the Modified Version to whoever possesses a copy -of it. In addition, you must do these things in the Modified Version: - -* **A.** Use in the Title Page (and on the covers, if any) a title distinct -from that of the Document, and from those of previous versions -(which should, if there were any, be listed in the History section -of the Document). You may use the same title as a previous version -if the original publisher of that version gives permission. -* **B.** List on the Title Page, as authors, one or more persons or entities -responsible for authorship of the modifications in the Modified -Version, together with at least five of the principal authors of the -Document (all of its principal authors, if it has fewer than five), -unless they release you from this requirement. -* **C.** State on the Title page the name of the publisher of the -Modified Version, as the publisher. -* **D.** Preserve all the copyright notices of the Document. -* **E.** Add an appropriate copyright notice for your modifications -adjacent to the other copyright notices. -* **F.** Include, immediately after the copyright notices, a license notice -giving the public permission to use the Modified Version under the -terms of this License, in the form shown in the Addendum below. -* **G.** Preserve in that license notice the full lists of Invariant Sections -and required Cover Texts given in the Document's license notice. -* **H.** Include an unaltered copy of this License. -* **I.** Preserve the section Entitled “History”, Preserve its Title, and add -to it an item stating at least the title, year, new authors, and -publisher of the Modified Version as given on the Title Page. If -there is no section Entitled “History” in the Document, create one -stating the title, year, authors, and publisher of the Document as -given on its Title Page, then add an item describing the Modified -Version as stated in the previous sentence. -* **J.** Preserve the network location, if any, given in the Document for -public access to a Transparent copy of the Document, and likewise -the network locations given in the Document for previous versions -it was based on. These may be placed in the “History” section. -You may omit a network location for a work that was published at -least four years before the Document itself, or if the original -publisher of the version it refers to gives permission. -* **K.** For any section Entitled “Acknowledgements” or “Dedications”, -Preserve the Title of the section, and preserve in the section all -the substance and tone of each of the contributor acknowledgements -and/or dedications given therein. -* **L.** Preserve all the Invariant Sections of the Document, -unaltered in their text and in their titles. Section numbers -or the equivalent are not considered part of the section titles. -* **M.** Delete any section Entitled “Endorsements”. Such a section -may not be included in the Modified Version. -* **N.** Do not retitle any existing section to be Entitled “Endorsements” -or to conflict in title with any Invariant Section. -* **O.** Preserve any Warranty Disclaimers. - -If the Modified Version includes new front-matter sections or -appendices that qualify as Secondary Sections and contain no material -copied from the Document, you may at your option designate some or all -of these sections as invariant. To do this, add their titles to the -list of Invariant Sections in the Modified Version's license notice. -These titles must be distinct from any other section titles. - -You may add a section Entitled “Endorsements”, provided it contains -nothing but endorsements of your Modified Version by various -parties--for example, statements of peer review or that the text has -been approved by an organization as the authoritative definition of a -standard. - -You may add a passage of up to five words as a Front-Cover Text, and a -passage of up to 25 words as a Back-Cover Text, to the end of the list -of Cover Texts in the Modified Version. Only one passage of -Front-Cover Text and one of Back-Cover Text may be added by (or -through arrangements made by) any one entity. If the Document already -includes a cover text for the same cover, previously added by you or -by arrangement made by the same entity you are acting on behalf of, -you may not add another; but you may replace the old one, on explicit -permission from the previous publisher that added the old one. - -The author(s) and publisher(s) of the Document do not by this License -give permission to use their names for publicity for or to assert or -imply endorsement of any Modified Version. - - -### 5. COMBINING DOCUMENTS - -You may combine the Document with other documents released under this -License, under the terms defined in section 4 above for modified -versions, provided that you include in the combination all of the -Invariant Sections of all of the original documents, unmodified, and -list them all as Invariant Sections of your combined work in its -license notice, and that you preserve all their Warranty Disclaimers. - -The combined work need only contain one copy of this License, and -multiple identical Invariant Sections may be replaced with a single -copy. If there are multiple Invariant Sections with the same name but -different contents, make the title of each such section unique by -adding at the end of it, in parentheses, the name of the original -author or publisher of that section if known, or else a unique number. -Make the same adjustment to the section titles in the list of -Invariant Sections in the license notice of the combined work. - -In the combination, you must combine any sections Entitled “History” -in the various original documents, forming one section Entitled -“History”; likewise combine any sections Entitled “Acknowledgements”, -and any sections Entitled “Dedications”. You must delete all sections -Entitled “Endorsements”. - - -### 6. COLLECTIONS OF DOCUMENTS - -You may make a collection consisting of the Document and other -documents released under this License, and replace the individual -copies of this License in the various documents with a single copy -that is included in the collection, provided that you follow the rules -of this License for verbatim copying of each of the documents in all -other respects. - -You may extract a single document from such a collection, and -distribute it individually under this License, provided you insert a -copy of this License into the extracted document, and follow this -License in all other respects regarding verbatim copying of that -document. - - -### 7. AGGREGATION WITH INDEPENDENT WORKS - -A compilation of the Document or its derivatives with other separate -and independent documents or works, in or on a volume of a storage or -distribution medium, is called an “aggregate” if the copyright -resulting from the compilation is not used to limit the legal rights -of the compilation's users beyond what the individual works permit. -When the Document is included in an aggregate, this License does not -apply to the other works in the aggregate which are not themselves -derivative works of the Document. - -If the Cover Text requirement of section 3 is applicable to these -copies of the Document, then if the Document is less than one half of -the entire aggregate, the Document's Cover Texts may be placed on -covers that bracket the Document within the aggregate, or the -electronic equivalent of covers if the Document is in electronic form. -Otherwise they must appear on printed covers that bracket the whole -aggregate. - - -### 8. TRANSLATION - -Translation is considered a kind of modification, so you may -distribute translations of the Document under the terms of section 4. -Replacing Invariant Sections with translations requires special -permission from their copyright holders, but you may include -translations of some or all Invariant Sections in addition to the -original versions of these Invariant Sections. You may include a -translation of this License, and all the license notices in the -Document, and any Warranty Disclaimers, provided that you also include -the original English version of this License and the original versions -of those notices and disclaimers. In case of a disagreement between -the translation and the original version of this License or a notice -or disclaimer, the original version will prevail. - -If a section in the Document is Entitled “Acknowledgements”, -“Dedications”, or “History”, the requirement (section 4) to Preserve -its Title (section 1) will typically require changing the actual -title. - - -### 9. TERMINATION - -You may not copy, modify, sublicense, or distribute the Document -except as expressly provided under this License. Any attempt -otherwise to copy, modify, sublicense, or distribute it is void, and -will automatically terminate your rights under this License. - -However, if you cease all violation of this License, then your license -from a particular copyright holder is reinstated (a) provisionally, -unless and until the copyright holder explicitly and finally -terminates your license, and (b) permanently, if the copyright holder -fails to notify you of the violation by some reasonable means prior to -60 days after the cessation. - -Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - -Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, receipt of a copy of some or all of the same material does -not give you any rights to use it. - - -### 10. FUTURE REVISIONS OF THIS LICENSE - -The Free Software Foundation may publish new, revised versions of the -GNU Free Documentation 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. See -<>. - -Each version of the License is given a distinguishing version number. -If the Document specifies that a particular numbered version of this -License “or any later version” applies to it, you have the option of -following the terms and conditions either of that specified version or -of any later version that has been published (not as a draft) by the -Free Software Foundation. If the Document does not specify a version -number of this License, you may choose any version ever published (not -as a draft) by the Free Software Foundation. If the Document -specifies that a proxy can decide which future versions of this -License can be used, that proxy's public statement of acceptance of a -version permanently authorizes you to choose that version for the -Document. - -### 11. RELICENSING - -“Massive Multiauthor Collaboration Site” (or “MMC Site”) means any -World Wide Web server that publishes copyrightable works and also -provides prominent facilities for anybody to edit those works. A -public wiki that anybody can edit is an example of such a server. A -“Massive Multiauthor Collaboration” (or “MMC”) contained in the site -means any set of copyrightable works thus published on the MMC site. - -“CC-BY-SA” means the Creative Commons Attribution-Share Alike 3.0 -license published by Creative Commons Corporation, a not-for-profit -corporation with a principal place of business in San Francisco, -California, as well as future copyleft versions of that license -published by that same organization. - -“Incorporate” means to publish or republish a Document, in whole or in -part, as part of another Document. - -An MMC is “eligible for relicensing” if it is licensed under this -License, and if all works that were first published under this License -somewhere other than this MMC, and subsequently incorporated in whole or -in part into the MMC, (1) had no cover texts or invariant sections, and -(2) were thus incorporated prior to November 1, 2008. - -The operator of an MMC Site may republish an MMC contained in the site -under CC-BY-SA on the same site at any time before August 1, 2009, -provided the MMC is eligible for relicensing. - - -## ADDENDUM: How to use this License for your documents - -To use this License in a document you have written, include a copy of -the License in the document and put the following copyright and -license notices just after the title page: - - Copyright (c) YEAR YOUR NAME. - Permission is granted to copy, distribute and/or modify this document - under the terms of the GNU Free Documentation License, Version 1.3 - or any later version published by the Free Software Foundation; - with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. - A copy of the license is included in the section entitled “GNU - Free Documentation License”. - -If you have Invariant Sections, Front-Cover Texts and Back-Cover Texts, -replace the `with...Texts.` line with this: - - with the Invariant Sections being LIST THEIR TITLES, with the - Front-Cover Texts being LIST, and with the Back-Cover Texts being LIST. - -If you have Invariant Sections without Cover Texts, or some other -combination of the three, merge those two alternatives to suit the -situation. - -If your document contains nontrivial examples of program code, we -recommend releasing these examples in parallel under your choice of -free software license, such as the GNU General Public License, -to permit their use in free software. From 7849c97273e2789b0dec8268994657ca81844d6f Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Thu, 3 Apr 2014 03:01:15 +0100 Subject: [PATCH 067/623] Avoid to build the webserver always as a single resource --- src/webserver.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/webserver.cpp b/src/webserver.cpp index e68d1ef7..447a7e1e 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -171,6 +171,7 @@ webserver::webserver(const create_webserver& params): regex_checking(params._regex_checking), ban_system_enabled(params._ban_system_enabled), post_process_enabled(params._post_process_enabled), + single_resource(params._single_resource), not_found_resource(params._not_found_resource), method_not_allowed_resource(params._method_not_allowed_resource), method_not_acceptable_resource(params._method_not_acceptable_resource), From 06295467e025c5e73c047a5eea3991e05b8ea5b6 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Thu, 3 Apr 2014 03:19:04 +0100 Subject: [PATCH 068/623] Make url case_insensitive optional in http_endpoint --- src/http_endpoint.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/http_endpoint.cpp b/src/http_endpoint.cpp index e066d68a..59afa375 100644 --- a/src/http_endpoint.cpp +++ b/src/http_endpoint.cpp @@ -55,7 +55,12 @@ http_endpoint::http_endpoint else this->url_modded = "/"; vector parts; + +#ifdef CASE_INSENSITIVE string_utilities::to_lower_copy(url, url_complete); +#else + url_complete = url; +#endif if(url_complete[0] != '/') url_complete = "/" + url_complete; From 9e7e810d31c9fa20cb1a8761ca61e24a8faf36fd Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Thu, 3 Apr 2014 03:19:37 +0100 Subject: [PATCH 069/623] Added test case with double endpoint --- test/basic.cpp | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/test/basic.cpp b/test/basic.cpp index ed8dae12..2c8bd888 100644 --- a/test/basic.cpp +++ b/test/basic.cpp @@ -113,6 +113,24 @@ class only_render_resource : public http_resource } }; +class ok_resource : public http_resource +{ + public: + void render_GET(const http_request& req, http_response** res) + { + *res = new http_string_response("OK", 200, "text/plain"); + } +}; + +class nok_resource : public http_resource +{ + public: + void render_GET(const http_request& req, http_response** res) + { + *res = new http_string_response("NOK", 200, "text/plain"); + } +}; + LT_BEGIN_SUITE(basic_suite) webserver* ws; @@ -130,6 +148,44 @@ LT_BEGIN_SUITE(basic_suite) } LT_END_SUITE(basic_suite) +LT_BEGIN_AUTO_TEST(basic_suite, two_endpoints) + + ok_resource* ok = new ok_resource(); + ws->register_resource("OK", ok); + nok_resource* nok = new nok_resource(); + ws->register_resource("NOK", nok); + + curl_global_init(CURL_GLOBAL_ALL); + std::string s; + { + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/OK"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "OK"); + curl_easy_cleanup(curl); + } + + std::string t; + { + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/NOK"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &t); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(t, "NOK"); + curl_easy_cleanup(curl); + } + +LT_END_AUTO_TEST(two_endpoints) + LT_BEGIN_AUTO_TEST(basic_suite, read_body) simple_resource* resource = new simple_resource(); ws->register_resource("base", resource); From c8cba0860f172a5a238d604457e52068457a7c24 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 20 Apr 2014 00:30:45 +0100 Subject: [PATCH 070/623] Modified cleanup flow to avoid segfault Possible case if modded_request gets deallocated in case of error --- src/webserver.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/webserver.cpp b/src/webserver.cpp index 447a7e1e..cad3a92d 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -213,9 +213,9 @@ void webserver::request_completed ( ) { details::modded_request* mr = static_cast(*con_cls); - mr->ws->internal_comet_manager->complete_request(mr->dhrs->connection_id); - if (0x0 != mr) + if (mr != 0x0) { + mr->ws->internal_comet_manager->complete_request(mr->dhrs->connection_id); if(mr->dhrs.res != 0x0 && mr->dhrs->ca != 0x0) mr->dhrs->ca(mr->dhrs->closure_data); delete mr; From e8331e307a458c262fa9a528ae11304c4cb9dec3 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 20 Apr 2014 00:40:49 +0100 Subject: [PATCH 071/623] Avoid segfault when a request is not actually built --- src/webserver.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/webserver.cpp b/src/webserver.cpp index cad3a92d..b855f2b2 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -215,7 +215,10 @@ void webserver::request_completed ( details::modded_request* mr = static_cast(*con_cls); if (mr != 0x0) { - mr->ws->internal_comet_manager->complete_request(mr->dhrs->connection_id); + if(mr->ws != 0x0) + { + mr->ws->internal_comet_manager->complete_request(mr->dhrs->connection_id); + } if(mr->dhrs.res != 0x0 && mr->dhrs->ca != 0x0) mr->dhrs->ca(mr->dhrs->closure_data); delete mr; From bb9f8b20626eb179ba0de79096f6fa60eff0cf16 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 20 Apr 2014 00:48:14 +0100 Subject: [PATCH 072/623] Moved tests to integ subdir and added new tests --- test/Makefile.am | 2 +- test/{ => integ}/basic.cpp | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) rename test/{ => integ}/basic.cpp (98%) diff --git a/test/Makefile.am b/test/Makefile.am index e5f6bee4..91d63dd4 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -21,7 +21,7 @@ AM_CPPFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/src/httpserver/ METASOURCES = AUTO check_PROGRAMS = basic -basic_SOURCES = basic.cpp +basic_SOURCES = integ/basic.cpp noinst_HEADERS = littletest.hpp AM_CXXFLAGS += -lcurl -Wall -fPIC diff --git a/test/basic.cpp b/test/integ/basic.cpp similarity index 98% rename from test/basic.cpp rename to test/integ/basic.cpp index 2c8bd888..bb162f16 100644 --- a/test/basic.cpp +++ b/test/integ/basic.cpp @@ -323,6 +323,13 @@ LT_BEGIN_AUTO_TEST(basic_suite, only_render) LT_ASSERT_EQ(res, 0); curl_easy_cleanup(curl); + curl = curl_easy_init(); + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "NOT_EXISTENT"); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + curl_easy_cleanup(curl); + curl = curl_easy_init(); curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); curl_easy_setopt(curl, CURLOPT_POST, 1L); From 85580eb0110054f9e67ab108f0eb023bda826312 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Wed, 23 Apr 2014 00:57:55 +0100 Subject: [PATCH 073/623] Changed comet_manager implementation to be private It can be accessed only by the webserver class --- src/httpserver/details/comet_manager.hpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/httpserver/details/comet_manager.hpp b/src/httpserver/details/comet_manager.hpp index 71ee5912..dff82f62 100644 --- a/src/httpserver/details/comet_manager.hpp +++ b/src/httpserver/details/comet_manager.hpp @@ -36,6 +36,8 @@ namespace httpserver { +class webserver; + namespace http { struct httpserver_ska; @@ -46,7 +48,7 @@ namespace details class comet_manager { - public: + private: comet_manager(); ~comet_manager(); @@ -82,12 +84,10 @@ class comet_manager const httpserver::http::http_utils::start_method_T& start_method ); - protected: comet_manager(const comet_manager&) { } - private: std::map > q_messages; std::map > q_waitings; std::map > q_blocks; @@ -97,6 +97,7 @@ class comet_manager pthread_rwlock_t comet_guard; pthread_mutex_t cleanmux; pthread_cond_t cleancond; + friend class httpserver::webserver; }; } //details From c4b17abb20514d52dfd072cdd616d35a7ffe17de Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Wed, 23 Apr 2014 23:49:37 +0100 Subject: [PATCH 074/623] Moved details cpp files to details dir --- src/Makefile.am | 2 +- src/{ => details}/comet_manager.cpp | 0 src/{ => details}/http_endpoint.cpp | 0 3 files changed, 1 insertion(+), 1 deletion(-) rename src/{ => details}/comet_manager.cpp (100%) rename src/{ => details}/http_endpoint.cpp (100%) diff --git a/src/Makefile.am b/src/Makefile.am index dd151f75..af169fee 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -19,7 +19,7 @@ AM_CPPFLAGS = -I../ -I$(srcdir)/httpserver/ METASOURCES = AUTO lib_LTLIBRARIES = libhttpserver.la -libhttpserver_la_SOURCES = string_utilities.cpp webserver.cpp http_utils.cpp http_endpoint.cpp http_request.cpp http_response.cpp http_resource.cpp comet_manager.cpp +libhttpserver_la_SOURCES = string_utilities.cpp webserver.cpp http_utils.cpp http_request.cpp http_response.cpp http_resource.cpp details/comet_manager.cpp details/http_endpoint.cpp noinst_HEADERS = httpserver/string_utilities.hpp httpserver/details/modded_request.hpp httpserver/details/http_response_ptr.hpp httpserver/details/cache_entry.hpp httpserver/details/comet_manager.hpp gettext.h nobase_include_HEADERS = httpserver.hpp httpserver/create_webserver.hpp httpserver/webserver.hpp httpserver/http_utils.hpp httpserver/details/http_endpoint.hpp httpserver/http_request.hpp httpserver/http_response.hpp httpserver/http_resource.hpp httpserver/binders.hpp httpserver/event_supplier.hpp httpserver/details/event_tuple.hpp httpserver/details/http_resource_mirror.hpp AM_CXXFLAGS += -fPIC -Wall diff --git a/src/comet_manager.cpp b/src/details/comet_manager.cpp similarity index 100% rename from src/comet_manager.cpp rename to src/details/comet_manager.cpp diff --git a/src/http_endpoint.cpp b/src/details/http_endpoint.cpp similarity index 100% rename from src/http_endpoint.cpp rename to src/details/http_endpoint.cpp From 24ae6ecf73b59759d36de8be66af17df77e6b56d Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Thu, 1 May 2014 09:05:25 +0100 Subject: [PATCH 075/623] Avoid thread death after start Almost all threads were diying after the webserver start. This was due to the mispositioning of a boolean that has now been moved up. --- src/webserver.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/webserver.cpp b/src/webserver.cpp index b855f2b2..51dac3ec 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -532,6 +532,7 @@ bool webserver::start(bool blocking) if(max_threads > num_threads) num_threads = max_threads; + this->running = true; if(start_method == http_utils::INTERNAL_SELECT) { for(int i = 0; i < num_threads; i++) @@ -583,7 +584,6 @@ bool webserver::start(bool blocking) details::daemon_item* di = new details::daemon_item(this, daemon); daemons.push_back(di); } - this->running = true; bool value_onclose = false; if(blocking) { From 5e7eb07d35760239bdf29a53307cbec4e9ef9243 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Thu, 1 May 2014 09:07:07 +0100 Subject: [PATCH 076/623] Solved warnings at compile time inherent virtual destructors --- src/httpserver/http_resource.hpp | 13 +++++++------ src/httpserver/http_response.hpp | 2 +- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/httpserver/http_resource.hpp b/src/httpserver/http_resource.hpp index a0895994..42076d23 100644 --- a/src/httpserver/http_resource.hpp +++ b/src/httpserver/http_resource.hpp @@ -48,12 +48,6 @@ template class http_resource { public: - /** - * Class destructor - **/ - ~http_resource() - { - } /** * Method used to answer to a generic request * @param req Request passed through http @@ -207,6 +201,13 @@ class http_resource return (*this); } + /** + * Class destructor + **/ + ~http_resource() + { + } + private: friend class webserver; friend void resource_init(std::map& res); diff --git a/src/httpserver/http_response.hpp b/src/httpserver/http_response.hpp index 55821cbc..16c0d432 100644 --- a/src/httpserver/http_response.hpp +++ b/src/httpserver/http_response.hpp @@ -209,7 +209,7 @@ class http_response { } - ~http_response(); + virtual ~http_response(); /** * Method used to get the content from the response. * @return the content in string form From ee474726f8cce87d50837b93461288a8e0ffb4fe Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Fri, 2 May 2014 22:37:49 +0100 Subject: [PATCH 077/623] Avoid to make http_response destrctor virtual --- src/httpserver/http_response.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/httpserver/http_response.hpp b/src/httpserver/http_response.hpp index 16c0d432..55821cbc 100644 --- a/src/httpserver/http_response.hpp +++ b/src/httpserver/http_response.hpp @@ -209,7 +209,7 @@ class http_response { } - virtual ~http_response(); + ~http_response(); /** * Method used to get the content from the response. * @return the content in string form From d76fe9240a59d4491ba36e8e9a78b4328536d996 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 11 May 2014 23:34:34 +0100 Subject: [PATCH 078/623] Updated http_response interface Implemented a much more cler interface based on builder class. Changed http_response to be a non modifiable class (still not completely achieved) --- configure.ac | 2 +- examples/Test.cpp | 129 ----- examples/Test.hpp | 41 -- examples/hello_world.cpp | 2 +- src/Makefile.am | 2 +- src/http_response.cpp | 63 +-- src/httpserver.hpp | 1 + src/httpserver/details/cache_entry.hpp | 3 +- src/httpserver/details/http_response_ptr.hpp | 4 +- src/httpserver/http_response.hpp | 478 ++----------------- src/httpserver/http_response_builder.hpp | 298 ++++++++++++ src/webserver.cpp | 20 +- test/integ/basic.cpp | 29 +- 13 files changed, 399 insertions(+), 673 deletions(-) delete mode 100644 examples/Test.cpp delete mode 100644 examples/Test.hpp create mode 100644 src/httpserver/http_response_builder.hpp diff --git a/configure.ac b/configure.ac index 97c04fe3..6130202f 100644 --- a/configure.ac +++ b/configure.ac @@ -21,7 +21,7 @@ AC_PREREQ(2.57) m4_define([libhttpserver_MAJOR_VERSION],[0])dnl -m4_define([libhttpserver_MINOR_VERSION],[7])dnl +m4_define([libhttpserver_MINOR_VERSION],[8])dnl m4_define([libhttpserver_REVISION],[2])dnl m4_define([libhttpserver_PKG_VERSION],[libhttpserver_MAJOR_VERSION.libhttpserver_MINOR_VERSION.libhttpserver_REVISION])dnl m4_define([libhttpserver_LDF_VERSION],[libhttpserver_MAJOR_VERSION:libhttpserver_MINOR_VERSION:libhttpserver_REVISION])dnl diff --git a/examples/Test.cpp b/examples/Test.cpp deleted file mode 100644 index a260fb04..00000000 --- a/examples/Test.cpp +++ /dev/null @@ -1,129 +0,0 @@ -/* - This file is part of libhttpserver - Copyright (C) 2011, 2012, 2013, 2014 Sebastiano Merlino - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 - USA -*/ - -#include "Test.hpp" -#include -#include -#include -#include -#include -#include -#include -using namespace std; - -webserver* ws_ptr; - -/* -void signal_callback_handler(int signum) -{ - cout << "bye!" << endl; - ws_ptr->stop(); -} -*/ -Test::Test() : http_resource() -{ -} - -Test2::Test2() : http_resource() -{ -} - -void Test::render_GET(const http_request& r, http_response** res) -{ -/* cout << r.get_version() << endl; - cout << r.get_requestor() << endl; - cout << r.get_requestor_port() << endl; - cout << "PROVA: " << r.get_arg("prova") << endl; - cout << "ALTRO: " << r.get_arg("altro") << endl; - cout << "THUMB: " << r.get_arg("thumbId") << endl; - cout << "COOKIE: " << r.get_cookie("auth") << endl; - std::map head; - r.get_headers(head); - for(std::map::const_iterator it = head.begin(); it != head.end(); ++it) - cout << (*it).first << "-> " << (*it).second << endl; - string pp = r.get_arg("prova"); */ - -/* - cout << r.get_querystring() << endl; - *res = new http_file_response("/home/etr/progs/libhttpserver/test/noimg.png", 200, "image/png"); -*/ - - std::vector topics; - topics.push_back("prova"); - *res = new long_polling_receive_response("", 200, "", topics, false, 10, "keepalive\n"); -} - -void Test::render_POST(const http_request& r, http_response** res) -{ -/* fstream filestr; - filestr.open("test.txt", fstream::out | fstream::app); - filestr << r.get_content() << endl; - filestr.close(); - cout << "DOPO" << endl; - vector vv = r.get_path_pieces(); - for(int i = 0; i < vv.size(); i++) - { - cout << vv[i] << endl; - } - return http_string_response("OK",200);*/ - - /* - http_string_response* s = new http_string_response("OK",100); - s->set_header(http_utils::http_header_location, "B"); - s->set_cookie("Ciccio", "Puppo"); - s->set_cookie("Peppe", "Puppo"); - cout << s->get_cookie("Ciccio") << endl; - *res = s; - */ - - *res = new long_polling_send_response("hi!!!!\n", "prova"); -} - -void Test2::render_GET(const http_request& r, http_response** res) -{ - cout << "D2" << endl; - typedef std::map c_type; - c_type c; - r.get_cookies(c); - for(c_type::const_iterator it = c.begin(); it != c.end(); ++it) - cout << (*it).first << " -> " << (*it).second << endl; - *res = new http_string_response("{\" var1 \" : \" "+r.get_arg("var1")+" \", \" var2 \" : \" "+r.get_arg("var2")+" \", \" var3 \" : \" "+r.get_arg("var3")+" \"}", 200); -} - -void Test::render_PUT(const http_request& r, http_response** res) -{ - *res = new http_string_response(r.get_content(), 200); -} - -int main() -{ -// signal(SIGINT, &signal_callback_handler); - webserver ws(create_webserver(8080)/*.max_threads(5)*/); - ws_ptr = &ws; - Test dt = Test(); - Test2 dt2 = Test2(); - ws.register_resource(string("base/{var1}/{var2}/drop_test/{var3}/tail"), &dt2, true); - ws.register_resource(string("other/side/{thumbId|[0-9]*}"), &dt, true); - ws.register_resource(string("another/{thumbId|[0-9]*}"), &dt, true); - ws.register_resource(string("edge/thumbnail"), &dt, true); - ws.start(true); - return 0; -} diff --git a/examples/Test.hpp b/examples/Test.hpp deleted file mode 100644 index a5f37131..00000000 --- a/examples/Test.hpp +++ /dev/null @@ -1,41 +0,0 @@ -/* - This file is part of libhttpserver - Copyright (C) 2011, 2012, 2013, 2014 Sebastiano Merlino - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 - USA -*/ - -#ifndef _TEST_HPP_ -#define _TEST_HPP_ -#include - -using namespace httpserver; - -class Test : public http_resource { - public: - Test(); - void render_GET(const http_request&, http_response**); - void render_PUT(const http_request&, http_response**); - void render_POST(const http_request&, http_response**); -}; - -class Test2 : public http_resource { - public: - Test2(); - void render_GET(const http_request&, http_response**); -}; - -#endif diff --git a/examples/hello_world.cpp b/examples/hello_world.cpp index 96fcd844..be091578 100755 --- a/examples/hello_world.cpp +++ b/examples/hello_world.cpp @@ -42,7 +42,7 @@ void hello_world_resource::render(const http_request& req, http_response** res) //it is possible to send a response initializing an http_string_response //that reads the content to send in response from a string. - *res = new http_string_response("Hello World!!!", 200); + *res = new http_response(http_response_builder("Hello World!!!", 200).string_response()); } int main() diff --git a/src/Makefile.am b/src/Makefile.am index af169fee..c7681d3f 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -21,7 +21,7 @@ METASOURCES = AUTO lib_LTLIBRARIES = libhttpserver.la libhttpserver_la_SOURCES = string_utilities.cpp webserver.cpp http_utils.cpp http_request.cpp http_response.cpp http_resource.cpp details/comet_manager.cpp details/http_endpoint.cpp noinst_HEADERS = httpserver/string_utilities.hpp httpserver/details/modded_request.hpp httpserver/details/http_response_ptr.hpp httpserver/details/cache_entry.hpp httpserver/details/comet_manager.hpp gettext.h -nobase_include_HEADERS = httpserver.hpp httpserver/create_webserver.hpp httpserver/webserver.hpp httpserver/http_utils.hpp httpserver/details/http_endpoint.hpp httpserver/http_request.hpp httpserver/http_response.hpp httpserver/http_resource.hpp httpserver/binders.hpp httpserver/event_supplier.hpp httpserver/details/event_tuple.hpp httpserver/details/http_resource_mirror.hpp +nobase_include_HEADERS = httpserver.hpp httpserver/create_webserver.hpp httpserver/webserver.hpp httpserver/http_utils.hpp httpserver/details/http_endpoint.hpp httpserver/http_request.hpp httpserver/http_response.hpp httpserver/http_resource.hpp httpserver/binders.hpp httpserver/event_supplier.hpp httpserver/details/event_tuple.hpp httpserver/details/http_resource_mirror.hpp httpserver/http_response_builder.hpp AM_CXXFLAGS += -fPIC -Wall libhttpserver_la_LIBADD = -lmicrohttpd libhttpserver_la_LDFLAGS = diff --git a/src/http_response.cpp b/src/http_response.cpp index 5a5a008e..d2eaf1be 100644 --- a/src/http_response.cpp +++ b/src/http_response.cpp @@ -29,12 +29,43 @@ #include "details/event_tuple.hpp" #include "webserver.hpp" #include "http_response.hpp" +#include "http_response_builder.hpp" using namespace std; namespace httpserver { +http_response::http_response(const http_response_builder& builder): + content(builder._content_hook), + response_code(builder._response_code), + autodelete(builder._autodelete), + realm(builder._realm), + opaque(builder._opaque), + reload_nonce(builder._reload_nonce), + fp(-1), + filename(builder._content_hook), + headers(builder._headers), + footers(builder._footers), + cookies(builder._cookies), + topics(builder._topics), + keepalive_secs(builder._keepalive_secs), + keepalive_msg(builder._keepalive_msg), + send_topic(builder._send_topic), + underlying_connection(0x0), + ca(0x0), + closure_data(0x0), + ce(builder._ce), + cycle_callback(builder._cycle_callback), + get_raw_response(this, builder._get_raw_response), + decorate_response(this, builder._decorate_response), + enqueue_response(this, builder._enqueue_response), + completed(false), + ws(0x0), + connection_id(0x0) +{ +} + http_response::~http_response() { if(ce != 0x0) @@ -60,27 +91,6 @@ size_t http_response::get_cookies(std::map(cls)->cycle_callback(buf); + int val = static_cast(cls)->cycle_callback(buf); if(val == -1) - static_cast(cls)->completed = true; + static_cast(cls)->completed = true; return val; } @@ -240,7 +250,7 @@ void http_response::get_raw_response_lp_receive( )->client_addr; *response = MHD_create_response_from_callback(MHD_SIZE_UNKNOWN, 80, - &long_polling_receive_response::data_generator, (void*) this, NULL); + &http_response::data_generator, (void*) this, NULL); ws->register_to_topics( topics, @@ -250,15 +260,14 @@ void http_response::get_raw_response_lp_receive( ); } -ssize_t long_polling_receive_response::data_generator( +ssize_t http_response::data_generator( void* cls, uint64_t pos, char* buf, size_t max ) { - long_polling_receive_response* _this = - static_cast(cls); + http_response* _this = static_cast(cls); if(_this->ws->pop_signaled(_this->connection_id)) { diff --git a/src/httpserver.hpp b/src/httpserver.hpp index 6c5d86b9..2149d70a 100644 --- a/src/httpserver.hpp +++ b/src/httpserver.hpp @@ -27,6 +27,7 @@ #include "httpserver/details/http_endpoint.hpp" #include "httpserver/http_resource.hpp" #include "httpserver/http_response.hpp" +#include "httpserver/http_response_builder.hpp" #include "httpserver/http_request.hpp" #include "httpserver/event_supplier.hpp" #include "httpserver/details/event_tuple.hpp" diff --git a/src/httpserver/details/cache_entry.hpp b/src/httpserver/details/cache_entry.hpp index 1e1f2442..b8186bb2 100644 --- a/src/httpserver/details/cache_entry.hpp +++ b/src/httpserver/details/cache_entry.hpp @@ -27,7 +27,8 @@ #include #include -#include "details/http_response_ptr.hpp" +#include "httpserver/details/http_response_ptr.hpp" +#include "httpserver/http_response.hpp" namespace httpserver { diff --git a/src/httpserver/details/http_response_ptr.hpp b/src/httpserver/details/http_response_ptr.hpp index 642bc362..62d3f72a 100644 --- a/src/httpserver/details/http_response_ptr.hpp +++ b/src/httpserver/details/http_response_ptr.hpp @@ -25,6 +25,8 @@ #ifndef _HTTP_RESPONSE_PTR_HPP_ #define _HTTP_RESPONSE_PTR_HPP_ +#include "httpserver/http_response.hpp" + namespace httpserver { @@ -60,7 +62,7 @@ struct http_response_ptr { if((*num_references) == 0) { - if(res && res->autodelete) + if(res && res->is_autodelete()) { delete res; res = 0x0; diff --git a/src/httpserver/http_response.hpp b/src/httpserver/http_response.hpp index 55821cbc..a8854116 100644 --- a/src/httpserver/http_response.hpp +++ b/src/httpserver/http_response.hpp @@ -36,6 +36,7 @@ namespace httpserver { class webserver; +class http_response_builder; namespace http { @@ -60,57 +61,7 @@ class bad_caching_attempt: public std::exception } }; -class http_file_response; -class http_basic_auth_fail_response; -class http_digest_auth_fail_response; -class switch_protocol_response; -class long_polling_receive_response; -class long_polling_send_response; -class cache_response; -class deferred_response; - -#define SPECIALIZE_RESPONSE_FOR(TYPE, S1, S2, S3) \ -http_response \ -( \ - const TYPE* response_type, \ - const std::string& content, \ - int response_code,\ - const std::string& content_type,\ - bool autodelete,\ - const std::string& realm,\ - const std::string& opaque,\ - bool reload_nonce,\ - const std::vector& topics,\ - int keepalive_secs,\ - const std::string keepalive_msg,\ - const std::string send_topic,\ - details::cache_entry* ce\ -):\ - content(content),\ - response_code(response_code),\ - autodelete(autodelete),\ - realm(realm),\ - opaque(opaque),\ - reload_nonce(reload_nonce),\ - fp(-1),\ - filename(content),\ - topics(topics),\ - keepalive_secs(keepalive_secs),\ - keepalive_msg(keepalive_msg),\ - send_topic(send_topic),\ - underlying_connection(0x0),\ - ca(0x0),\ - closure_data(0x0),\ - ce(ce),\ - get_raw_response(this, &http_response::get_raw_response_## S1),\ - decorate_response(this, &http_response::decorate_response_## S2),\ - enqueue_response(this, &http_response::enqueue_response_## S3),\ - completed(false),\ - ws(0x0),\ - connection_id(0x0)\ -{\ - set_header(http_utils::http_header_content_type, content_type);\ -} +typedef ssize_t(*cycle_callback_ptr)(const std::string&); /** * Class representing an abstraction for an Http Response. It is used from classes using these apis to send information through http protocol. @@ -118,63 +69,7 @@ http_response \ class http_response { public: - /** - * Constructor used to build an http_response with a content and a response_code - * @param content The content to set for the request. (if the response_type is FILE_CONTENT, it represents the path to the file to read from). - * @param response_code The response code to set for the request. - * @param response_type param indicating if the content have to be read from a string or from a file - **/ - template - http_response - ( - const TYPE* t, - const std::string& content = "", - int response_code = 200, - const std::string& content_type = "text/plain", - bool autodelete = true, - const std::string& realm = "", - const std::string& opaque = "", - bool reload_nonce = false, - const std::vector& topics = std::vector(), - int keepalive_secs = -1, - const std::string keepalive_msg = "", - const std::string send_topic = "", - details::cache_entry* ce = 0x0 - ): - content(content), - response_code(response_code), - autodelete(autodelete), - realm(realm), - opaque(opaque), - reload_nonce(reload_nonce), - fp(-1), - filename(content), - topics(topics), - keepalive_secs(keepalive_secs), - keepalive_msg(keepalive_msg), - send_topic(send_topic), - underlying_connection(0x0), - ca(0x0), - closure_data(0x0), - ce(ce), - get_raw_response(this, &http_response::get_raw_response_str), - decorate_response(this, &http_response::decorate_response_str), - enqueue_response(this, &http_response::enqueue_response_str), - completed(false), - ws(0x0), - connection_id(0x0) - { - set_header(http_utils::http_header_content_type, content_type); - } - - SPECIALIZE_RESPONSE_FOR(http_file_response, file, str, str); - SPECIALIZE_RESPONSE_FOR(http_basic_auth_fail_response, str, str, basic); - SPECIALIZE_RESPONSE_FOR(http_digest_auth_fail_response, str, str, digest); - SPECIALIZE_RESPONSE_FOR(switch_protocol_response, switch_r, str, str); - SPECIALIZE_RESPONSE_FOR(long_polling_receive_response, lp_receive, str, str); - SPECIALIZE_RESPONSE_FOR(long_polling_send_response, lp_send, str, str); - SPECIALIZE_RESPONSE_FOR(cache_response, cache, cache, str); - SPECIALIZE_RESPONSE_FOR(deferred_response, deferred, deferred, str); + http_response(const http_response_builder& builder); /** * Copy constructor @@ -200,6 +95,7 @@ class http_response ca(0x0), closure_data(0x0), ce(b.ce), + cycle_callback(b.cycle_callback), get_raw_response(b.get_raw_response), decorate_response(b.decorate_response), enqueue_response(b.enqueue_response), @@ -218,26 +114,12 @@ class http_response { return this->content; } + void get_content(std::string& result) { result = this->content; } - /** - * Method used to set the content of the response - * @param content The content to set - **/ - void set_content(const std::string& content) - { - this->content = content; - } - void grow_content(const std::string& content) - { - this->content.append(content); - } - void grow_content(const char* content, size_t size) - { - this->content.append(content, size); - } + /** * Method used to get a specified header defined for the response * @param key The header identification @@ -247,10 +129,12 @@ class http_response { return this->headers[key]; } + void get_header(const std::string& key, std::string& result) { result = this->headers[key]; } + /** * Method used to get a specified footer defined for the response * @param key The footer identification @@ -260,64 +144,22 @@ class http_response { return this->footers[key]; } + void get_footer(const std::string& key, std::string& result) { result = this->footers[key]; } + const std::string get_cookie(const std::string& key) { return this->cookies[key]; } + void get_cookie(const std::string& key, std::string& result) { result = this->cookies[key]; } - /** - * Method used to set an header value by key. - * @param key The name identifying the header - * @param value The value assumed by the header - **/ - void set_header(const std::string& key, const std::string& value) - { - this->headers[key] = value; - } - /** - * Method used to set a footer value by key. - * @param key The name identifying the footer - * @param value The value assumed by the footer - **/ - void set_footer(const std::string& key, const std::string& value) - { - this->footers[key] = value; - } - void set_cookie(const std::string& key, const std::string& value) - { - this->cookies[key] = value; - } - /** - * Method used to set the content type for the request. This is a shortcut of setting the corresponding header. - * @param content_type the content type to use for the request - **/ - void set_content_type(const std::string& content_type) - { - this->headers[http_utils::http_header_content_type] = content_type; - } - /** - * Method used to remove previously defined header - * @param key The header to remove - **/ - void remove_header(const std::string& key) - { - this->headers.erase(key); - } - void remove_footer(const std::string& key) - { - this->footers.erase(key); - } - void remove_cookie(const std::string& key) - { - this->cookies.erase(key); - } + /** * Method used to get all headers passed with the request. * @return a map containing all headers. @@ -325,6 +167,7 @@ class http_response size_t get_headers( std::map& result ) const; + /** * Method used to get all footers passed with the request. * @return a map containing all footers. @@ -332,43 +175,11 @@ class http_response size_t get_footers( std::map& result ) const; + size_t get_cookies( std::map& result ) const; - /** - * Method used to set all headers of the response. - * @param headers The headers key-value map to set for the response. - **/ - void set_headers(const std::map& headers) - { - std::map::const_iterator it; - for(it = headers.begin(); it != headers.end(); ++it) - this->headers[it->first] = it->second; - } - /** - * Method used to set all footers of the response. - * @param footers The footers key-value map to set for the response. - **/ - void set_footers(const std::map& footers) - { - std::map::const_iterator it; - for(it = footers.begin(); it != footers.end(); ++it) - this->footers[it->first] = it->second; - } - void set_cookies(const std::map& cookies) - { - std::map::const_iterator it; - for(it = cookies.begin(); it != cookies.end(); ++it) - this->cookies[it->first] = it->second; - } - /** - * Method used to set the response code of the response - * @param response_code the response code to set - **/ - void set_response_code(int response_code) - { - this->response_code = response_code; - } + /** * Method used to get the response code from the response * @return The response code @@ -377,30 +188,42 @@ class http_response { return this->response_code; } + const std::string get_realm() const { return this->realm; } + void get_realm(std::string& result) const { result = this->realm; } + const std::string get_opaque() const { return this->opaque; } + void get_opaque(std::string& result) const { result = this->opaque; } + const bool need_nonce_reload() const { return this->reload_nonce; } + int get_switch_callback() const { return 0; } + + bool is_autodelete() const + { + return autodelete; + } + size_t get_topics(std::vector& topics) const { typedef std::vector::const_iterator topics_it; @@ -408,20 +231,12 @@ class http_response topics.push_back(*it); return topics.size(); } - void set_closure_action(void(*ca)(void*), void* closure_data = 0x0) - { - this->ca = ca; - this->closure_data = closure_data; - } protected: - typedef details::binders::functor_two get_raw_response_t; + typedef details::binders::functor_two get_raw_response_t; - typedef details::binders::functor_one decorate_response_t; + typedef details::binders::functor_one decorate_response_t; - typedef details::binders::functor_two enqueue_response_t; + typedef details::binders::functor_two enqueue_response_t; std::string content; int response_code; @@ -442,6 +257,7 @@ class http_response void(*ca)(void*); void* closure_data; details::cache_entry* ce; + cycle_callback_ptr cycle_callback; const get_raw_response_t get_raw_response; const decorate_response_t decorate_response; @@ -477,237 +293,13 @@ class http_response friend class webserver; friend struct details::http_response_ptr; - - friend void clone_response(const http_response& hr, - http_response** dhr - ); - - friend class cache_response; - friend class deferred_response; + friend class http_response_builder; + friend void clone_response(const http_response& hr, http_response** dhr); + friend ssize_t details::cb(void* cls, uint64_t pos, char* buf, size_t max); private: http_response& operator=(const http_response& b); -}; - -class http_string_response : public http_response -{ - public: - http_string_response - ( - const std::string& content, - int response_code, - const std::string& content_type = "text/plain", - bool autodelete = true - ): http_response(this, content, response_code, content_type, autodelete) - { - } - - http_string_response(const http_response& b) : http_response(b) { } - private: - friend class webserver; -}; - -class http_byte_response : public http_response -{ - public: - http_byte_response - ( - const char* content, - size_t content_length, - int response_code, - const std::string& content_type = "text/plain", - bool autodelete = true - ): http_response( - this, std::string(content, content_length), - response_code, content_type, autodelete) - { - } - private: - friend class webserver; -}; - -class http_file_response : public http_response -{ - public: - http_file_response - ( - const std::string& filename, - int response_code, - const std::string& content_type = "text/plain", - bool autodelete = true - ) : http_response(this,filename,response_code,content_type,autodelete) - { - } - - http_file_response(const http_response& b) : http_response(b) { } - private: - friend class webserver; -}; - -class http_basic_auth_fail_response : public http_response -{ - public: - http_basic_auth_fail_response - ( - const std::string& content, - int response_code, - const std::string& content_type = "text/plain", - bool autodelete = true, - const std::string& realm = "" - ) : http_response(this, content, response_code, - content_type, autodelete, realm) - { - } - - http_basic_auth_fail_response(const http_response& b) : - http_response(b) { } - private: - friend class webserver; -}; - -class http_digest_auth_fail_response : public http_response -{ - public: - http_digest_auth_fail_response - ( - const std::string& content, - int response_code, - const std::string& content_type = "text/plain", - bool autodelete = true, - const std::string& realm = "", - const std::string& opaque = "", - bool reload_nonce = false - ) : http_response(this, content, response_code, - content_type, autodelete, realm, opaque, reload_nonce) - { - } - - http_digest_auth_fail_response(const http_response& b) : - http_response(b) { } - private: - friend class webserver; -}; - -class shoutCAST_response : public http_response -{ - public: - shoutCAST_response - ( - const std::string& content, - int response_code, - const std::string& content_type = "text/plain", - bool autodelete = true - ); - shoutCAST_response(const http_response& b) : http_response(b) { } - private: - friend class webserver; -}; - -class switch_protocol_response : public http_response -{ - public: - switch_protocol_response() : http_response(this) - { - } - - switch_protocol_response(const http_response& b) : http_response(b) - { - } - private: - friend class webserver; -}; - -class long_polling_receive_response : public http_response -{ - public: - long_polling_receive_response - ( - const std::string& content, - int response_code, - const std::string& content_type, - const std::vector& topics, - bool autodelete = true, - int keepalive_secs = -1, - std::string keepalive_msg = "" - ) : http_response(this, content, response_code, content_type, - autodelete, "", "", false, topics, keepalive_secs, keepalive_msg - ) - { - } - - long_polling_receive_response(const http_response& b) : http_response(b) - { - } - - static ssize_t data_generator (void* cls, uint64_t pos, - char* buf, size_t max - ); - - friend class webserver; -}; - -class long_polling_send_response : public http_response -{ - public: - long_polling_send_response - ( - const std::string& content, - const std::string& topic, - bool autodelete = true - ) : http_response(this, content, 200, "", autodelete, "", "", false, - std::vector(), -1, "", topic - ) - { - } - - long_polling_send_response(const http_response& b) : http_response(b) - { - } - private: - friend class webserver; -}; - -class cache_response : public http_response -{ - public: - cache_response - ( - const std::string& key - ) : http_response(this, key, 200, "", true, "", "", false, - std::vector(), -1, "", "", 0x0 - ) - { - } - cache_response - ( - details::cache_entry* ce - ) : http_response(this, "", 200, "", true, "", "", false, - std::vector(), -1, "", "", ce - ) - { - if(ce == 0x0) - throw bad_caching_attempt(); - } - - cache_response(const http_response& b) : http_response(b) { } - ~cache_response(); - protected: - friend class webserver; -}; - -class deferred_response : public http_response -{ - public: - deferred_response - ( - ) : http_response(this) - { - } - deferred_response(const http_response& b) : http_response(b) { } - virtual ssize_t cycle_callback(const std::string& buf); - private: - friend class webserver; - friend ssize_t details::cb(void*, uint64_t, char*, size_t); + static ssize_t data_generator (void* cls, uint64_t pos, char* buf, size_t max); }; }; diff --git a/src/httpserver/http_response_builder.hpp b/src/httpserver/http_response_builder.hpp new file mode 100644 index 00000000..f1fa12ce --- /dev/null +++ b/src/httpserver/http_response_builder.hpp @@ -0,0 +1,298 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011, 2012, 2013, 2014 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#if !defined (_HTTPSERVER_HPP_INSIDE_) && !defined (HTTPSERVER_COMPILATION) +#error "Only or can be included directly." +#endif + +#ifndef _HTTP_RESPONSE_BUILDER_HPP_ +#define _HTTP_RESPONSE_BUILDER_HPP_ +#include +#include +#include "httpserver/http_response.hpp" + +struct MHD_Connection; + +namespace httpserver +{ + +class webserver; + +namespace http +{ + class header_comparator; +}; + +namespace details +{ + struct cache_entry; +}; + +using namespace http; + +struct byte_string +{ + public: + byte_string(const char* content_hook, size_t content_length): + content_hook(content_hook), + content_length(content_length) + { + } + + const char* get_content_hook() const + { + return content_hook; + } + + size_t get_content_length() const + { + return content_length; + } + + private: + const char* content_hook; + size_t content_length; +}; + +class http_response_builder +{ + public: + explicit http_response_builder( + const std::string& content_hook, + int response_code = 200, + const std::string& content_type = "text/plain", + bool autodelete = true + ): + _content_hook(content_hook), + _response_code(response_code), + _autodelete(autodelete), + _realm(""), + _opaque(""), + _reload_nonce(false), + _headers(std::map()), + _footers(std::map()), + _cookies(std::map()), + _topics(std::vector()), + _keepalive_secs(-1), + _keepalive_msg(""), + _send_topic(""), + _ce(0x0), + _get_raw_response(&http_response::get_raw_response_str), + _decorate_response(&http_response::decorate_response_str), + _enqueue_response(&http_response::enqueue_response_str) + { + _headers[http_utils::http_header_content_type] = content_type; + } + + http_response_builder( + const byte_string& content_hook, + int response_code = 200, + const std::string& content_type = "text/plain", + bool autodelete = true + ): + _content_hook(std::string(content_hook.get_content_hook(), content_hook.get_content_length())), + _response_code(response_code), + _autodelete(autodelete), + _realm(""), + _opaque(""), + _reload_nonce(false), + _headers(std::map()), + _footers(std::map()), + _cookies(std::map()), + _topics(std::vector()), + _keepalive_secs(-1), + _keepalive_msg(""), + _send_topic(""), + _ce(0x0), + _get_raw_response(&http_response::get_raw_response_str), + _decorate_response(&http_response::decorate_response_str), + _enqueue_response(&http_response::enqueue_response_str) + { + _headers[http_utils::http_header_content_type] = content_type; + } + + http_response_builder(const http_response_builder& b): + _content_hook(b._content_hook), + _response_code(b._response_code), + _autodelete(b._autodelete), + _realm(b._realm), + _opaque(b._opaque), + _reload_nonce(b._reload_nonce), + _headers(b._headers), + _footers(b._footers), + _cookies(b._cookies), + _topics(b._topics), + _keepalive_secs(b._keepalive_secs), + _keepalive_msg(b._keepalive_msg), + _send_topic(b._send_topic), + _ce(b._ce), + _get_raw_response(b._get_raw_response), + _decorate_response(b._decorate_response), + _enqueue_response(b._enqueue_response) + { + } + + http_response_builder& operator=(const http_response_builder& b) + { + _content_hook = b._content_hook; + _response_code = b._response_code; + _autodelete = b._autodelete; + _realm = b._realm; + _opaque = b._opaque; + _reload_nonce = b._reload_nonce; + _headers = b._headers; + _footers = b._footers; + _cookies = b._cookies; + _topics = b._topics; + _keepalive_secs = b._keepalive_secs; + _keepalive_msg = b._keepalive_msg; + _send_topic = b._send_topic; + _ce = b._ce; + _get_raw_response = b._get_raw_response; + _decorate_response = b._decorate_response; + _enqueue_response = b._enqueue_response; + return *this; + } + + ~http_response_builder() + { + } + + http_response_builder& string_response() + { + return *this; + } + + http_response_builder& file_response() + { + _get_raw_response = &http_response::get_raw_response_file; + return *this; + } + + http_response_builder& basic_auth_fail_response(const std::string& realm = "") + { + _realm = realm; + _enqueue_response = &http_response::enqueue_response_basic; + return *this; + } + + http_response_builder& digest_auth_fail_response( + const std::string& realm = "", + const std::string& opaque = "", + bool reload_nonce = false + ) + { + _realm = realm; + _opaque = opaque; + _reload_nonce = reload_nonce; + _enqueue_response = &http_response::enqueue_response_digest; + return *this; + } + + http_response_builder& long_polling_receive_response( + const std::vector& topics, + int keepalive_secs = -1, + std::string keepalive_msg = "" + ) + { + _topics = topics; + _keepalive_secs = keepalive_secs; + _keepalive_msg = keepalive_msg; + _get_raw_response = &http_response::get_raw_response_lp_receive; + return *this; + } + + http_response_builder& long_polling_send_response(const std::string& send_topic) + { + _send_topic = send_topic; + _get_raw_response = &http_response::get_raw_response_lp_send; + return *this; + } + + http_response_builder& cache_response() + { + _get_raw_response = &http_response::get_raw_response_cache; + _decorate_response = &http_response::decorate_response_cache; + return *this; + } + + http_response_builder& deferred_response(cycle_callback_ptr cycle_callback) + { + _cycle_callback = cycle_callback; + _get_raw_response = &http_response::get_raw_response_deferred; + _decorate_response = &http_response::decorate_response_deferred; + return *this; + } + + http_response_builder& shoutCAST_response() + { + _response_code |= http_utils::shoutcast_response; + return *this; + } + + http_response_builder& with_header(const std::string& key, const std::string& value) + { + _headers[key] = value; return *this; + } + + http_response_builder& with_footer(const std::string& key, const std::string& value) + { + _footers[key] = value; return *this; + } + + http_response_builder& with_cookie(const std::string& key, const std::string& value) + { + _cookies[key] = value; return *this; + } + + private: + std::string _content_hook; + int _response_code; + bool _autodelete; + std::string _realm; + std::string _opaque; + bool _reload_nonce; + std::map _headers; + std::map _footers; + std::map _cookies; + std::vector _topics; + int _keepalive_secs; + std::string _keepalive_msg; + std::string _send_topic; + cycle_callback_ptr _cycle_callback; + details::cache_entry* _ce; + + void (http_response::*_get_raw_response)(MHD_Response**, webserver*); + void (http_response::*_decorate_response)(MHD_Response*); + int (http_response::*_enqueue_response)(MHD_Connection*, MHD_Response*); + + http_response_builder& cache_response(details::cache_entry* ce) + { + _ce = ce; + _get_raw_response = &http_response::get_raw_response_cache; + _decorate_response = &http_response::decorate_response_cache; + return *this; + } + + friend struct http_response; +}; + +}; +#endif //_HTTP_RESPONSE_BUILDER_HPP_ diff --git a/src/webserver.cpp b/src/webserver.cpp index 51dac3ec..669338fa 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -39,6 +39,7 @@ #include "http_resource.hpp" #include "http_response.hpp" #include "http_request.hpp" +#include "http_response_builder.hpp" #include "details/http_endpoint.hpp" #include "string_utilities.hpp" #include "details/http_resource_mirror.hpp" @@ -80,12 +81,12 @@ struct daemon_item void empty_render(const http_request& r, http_response** res) { - *res = new http_string_response("", 200); + *res = new http_response(http_response_builder("", 200).string_response()); } void empty_not_acceptable_render(const http_request& r, http_response** res) { - *res = new http_string_response(NOT_METHOD_ERROR, 200); + *res = new http_response(http_response_builder(NOT_METHOD_ERROR, 200).string_response()); } bool empty_is_allowed(const std::string& method) @@ -823,10 +824,7 @@ void webserver::not_found_page( if(not_found_resource != 0x0) not_found_resource(*mr->dhr, dhrs); else - *dhrs = new http_string_response( - NOT_FOUND_ERROR, - http_utils::http_not_found - ); + *dhrs = new http_response(http_response_builder(NOT_FOUND_ERROR, http_utils::http_not_found).string_response()); } int webserver::method_not_acceptable_page (const void *cls, @@ -858,10 +856,7 @@ void webserver::method_not_allowed_page( if(method_not_acceptable_resource != 0x0) method_not_allowed_resource(*mr->dhr, dhrs); else - *dhrs = new http_string_response( - METHOD_ERROR, - http_utils::http_method_not_allowed - ); + *dhrs = new http_response(http_response_builder(METHOD_ERROR, http_utils::http_method_not_allowed).string_response()); } void webserver::internal_error_page( @@ -873,10 +868,7 @@ void webserver::internal_error_page( if(internal_error_resource != 0x0 && !force_our) internal_error_resource(*mr->dhr, dhrs); else - *dhrs = new http_string_response( - GENERIC_ERROR, - http_utils::http_internal_server_error - ); + *dhrs = new http_response(http_response_builder(GENERIC_ERROR, http_utils::http_internal_server_error).string_response()); } int webserver::bodyless_requests_answer( diff --git a/test/integ/basic.cpp b/test/integ/basic.cpp index bb162f16..c61c7c0c 100644 --- a/test/integ/basic.cpp +++ b/test/integ/basic.cpp @@ -50,12 +50,12 @@ class simple_resource : public http_resource public: void render_GET(const http_request& req, http_response** res) { - *res = new http_string_response("OK", 200, "text/plain"); + *res = new http_response(http_response_builder("OK", 200, "text/plain").string_response()); } void render_POST(const http_request& req, http_response** res) { - *res = new http_string_response( - req.get_arg("arg1")+req.get_arg("arg2"), 200, "text/plain" + *res = new http_response( + http_response_builder(req.get_arg("arg1")+req.get_arg("arg2"), 200, "text/plain").string_response() ); } }; @@ -65,7 +65,7 @@ class long_content_resource : public http_resource public: void render_GET(const http_request& req, http_response** res) { - *res = new http_string_response(lorem_ipsum, 200, "text/plain"); + *res = new http_response(http_response_builder(lorem_ipsum, 200, "text/plain").string_response()); } }; @@ -74,8 +74,9 @@ class header_test_resource : public http_resource public: void render_GET(const http_request& req, http_response** res) { - *res = new http_string_response("OK", 200, "text/plain"); - (*res)->set_header("KEY", "VALUE"); + http_response_builder hrb("OK", 200, "text/plain"); + hrb.with_header("KEY", "VALUE"); + *res = new http_response(hrb.string_response()); } }; @@ -84,23 +85,23 @@ class complete_test_resource : public http_resource public: void render_GET(const http_request& req, http_response** res) { - *res = new http_string_response("OK", 200, "text/plain"); + *res = new http_response(http_response_builder("OK", 200, "text/plain").string_response()); } void render_POST(const http_request& req, http_response** res) { - *res = new http_string_response("OK", 200, "text/plain"); + *res = new http_response(http_response_builder("OK", 200, "text/plain").string_response()); } void render_PUT(const http_request& req, http_response** res) { - *res = new http_string_response("OK", 200, "text/plain"); + *res = new http_response(http_response_builder("OK", 200, "text/plain").string_response()); } void render_DELETE(const http_request& req, http_response** res) { - *res = new http_string_response("OK", 200, "text/plain"); + *res = new http_response(http_response_builder("OK", 200, "text/plain").string_response()); } void render_CONNECT(const http_request& req, http_response** res) { - *res = new http_string_response("OK", 200, "text/plain"); + *res = new http_response(http_response_builder("OK", 200, "text/plain").string_response()); } }; @@ -109,7 +110,7 @@ class only_render_resource : public http_resource public: void render(const http_request& req, http_response** res) { - *res = new http_string_response("OK", 200, "text/plain"); + *res = new http_response(http_response_builder("OK", 200, "text/plain").string_response()); } }; @@ -118,7 +119,7 @@ class ok_resource : public http_resource public: void render_GET(const http_request& req, http_response** res) { - *res = new http_string_response("OK", 200, "text/plain"); + *res = new http_response(http_response_builder("OK", 200, "text/plain").string_response()); } }; @@ -127,7 +128,7 @@ class nok_resource : public http_resource public: void render_GET(const http_request& req, http_response** res) { - *res = new http_string_response("NOK", 200, "text/plain"); + *res = new http_response(http_response_builder("NOK", 200, "text/plain").string_response()); } }; From 8951cee059258a75987bc3e549b7753d87d25972 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 11 May 2014 23:41:14 +0100 Subject: [PATCH 079/623] Avoid warnings from clang --- src/httpserver/http_response_builder.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/httpserver/http_response_builder.hpp b/src/httpserver/http_response_builder.hpp index f1fa12ce..c405e234 100644 --- a/src/httpserver/http_response_builder.hpp +++ b/src/httpserver/http_response_builder.hpp @@ -291,7 +291,7 @@ class http_response_builder return *this; } - friend struct http_response; + friend class http_response; }; }; From 0cf71bf99a62ec6fa4f3f88d4dd59d29ebde4817 Mon Sep 17 00:00:00 2001 From: Shane Peelar Date: Sun, 15 Jun 2014 16:52:29 -0400 Subject: [PATCH 080/623] Added subdir-objects to configure.ac so automake 1.4 doesn't complain about forward compatibility --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 6130202f..bd19a09e 100644 --- a/configure.ac +++ b/configure.ac @@ -26,7 +26,7 @@ m4_define([libhttpserver_REVISION],[2])dnl m4_define([libhttpserver_PKG_VERSION],[libhttpserver_MAJOR_VERSION.libhttpserver_MINOR_VERSION.libhttpserver_REVISION])dnl m4_define([libhttpserver_LDF_VERSION],[libhttpserver_MAJOR_VERSION:libhttpserver_MINOR_VERSION:libhttpserver_REVISION])dnl AC_INIT([libhttpserver], libhttpserver_PKG_VERSION, [electrictwister2000@gmail.com]) -AM_INIT_AUTOMAKE +AM_INIT_AUTOMAKE([subdir-objects]) AC_CONFIG_HEADERS([config.h]) AC_CONFIG_MACRO_DIR([m4]) From a3fb0ecee3cf607acea7902355b8716161ddd325 Mon Sep 17 00:00:00 2001 From: Shane Peelar Date: Mon, 16 Jun 2014 03:59:10 -0400 Subject: [PATCH 081/623] Introduce support for building on MinGW/Cygwin systems This patch modifies the build to properly handle MinGW/Cygwin hosts and adds support for Windows sockets in the source. This was achieved by using MHD_socket as the socket datatype where necessary, which is guaranteed to resolve to SOCKET on windows and int on unix systems, preserving old functionality while adding new. Some internal functions were modified to accomodate the change, namely those that implictly converted sockets to ints (which would have caused problems on x86_64 windows, as those are 8 byte unsigned) All test cases pass with MSYS2/MinGW compiler. --- configure.ac | 19 ++++++++- src/Makefile.am | 2 +- src/details/comet_manager.cpp | 3 +- src/details/http_endpoint.cpp | 6 +-- src/http_response.cpp | 4 +- src/http_utils.cpp | 8 ++++ src/httpserver/create_webserver.hpp | 6 +-- src/httpserver/http_utils.hpp | 10 ++--- src/httpserver/webserver.hpp | 6 ++- src/webserver.cpp | 62 +++++++++++++++++++---------- 10 files changed, 86 insertions(+), 40 deletions(-) diff --git a/configure.ac b/configure.ac index bd19a09e..c3333227 100644 --- a/configure.ac +++ b/configure.ac @@ -53,6 +53,21 @@ if test x"$samedirectory" = x"no"; then fi fi +case "$host" in + *-mingw*) + NETWORK_HEADER="winsock2.h" + REGEX_LIBS="-lregex -no-undefined" + ;; + *-cygwin*) + NETWORK_HEADER="winsock2.h" + REGEX_LIBS="-lregex -no-undefined" + ;; + *) + NETWORK_HEADER="arpa/inet.h" + REGEX_LIBS="" + ;; +esac + # Checks for header files. AC_HEADER_STDC AC_CHECK_HEADER([string],[],[AC_MSG_ERROR("C++ strings not found")]) @@ -73,7 +88,7 @@ AC_CHECK_HEADER([ctype.h],[],[AC_MSG_ERROR("cctype not found")]) AC_CHECK_HEADER([regex.h],[],[AC_MSG_ERROR("regex.h not found")]) AC_CHECK_HEADER([sys/stat.h],[],[AC_MSG_ERROR("sys/stat.h not found")]) AC_CHECK_HEADER([sys/types.h],[],[AC_MSG_ERROR("sys/types.h not found")]) -AC_CHECK_HEADER([arpa/inet.h],[],[AC_MSG_ERROR("arpa/inet.h not found")]) +AC_CHECK_HEADER([$NETWORK_HEADER],[],[AC_MSG_ERROR("$NETWORK_HEADER not found")]) AC_CHECK_HEADER([signal.h],[],[AC_MSG_ERROR("signal.h not found")]) AC_CHECK_HEADER([gnutls/gnutls.h],[have_gnutls="yes"],[AC_MSG_WARN("gnutls/gnutls.h not found. TLS will be disabled"); have_gnutls="no"]) @@ -84,7 +99,7 @@ PKG_CHECK_MODULES([LIBMICROHTTPD],[libmicrohttpd >= 0.9.7],[],[AC_MSG_ERROR("lib #AC_CHECK_LIB([microhttpd],[MHD_start_daemon],[],[AC_MSG_ERROR("Microhttpd header files not found. Please use a version >= 0.9.9.")]) CXXFLAGS="-DHTTPSERVER_COMPILATION -D_REENTRANT $LIBMICROHTTPD_CFLAGS $CXXFLAGS" -LDFLAGS="$LIBMICROHTTPD_LIBS $LD_FLAGS" +LDFLAGS="$LIBMICROHTTPD_LIBS $REGEX_LIBS $LD_FLAGS" m4_pattern_allow([AC_TYPE_SIZE_T]) m4_pattern_allow([AC_TYPE_UINT16_T]) diff --git a/src/Makefile.am b/src/Makefile.am index c7681d3f..c88305f1 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -26,7 +26,7 @@ AM_CXXFLAGS += -fPIC -Wall libhttpserver_la_LIBADD = -lmicrohttpd libhttpserver_la_LDFLAGS = -install-exec-hook: +install-data-hook: (mkdir -p $(DESTDIR)$(includedir) && cd $(DESTDIR)$(includedir) && $(LN_S) -f httpserver.hpp httpserverpp) uninstall-hook: diff --git a/src/details/comet_manager.cpp b/src/details/comet_manager.cpp index c3b42640..d9d13adf 100644 --- a/src/details/comet_manager.cpp +++ b/src/details/comet_manager.cpp @@ -21,6 +21,7 @@ #include #include #include "details/comet_manager.hpp" +#include using namespace std; @@ -140,7 +141,7 @@ size_t comet_manager::get_topic_consumers( { consumers.insert((*it)); } - int size = consumers.size(); + std::set::size_type size = consumers.size(); pthread_rwlock_unlock(&comet_guard); return size; } diff --git a/src/details/http_endpoint.cpp b/src/details/http_endpoint.cpp index 59afa375..1fa7e059 100644 --- a/src/details/http_endpoint.cpp +++ b/src/details/http_endpoint.cpp @@ -99,8 +99,8 @@ http_endpoint::http_endpoint (parts[i][parts[i].size() - 1] == '}') ) { - int bar = parts[i].find_first_of('|'); - if(bar != (int)string::npos) + std::string::size_type bar = parts[i].find_first_of('|'); + if(bar != string::npos) { this->url_pars.push_back(parts[i].substr(1, bar - 1)); if(first) @@ -201,7 +201,7 @@ http_endpoint& http_endpoint::operator =(const http_endpoint& h) bool http_endpoint::operator <(const http_endpoint& b) const { - COMPARATOR(this->url_modded, b.url_modded, toupper); + COMPARATOR(this->url_modded, b.url_modded, std::toupper); } bool http_endpoint::match(const http_endpoint& url) const diff --git a/src/http_response.cpp b/src/http_response.cpp index d2eaf1be..d086e15e 100644 --- a/src/http_response.cpp +++ b/src/http_response.cpp @@ -207,7 +207,7 @@ namespace details ssize_t cb(void* cls, uint64_t pos, char* buf, size_t max) { - int val = static_cast(cls)->cycle_callback(buf); + ssize_t val = static_cast(cls)->cycle_callback(buf); if(val == -1) static_cast(cls)->completed = true; return val; @@ -272,7 +272,7 @@ ssize_t http_response::data_generator( if(_this->ws->pop_signaled(_this->connection_id)) { string message; - int size = _this->ws->read_message(_this->connection_id, message); + size_t size = _this->ws->read_message(_this->connection_id, message); memcpy(buf, message.c_str(), size); return size; } diff --git a/src/http_utils.cpp b/src/http_utils.cpp index 74d00629..96c05df0 100644 --- a/src/http_utils.cpp +++ b/src/http_utils.cpp @@ -21,7 +21,15 @@ #include #include #include +#if defined(__MINGW32__) || defined(__CYGWIN32__) +#define _WINDOWS +#undef _WIN32_WINNT +#define _WIN32_WINNT 0x600 +#include +#include +#else #include +#endif #include #include #include diff --git a/src/httpserver/create_webserver.hpp b/src/httpserver/create_webserver.hpp index 0c6582b3..262a02e4 100644 --- a/src/httpserver/create_webserver.hpp +++ b/src/httpserver/create_webserver.hpp @@ -85,7 +85,7 @@ class create_webserver { } - explicit create_webserver(int port): + explicit create_webserver(uint16_t port): _port(port), _start_method(http::http_utils::INTERNAL_SELECT), _max_threads(0), @@ -125,7 +125,7 @@ class create_webserver { } - create_webserver& port(int port) { _port = port; return *this; } + create_webserver& port(uint16_t port) { _port = port; return *this; } create_webserver& start_method( const http::http_utils::start_method_T& start_method ) @@ -317,7 +317,7 @@ class create_webserver } private: - int _port; + uint16_t _port; http::http_utils::start_method_T _start_method; int _max_threads; int _max_connections; diff --git a/src/httpserver/http_utils.hpp b/src/httpserver/http_utils.hpp index deccc180..9707aca9 100644 --- a/src/httpserver/http_utils.hpp +++ b/src/httpserver/http_utils.hpp @@ -27,7 +27,7 @@ #include #include -#include +#include #include #include #include @@ -238,8 +238,8 @@ class http_utils \ for (size_t n = 0; n < l1; n++)\ {\ - char xc = op((x)[n]);\ - char yc = op((y)[n]);\ + int xc = op((x)[n]);\ + int yc = op((y)[n]);\ if (xc < yc) return true;\ if (xc > yc) return false;\ }\ @@ -255,7 +255,7 @@ class header_comparator { **/ bool operator()(const std::string& x,const std::string& y) const { - COMPARATOR(x, y, toupper); + COMPARATOR(x, y, std::toupper); } }; @@ -274,7 +274,7 @@ class arg_comparator { bool operator()(const std::string& x,const std::string& y) const { #ifdef CASE_INSENSITIVE - COMPARATOR(x, y, toupper); + COMPARATOR(x, y, std::toupper); #else COMPARATOR(x, y, ); #endif diff --git a/src/httpserver/webserver.hpp b/src/httpserver/webserver.hpp index 305cf0d9..55591524 100644 --- a/src/httpserver/webserver.hpp +++ b/src/httpserver/webserver.hpp @@ -188,7 +188,7 @@ class webserver webserver& operator=(const webserver& other); private: - const int port; + const uint16_t port; http::http_utils::start_method_T start_method; const int max_threads; const int max_connections; @@ -200,7 +200,9 @@ class webserver validator_ptr validator; unescaper_ptr unescaper; const struct sockaddr* bind_address; - int bind_socket; + /* Changed type to MHD_socket because this type will always reflect the + platform's actual socket type (e.g. SOCKET on windows, int on unixes)*/ + MHD_socket bind_socket; const int max_thread_stack_size; const bool use_ssl; const bool use_ipv6; diff --git a/src/webserver.cpp b/src/webserver.cpp index 669338fa..50ea3196 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -27,7 +27,15 @@ #include #include #include +#include + +#if defined(__MINGW32__) || defined(__CYGWIN32__) +#include +#define _WINDOWS +#else #include +#endif + #include #include #include @@ -115,12 +123,16 @@ struct compare_value } }; +#ifndef __MINGW32__ static void catcher (int sig) { } +#endif static void ignore_sigpipe () { +//Mingw doesn't implement SIGPIPE +#ifndef __MINGW32__ struct sigaction oldsig; struct sigaction sig; @@ -136,6 +148,7 @@ static void ignore_sigpipe () gettext("Failed to install SIGPIPE handler: %s\n"), strerror (errno) ); +#endif } //WEBSERVER @@ -259,7 +272,7 @@ void* webserver::select(void* self) fd_set es; struct timeval timeout_value; details::daemon_item* di = static_cast(self); - int max; + MHD_socket max; while (di->ws->is_running()) { max = 0; @@ -298,7 +311,7 @@ void* webserver::select(void* self) int local_max; (*it).second.supply_events(&rs, &ws, &es, &local_max); - if(local_max > max) + if((MHD_socket) local_max > max) max = local_max; struct timeval t = (*it).second.get_timeout(); @@ -321,7 +334,9 @@ void* webserver::select(void* self) timeout_value.tv_sec = timeout_secs; timeout_value.tv_usec = timeout_microsecs; - ::select (max + 1, &rs, &ws, &es, &timeout_value); + /*On unix, MHD_socket will be an int anyway. + On windows, the cast is safe because winsock ignores first argument to select*/ + ::select ((int) max + 1, &rs, &ws, &es, &timeout_value); MHD_run (di->daemon); //EVENT SUPPLIERS DISPATCHING @@ -338,24 +353,25 @@ void* webserver::select(void* self) return 0x0; } -int create_socket (int domain, int type, int protocol) +MHD_socket create_socket (int domain, int type, int protocol) { int sock_cloexec = SOCK_CLOEXEC; int ctype = SOCK_STREAM | sock_cloexec; - int fd; - - /* use SOCK_STREAM rather than ai_socktype: some getaddrinfo + + /* use SOCK_STREAM rather than ai_socktype: some getaddrinfo * implementations do not set ai_socktype, e.g. RHL6.2. */ - fd = socket(domain, ctype, protocol); + MHD_socket fd = socket(domain, ctype, protocol); + +#ifdef _WINDOWS + if (fd == INVALID_SOCKET) +#else if ((fd == -1) && (errno == EINVAL || errno == EPROTONOSUPPORT) && (sock_cloexec != 0) ) +#endif { - sock_cloexec = 0; fd = socket(domain, type, protocol); } - if (-1 == fd) - return -1; return fd; } @@ -488,7 +504,7 @@ bool webserver::start(bool blocking) setsockopt (bind_socket, SOL_SOCKET, SO_REUSEADDR, - &on, sizeof (on)); + (const char*) &on, sizeof (on)); if(use_ipv6) { @@ -496,16 +512,21 @@ bool webserver::start(bool blocking) #ifdef IPV6_V6ONLY setsockopt (bind_socket, IPPROTO_IPV6, IPV6_V6ONLY, - &on, sizeof (on) + (const char*) &on, sizeof (on) ); #endif #endif } bind(bind_socket, servaddr, addrlen); } +#ifdef _WINDOWS + unsigned long ioarg = 1; + ioctlsocket(bind_socket, FIONBIO, &ioarg); +#else int flags = fcntl (bind_socket, F_GETFL); flags |= O_NONBLOCK; fcntl (bind_socket, F_SETFL, flags); +#endif if(!bind_settled) listen(bind_socket, 1); iov.push_back(gen(MHD_OPTION_LISTEN_SOCKET, bind_socket)); @@ -730,7 +751,7 @@ int webserver::build_request_args ( mr->dhr->querystring += string(buf); } } - int size = internal_unescaper((void*) mr->ws, value); + size_t size = internal_unescaper((void*) mr->ws, value); mr->dhr->set_arg(key, string(value, size)); return MHD_YES; } @@ -1046,18 +1067,17 @@ int webserver::finalize_answer( details::http_resource_mirror >::iterator it; - int len = -1; - int tot_len = -1; + size_t len = 0; + size_t tot_len = 0; for( it=registered_resources.begin(); it!=registered_resources.end(); ++it ) { - int endpoint_pieces_len = (*it).first.get_url_pieces_num(); - int endpoint_tot_len = (*it).first.get_url_complete_size(); - if(tot_len == -1 || - len == -1 || + size_t endpoint_pieces_len = (*it).first.get_url_pieces_num(); + size_t endpoint_tot_len = (*it).first.get_url_complete_size(); + if(!found || endpoint_pieces_len > len || ( endpoint_pieces_len == len && @@ -1078,7 +1098,7 @@ int webserver::finalize_answer( { vector url_pars; - unsigned int pars_size = + size_t pars_size = found_endpoint->first.get_url_pars(url_pars); vector url_pieces; From 1ac799d3b9ce8019c85c72634f2b13c030402b37 Mon Sep 17 00:00:00 2001 From: Shane Peelar Date: Tue, 17 Jun 2014 20:39:19 -0400 Subject: [PATCH 082/623] Updated Travis to use libmicrohttpd-0.9.37 --- .travis.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index fc067d77..71a01ec0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,9 +5,9 @@ compiler: # Change this to your needs before_install: - sudo apt-get install texinfo - - wget http://199.231.187.83/resources/libmicrohttpd-0.9.26.tar.gz - - tar -xvzf libmicrohttpd-0.9.26.tar.gz - - cd libmicrohttpd-0.9.26 + - wget ftp://ftp.gnu.org/gnu/libmicrohttpd/libmicrohttpd-0.9.37.tar.gz + - tar -xvzf libmicrohttpd-0.9.37.tar.gz + - cd libmicrohttpd-0.9.37 - ./configure --prefix=/usr - make - sudo make install From 9d8c006e6ab8594409f16ef3d5ea364b8973c47d Mon Sep 17 00:00:00 2001 From: Shane Peelar Date: Wed, 18 Jun 2014 12:07:42 -0400 Subject: [PATCH 083/623] Modified one last area where int was used as socket type instead of MHD_socket, this time being in the API. MHD_socket is int on unix systems, so this change shouldn't break any existing code --- README.md | 4 ++++ src/httpserver/details/event_tuple.hpp | 2 +- src/httpserver/event_supplier.hpp | 2 +- src/webserver.cpp | 4 ++-- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 278e70cd..0a1ad396 100644 --- a/README.md +++ b/README.md @@ -135,6 +135,10 @@ Requirements * libmicrohttpd >= 0.9.7 * doxygen (if you want to build code reference) +Additionally, for MinGW on windows you will need: +* libwinpthread (For MinGW-w64, if you use thread model posix then you have this) +* libgnurx >= 2.5.1 + Compilation =========== libhttpserver uses the standard system where the usual build process diff --git a/src/httpserver/details/event_tuple.hpp b/src/httpserver/details/event_tuple.hpp index ec289120..a81ca7b9 100644 --- a/src/httpserver/details/event_tuple.hpp +++ b/src/httpserver/details/event_tuple.hpp @@ -42,7 +42,7 @@ namespace details fd_set*, fd_set*, fd_set*, - int* + MHD_socket* ); typedef struct timeval(*get_timeout_ptr)(); diff --git a/src/httpserver/event_supplier.hpp b/src/httpserver/event_supplier.hpp index 6835050e..9c7e223b 100644 --- a/src/httpserver/event_supplier.hpp +++ b/src/httpserver/event_supplier.hpp @@ -43,7 +43,7 @@ class event_supplier fd_set* read_fdset, fd_set* write_fdset, fd_set* exc_fdset, - int* max + MHD_socket* max ) const { static_cast(this)->supply_events( diff --git a/src/webserver.cpp b/src/webserver.cpp index 50ea3196..e5122cd0 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -308,10 +308,10 @@ void* webserver::select(void* self) ++it ) { - int local_max; + MHD_socket local_max; (*it).second.supply_events(&rs, &ws, &es, &local_max); - if((MHD_socket) local_max > max) + if(local_max > max) max = local_max; struct timeval t = (*it).second.get_timeout(); From 9577eb620370ecb458f35d625810187652672e22 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Mon, 23 Jun 2014 02:42:04 +0100 Subject: [PATCH 084/623] Update libmicrohttpd dependency to version 0.9.37 --- README.md | 2 +- configure.ac | 2 +- libhttpserver.pc.in | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 0a1ad396..ff8b0cf6 100644 --- a/README.md +++ b/README.md @@ -132,7 +132,7 @@ used per connection to avoid resource exhaustion. Requirements ============ * g++ >= 4.1.2 -* libmicrohttpd >= 0.9.7 +* libmicrohttpd >= 0.9.37 * doxygen (if you want to build code reference) Additionally, for MinGW on windows you will need: diff --git a/configure.ac b/configure.ac index c3333227..e271a012 100644 --- a/configure.ac +++ b/configure.ac @@ -94,7 +94,7 @@ AC_CHECK_HEADER([signal.h],[],[AC_MSG_ERROR("signal.h not found")]) AC_CHECK_HEADER([gnutls/gnutls.h],[have_gnutls="yes"],[AC_MSG_WARN("gnutls/gnutls.h not found. TLS will be disabled"); have_gnutls="no"]) # Checks for libmicrohttpd -PKG_CHECK_MODULES([LIBMICROHTTPD],[libmicrohttpd >= 0.9.7],[],[AC_MSG_ERROR("libmicrohttpd not present or too old - install libmicrohttpd >= 0.9.7")]) +PKG_CHECK_MODULES([LIBMICROHTTPD],[libmicrohttpd >= 0.9.37],[],[AC_MSG_ERROR("libmicrohttpd not present or too old - install libmicrohttpd >= 0.9.37")]) #AC_CHECK_HEADER([microhttpd.h],[],[AC_MSG_ERROR("Microhttpd header files not found. Please use a version >= 0.9.9.")]) #AC_CHECK_LIB([microhttpd],[MHD_start_daemon],[],[AC_MSG_ERROR("Microhttpd header files not found. Please use a version >= 0.9.9.")]) diff --git a/libhttpserver.pc.in b/libhttpserver.pc.in index 51bba99d..36355eea 100644 --- a/libhttpserver.pc.in +++ b/libhttpserver.pc.in @@ -6,7 +6,7 @@ includedir=@includedir@ Name: libhttpserver Description: A C++ library for creating an embedded Rest HTTP server Version: @VERSION@ -Requires: libmicrohttpd >= 0.9.7 +Requires: libmicrohttpd >= 0.9.37 Conflicts: Libs: -L${libdir} -lmicrohttpd Libs.private: @LHT_LIBDEPS@ From f8cae1d5e664cd853d121871b9098127057da2cc Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Mon, 23 Jun 2014 02:45:23 +0100 Subject: [PATCH 085/623] Added Shane Peelar in the list of authors --- AUTHORS | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/AUTHORS b/AUTHORS index adf0a9aa..cba524df 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1,7 +1,10 @@ -Primary developer: +- Primary developer: Sebastiano Merlino (maintainer) -Code contributions also came from: +- Code contributions also came from: Dario Mazza Andrea Nicotra Jeff Waller + +- Support for building on MinGW/Cygwin systems +Shane Peelar From 8bf005aff8cb86afdead20ac2a4c3e563593a8ce Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Mon, 23 Jun 2014 02:48:56 +0100 Subject: [PATCH 086/623] Updated version --- ChangeLog | 5 +++++ debian/changelog.in | 7 +++++++ 2 files changed, 12 insertions(+) diff --git a/ChangeLog b/ChangeLog index 1758fb2d..bae73d79 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +Sun Jul 23 02:46:20 2014 +0100 + Support for building on MinGW/Cygwin systems + min libmicrohttpd version moved to 0.9.37 + Moved to version 0.8.0 + Sat Mar 23 15:22:40 2014 +0100 Continue the cleanup reducing webserver.cpp responsibilities Deep work on documentation diff --git a/debian/changelog.in b/debian/changelog.in index f22ad177..117db801 100644 --- a/debian/changelog.in +++ b/debian/changelog.in @@ -1,3 +1,10 @@ +libhttpserver (0.8.0) unstable; urgency=low + * Support for building on MinGW/Cygwin systems + * min libmicrohttpd version moved to 0.9.37 + * Moved to version 0.8.0 + + -- Sebastiano Merlino Sat, 23 Jul 2014 02:46:20 +0100 + libhttpserver (0.7.2) unstable; urgency=low * Documentation updates * Reduced responsibilities of webserver class From d26562e95d173852aee4007a3ea8df9c959e4fb7 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Mon, 23 Jun 2014 20:15:30 +0100 Subject: [PATCH 087/623] [merlinos] Changed reference version in the documentation --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ff8b0cf6..dd358c56 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ Copyright (C) 2014 Sebastiano Merlino. Free Documentation License". --> -The libhttpserver (0.7.2) reference manual +The libhttpserver (0.8.0) reference manual ========================================== [![Build Status](https://travis-ci.org/etr/libhttpserver.png?branch=master)](https://travis-ci.org/etr/libhttpserver) From a47c3e02adb2dc7062d69f5870a91040ee6bcaa5 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Mon, 23 Jun 2014 23:52:48 +0100 Subject: [PATCH 088/623] corrected indentation on http_utils --- src/httpserver/http_utils.hpp | 274 +++++++++++++++++----------------- 1 file changed, 137 insertions(+), 137 deletions(-) diff --git a/src/httpserver/http_utils.hpp b/src/httpserver/http_utils.hpp index 9707aca9..0ba884d1 100644 --- a/src/httpserver/http_utils.hpp +++ b/src/httpserver/http_utils.hpp @@ -90,143 +90,143 @@ class http_utils IPV4 = 4, IPV6 = 16 }; - static const short http_method_connect_code; - static const short http_method_delete_code; - static const short http_method_get_code; - static const short http_method_head_code; - static const short http_method_options_code; - static const short http_method_post_code; - static const short http_method_put_code; - static const short http_method_trace_code; - static const short http_method_unknown_code; - - static const int http_continue; - static const int http_switching_protocol; - static const int http_processing; - - static const int http_ok; - static const int http_created; - static const int http_accepted; - static const int http_non_authoritative_information; - static const int http_no_content; - static const int http_reset_content; - static const int http_partial_content; - static const int http_multi_status; - - static const int http_multiple_choices; - static const int http_moved_permanently; - static const int http_found; - static const int http_see_other; - static const int http_not_modified; - static const int http_use_proxy; - static const int http_switch_proxy; - static const int http_temporary_redirect; - - static const int http_bad_request; - static const int http_unauthorized; - static const int http_payment_required; - static const int http_forbidden; - static const int http_not_found; - static const int http_method_not_allowed; - static const int http_method_not_acceptable; - static const int http_proxy_authentication_required; - static const int http_request_timeout; - static const int http_conflict; - static const int http_gone; - static const int http_length_required; - static const int http_precondition_failed; - static const int http_request_entity_too_large; - static const int http_request_uri_too_long; - static const int http_unsupported_media_type; - static const int http_requested_range_not_satisfiable; - static const int http_expectation_failed; - static const int http_unprocessable_entity; - static const int http_locked; - static const int http_failed_dependency; - static const int http_unordered_collection; - static const int http_upgrade_required; - static const int http_retry_with; - - static const int http_internal_server_error; - static const int http_not_implemented; - static const int http_bad_gateway; - static const int http_service_unavailable; - static const int http_gateway_timeout; - static const int http_version_not_supported; - static const int http_variant_also_negotiated; - static const int http_insufficient_storage; - static const int http_bandwidth_limit_exceeded; - static const int http_not_extended; - - static const int shoutcast_response; - - /* See also: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html */ - static const std::string http_header_accept; - static const std::string http_header_accept_charset; - static const std::string http_header_accept_encoding; - static const std::string http_header_accept_language; - static const std::string http_header_accept_ranges; - static const std::string http_header_age; - static const std::string http_header_allow; - static const std::string http_header_authorization; - static const std::string http_header_cache_control; - static const std::string http_header_connection; - static const std::string http_header_content_encoding; - static const std::string http_header_content_language; - static const std::string http_header_content_length; - static const std::string http_header_content_location; - static const std::string http_header_content_md5; - static const std::string http_header_content_range; - static const std::string http_header_content_type; - static const std::string http_header_date; - static const std::string http_header_etag; - static const std::string http_header_expect; - static const std::string http_header_expires; - static const std::string http_header_from; - static const std::string http_header_host; - static const std::string http_header_if_match; - static const std::string http_header_if_modified_since; - static const std::string http_header_if_none_match; - static const std::string http_header_if_range; - static const std::string http_header_if_unmodified_since; - static const std::string http_header_last_modified; - static const std::string http_header_location; - static const std::string http_header_max_forwards; - static const std::string http_header_pragma; - static const std::string http_header_proxy_authenticate; - static const std::string http_header_proxy_authentication; - static const std::string http_header_range; - static const std::string http_header_referer; - static const std::string http_header_retry_after; - static const std::string http_header_server; - static const std::string http_header_te; - static const std::string http_header_trailer; - static const std::string http_header_transfer_encoding; - static const std::string http_header_upgrade; - static const std::string http_header_user_agent; - static const std::string http_header_vary; - static const std::string http_header_via; - static const std::string http_header_warning; - static const std::string http_header_www_authenticate; - - static const std::string http_version_1_0; - static const std::string http_version_1_1; - - static const std::string http_method_connect; - static const std::string http_method_delete; - static const std::string http_method_head; - static const std::string http_method_get; - static const std::string http_method_options; - static const std::string http_method_post; - static const std::string http_method_put; - static const std::string http_method_trace; - - static const std::string http_post_encoding_form_urlencoded; - static const std::string http_post_encoding_multipart_formdata; - static size_t tokenize_url(const std::string&, - std::vector& result, const char separator = '/' - ); - static void standardize_url(const std::string&, std::string& result); + static const short http_method_connect_code; + static const short http_method_delete_code; + static const short http_method_get_code; + static const short http_method_head_code; + static const short http_method_options_code; + static const short http_method_post_code; + static const short http_method_put_code; + static const short http_method_trace_code; + static const short http_method_unknown_code; + + static const int http_continue; + static const int http_switching_protocol; + static const int http_processing; + + static const int http_ok; + static const int http_created; + static const int http_accepted; + static const int http_non_authoritative_information; + static const int http_no_content; + static const int http_reset_content; + static const int http_partial_content; + static const int http_multi_status; + + static const int http_multiple_choices; + static const int http_moved_permanently; + static const int http_found; + static const int http_see_other; + static const int http_not_modified; + static const int http_use_proxy; + static const int http_switch_proxy; + static const int http_temporary_redirect; + + static const int http_bad_request; + static const int http_unauthorized; + static const int http_payment_required; + static const int http_forbidden; + static const int http_not_found; + static const int http_method_not_allowed; + static const int http_method_not_acceptable; + static const int http_proxy_authentication_required; + static const int http_request_timeout; + static const int http_conflict; + static const int http_gone; + static const int http_length_required; + static const int http_precondition_failed; + static const int http_request_entity_too_large; + static const int http_request_uri_too_long; + static const int http_unsupported_media_type; + static const int http_requested_range_not_satisfiable; + static const int http_expectation_failed; + static const int http_unprocessable_entity; + static const int http_locked; + static const int http_failed_dependency; + static const int http_unordered_collection; + static const int http_upgrade_required; + static const int http_retry_with; + + static const int http_internal_server_error; + static const int http_not_implemented; + static const int http_bad_gateway; + static const int http_service_unavailable; + static const int http_gateway_timeout; + static const int http_version_not_supported; + static const int http_variant_also_negotiated; + static const int http_insufficient_storage; + static const int http_bandwidth_limit_exceeded; + static const int http_not_extended; + + static const int shoutcast_response; + + /* See also: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html */ + static const std::string http_header_accept; + static const std::string http_header_accept_charset; + static const std::string http_header_accept_encoding; + static const std::string http_header_accept_language; + static const std::string http_header_accept_ranges; + static const std::string http_header_age; + static const std::string http_header_allow; + static const std::string http_header_authorization; + static const std::string http_header_cache_control; + static const std::string http_header_connection; + static const std::string http_header_content_encoding; + static const std::string http_header_content_language; + static const std::string http_header_content_length; + static const std::string http_header_content_location; + static const std::string http_header_content_md5; + static const std::string http_header_content_range; + static const std::string http_header_content_type; + static const std::string http_header_date; + static const std::string http_header_etag; + static const std::string http_header_expect; + static const std::string http_header_expires; + static const std::string http_header_from; + static const std::string http_header_host; + static const std::string http_header_if_match; + static const std::string http_header_if_modified_since; + static const std::string http_header_if_none_match; + static const std::string http_header_if_range; + static const std::string http_header_if_unmodified_since; + static const std::string http_header_last_modified; + static const std::string http_header_location; + static const std::string http_header_max_forwards; + static const std::string http_header_pragma; + static const std::string http_header_proxy_authenticate; + static const std::string http_header_proxy_authentication; + static const std::string http_header_range; + static const std::string http_header_referer; + static const std::string http_header_retry_after; + static const std::string http_header_server; + static const std::string http_header_te; + static const std::string http_header_trailer; + static const std::string http_header_transfer_encoding; + static const std::string http_header_upgrade; + static const std::string http_header_user_agent; + static const std::string http_header_vary; + static const std::string http_header_via; + static const std::string http_header_warning; + static const std::string http_header_www_authenticate; + + static const std::string http_version_1_0; + static const std::string http_version_1_1; + + static const std::string http_method_connect; + static const std::string http_method_delete; + static const std::string http_method_head; + static const std::string http_method_get; + static const std::string http_method_options; + static const std::string http_method_post; + static const std::string http_method_put; + static const std::string http_method_trace; + + static const std::string http_post_encoding_form_urlencoded; + static const std::string http_post_encoding_multipart_formdata; + static size_t tokenize_url(const std::string&, + std::vector& result, const char separator = '/' + ); + static void standardize_url(const std::string&, std::string& result); }; #define COMPARATOR(x, y, op) \ From d68e99edaaaf79cbd7e3f672aef825d97be39ff9 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Fri, 11 Jul 2014 02:32:41 +0100 Subject: [PATCH 089/623] Added some tests for http_utils sublib --- test/Makefile.am | 3 +- test/unit/http_utils_test.cpp | 52 +++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 test/unit/http_utils_test.cpp diff --git a/test/Makefile.am b/test/Makefile.am index 91d63dd4..c79ae25b 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -19,9 +19,10 @@ LDADD = $(top_builddir)/src/libhttpserver.la AM_CPPFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/src/httpserver/ METASOURCES = AUTO -check_PROGRAMS = basic +check_PROGRAMS = basic http_utils basic_SOURCES = integ/basic.cpp +http_utils_SOURCES = unit/http_utils_test.cpp noinst_HEADERS = littletest.hpp AM_CXXFLAGS += -lcurl -Wall -fPIC diff --git a/test/unit/http_utils_test.cpp b/test/unit/http_utils_test.cpp new file mode 100644 index 00000000..12b23146 --- /dev/null +++ b/test/unit/http_utils_test.cpp @@ -0,0 +1,52 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011, 2012, 2013, 2014 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#include "littletest.hpp" +#include "http_utils.hpp" +#include + +using namespace httpserver; +using namespace std; + +LT_BEGIN_SUITE(http_utils_suite) + void set_up() + { + } + + void tear_down() + { + } +LT_END_SUITE(http_utils_suite) + +LT_BEGIN_AUTO_TEST(http_utils_suite, unescape) + char* string_with_plus = (char*) malloc(5 * sizeof(char)); + sprintf(string_with_plus, "%s", "A%20B"); + int expected_size = http::http_unescape(string_with_plus); + + char* expected = (char*) malloc(3 * sizeof(char)); + sprintf(expected, "%s", "A B"); + + LT_CHECK_EQ(string(string_with_plus), string(expected)); + LT_CHECK_EQ(expected_size, 3); +LT_END_AUTO_TEST(unescape) + +LT_BEGIN_AUTO_TEST_ENV() + AUTORUN_TESTS() +LT_END_AUTO_TEST_ENV() From 91f3a4229502899d8f16dea0549163c54191b232 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Fri, 11 Jul 2014 02:38:32 +0100 Subject: [PATCH 090/623] fit \0 in strings in http_utils_test --- test/unit/http_utils_test.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/unit/http_utils_test.cpp b/test/unit/http_utils_test.cpp index 12b23146..09d2767e 100644 --- a/test/unit/http_utils_test.cpp +++ b/test/unit/http_utils_test.cpp @@ -36,11 +36,11 @@ LT_BEGIN_SUITE(http_utils_suite) LT_END_SUITE(http_utils_suite) LT_BEGIN_AUTO_TEST(http_utils_suite, unescape) - char* string_with_plus = (char*) malloc(5 * sizeof(char)); + char* string_with_plus = (char*) malloc(6 * sizeof(char)); sprintf(string_with_plus, "%s", "A%20B"); int expected_size = http::http_unescape(string_with_plus); - char* expected = (char*) malloc(3 * sizeof(char)); + char* expected = (char*) malloc(4 * sizeof(char)); sprintf(expected, "%s", "A B"); LT_CHECK_EQ(string(string_with_plus), string(expected)); From b4657167f1ce0deb6d2ad39a2537061b20528727 Mon Sep 17 00:00:00 2001 From: Chris Love Date: Fri, 29 Aug 2014 21:09:47 -0700 Subject: [PATCH 091/623] Updated example programs and Makefiles --- Makefile.am | 4 +- configure.ac | 1 + examples/Makefile.am | 26 +++++++ examples/README.md | 57 +++++++++++++++ examples/readme | 3 - examples/service.cpp | 166 +++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 252 insertions(+), 5 deletions(-) create mode 100644 examples/Makefile.am create mode 100644 examples/README.md delete mode 100644 examples/readme create mode 100644 examples/service.cpp diff --git a/Makefile.am b/Makefile.am index 4001ab70..cee644e1 100644 --- a/Makefile.am +++ b/Makefile.am @@ -24,8 +24,8 @@ LIBTOOL_DEPS = @LIBTOOL_DEPS@ AUTOMAKE_OPTIONS = foreign 1.4 ACLOCAL_AMFLAGS = -I m4 -SUBDIRS = src test -DIST_SUBDIRS = src test +SUBDIRS = src test examples +DIST_SUBDIRS = src test examples EXTRA_DIST = libhttpserver.pc.in debian/changelog.in debian/control.in debian/copyright.in debian/rules.in debian/libhttpserver-dev.install.in debian/libhttpserver.install.in redhat/libhttpserver.SPEC.in $(DX_CONFIG) MOSTLYCLEANFILES = $(DX_CLEANFILES) redhat/SOURCES/* diff --git a/configure.ac b/configure.ac index e271a012..93b65d9b 100644 --- a/configure.ac +++ b/configure.ac @@ -176,6 +176,7 @@ AC_OUTPUT( doc/Makefile src/Makefile test/Makefile + examples/Makefile debian/changelog debian/copyright debian/control diff --git a/examples/Makefile.am b/examples/Makefile.am new file mode 100644 index 00000000..a419f400 --- /dev/null +++ b/examples/Makefile.am @@ -0,0 +1,26 @@ +# +# This file is part of libhttpserver +# Copyright (C) 2011, 2012, 2013, 2014 Sebastiano Merlino +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +LDADD = $(top_builddir)/src/libhttpserver.la +AM_CPPFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/src/httpserver/ +METASOURCES = AUTO +noinst_PROGRAMS = hello_world service + +hello_world_SOURCES = hello_world.cpp +service_SOURCES = service.cpp + diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 00000000..f96596df --- /dev/null +++ b/examples/README.md @@ -0,0 +1,57 @@ +Example Programs +================ + +hello_world.cpp - a very simple example of using libhttpserver to + create a Rest server capable of receiving and processing + HTTP requests. The server will be listening on port + 8080. + + +service.cpp - an example using more of the libhttpserver API. + This creates a Rest server capable of running with + HTTP or HTTPS (provided that libhttpserver and + libmicrohttpd have been compiled with SSL support. + + The server can be configured via command line + arguments: + + -p - port number to listen on (default 8080) + -s - enable HTTPS + -k - server key filename (default "key.pem") + -c - server certificate filename (default "cert.pem") + +Creating Certificates +===================== +Self-signed certificates can be created using OpenSSL using the +following steps: + +$ openssl genrsa -des3 -passout pass:x -out serer.pass.key 2048 +$ openssl rsa -passin pass:x -in server.pass.key -out server.key +$ openssl req -new -key server.key -out server.csr +$ openssl x509 -req -days 365 -in server.csr -signkey server.key -out server.crt + +On the last step when prompted for a challenge password it can be left +empty. + +Thanks to https://devcenter.heroku.com/articles/ssl-certificate-self +for these instructions. + +Keystore configuration +====================== +If using a local client such as RestClient for testing the Rest server +then a keystore needs to be established. These commands should be +bundled with your Java installation. + +$ keytool -noprompt -import -keystore /path/to/restclient.store -alias +restclient -file /path/to/server.crt + +The keys in the store can be listed as follows: + +$ keytool -list -v -keystore /path/to/restclient.store + +The client can then be configured to use this keystore. Thanks to +http://rubensgomes.blogspot.com/2012/01/how-to-set-up-restclient-for-ssl.html +for instructions on configuring RestClient. + + + diff --git a/examples/readme b/examples/readme deleted file mode 100644 index 36ed4896..00000000 --- a/examples/readme +++ /dev/null @@ -1,3 +0,0 @@ -In order to compile test file it is necessary to compile running: - - g++ -lhttpserver -lboost_thread Test.cpp -o Test diff --git a/examples/service.cpp b/examples/service.cpp new file mode 100644 index 00000000..fee6e9d4 --- /dev/null +++ b/examples/service.cpp @@ -0,0 +1,166 @@ +#include +#include +#include +#include + +using namespace httpserver; + +class service_resource: public http_resource { +public: + service_resource(); + + ~service_resource(); + + void render_GET(const http_request &req, http_response** res); + + void render_PUT(const http_request &req, http_response** res); + + void render_POST(const http_request &req, http_response** res); + + void render(const http_request &req, http_response** res); + + void render_HEAD(const http_request &req, http_response** res); + + void render_OPTIONS(const http_request &req, http_response** res); + + void render_CONNECT(const http_request &req, http_response** res); + + void render_DELETE(const http_request &req, http_response** res); + +private: + + +}; + +service_resource::service_resource() +{} + +service_resource::~service_resource() +{} + +void +service_resource::render_GET(const http_request &req, http_response** res) +{ + std::cout << "service_resource::render_GET()" << std::endl; + + *res = new http_response(http_response_builder("GET response", 200).string_response()); +} + + +void +service_resource::render_PUT(const http_request &req, http_response** res) +{ + std::cout << "service_resource::render_PUT()" << std::endl; + + *res = new http_response(http_response_builder("PUT response", 200).string_response()); +} + + +void +service_resource::render_POST(const http_request &req, http_response** res) +{ + std::cout << "service_resource::render_POST()" << std::endl; + + *res = new http_response(http_response_builder("POST response", 200).string_response()); +} +void +service_resource::render(const http_request &req, http_response** res) +{ + std::cout << "service_resource::render()" << std::endl; + + *res = new http_response(http_response_builder("generic response", 200).string_response()); +} + + +void +service_resource::render_HEAD(const http_request &req, http_response** res) +{ + std::cout << "service_resource::render_HEAD()" << std::endl; + + *res = new http_response(http_response_builder("HEAD response", 200).string_response()); +} + +void +service_resource::render_OPTIONS(const http_request &req, http_response** res) +{ + std::cout << "service_resource::render_OPTIONS()" << std::endl; + + *res = new http_response(http_response_builder("OPTIONS response", 200).string_response()); +} + +void +service_resource::render_CONNECT(const http_request &req, http_response** res) +{ + std::cout << "service_resource::render_CONNECT()" << std::endl; + + *res = new http_response(http_response_builder("CONNECT response", 200).string_response()); +} + +void +service_resource::render_DELETE(const http_request &req, http_response** res) +{ + std::cout << "service_resource::render_DELETE()" << std::endl; + + *res = new http_response(http_response_builder("DELETE response", 200).string_response()); +} + +int main(int argc, char **argv) +{ + uint16_t port=8090; + int c; + const char *key="key.pem"; + const char *cert="cert.pem"; + bool secure=false; + + while ((c = getopt(argc,argv,"p:k:c:s")) != EOF) { + switch (c) { + case 'p': + port=strtoul(optarg,NULL,10); + break; + case 'k': + key = optarg; + break; + case 'c': + cert=optarg; + break; + case 's': + secure=true; + break; + default: + break; + } + } + + std::cout << "Using port " << port << std::endl; + if (secure) { + std::cout << "Key: " << key << " Certificate: " << cert + << std::endl; + } + + // + // Use builder to define webserver configuration options + // + create_webserver cw = create_webserver(port).max_threads(5); + + if (secure) { + cw.use_ssl().https_mem_key(key).https_mem_cert(cert); + } + + // + // Create webserver using the configured options + // + webserver ws = cw; + + // + // Create and register service resource available at /service + // + service_resource res; + ws.register_resource("/service",&res,true); + + // + // Start and block the webserver + // + ws.start(true); + + return 0; +} From 4921f6929245d90f6d684838c4ac1e8961488c82 Mon Sep 17 00:00:00 2001 From: Chris Love Date: Fri, 29 Aug 2014 21:13:34 -0700 Subject: [PATCH 092/623] Fix formatting of README.md in examples --- examples/README.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/examples/README.md b/examples/README.md index f96596df..3777fbcf 100644 --- a/examples/README.md +++ b/examples/README.md @@ -25,10 +25,10 @@ Creating Certificates Self-signed certificates can be created using OpenSSL using the following steps: -$ openssl genrsa -des3 -passout pass:x -out serer.pass.key 2048 -$ openssl rsa -passin pass:x -in server.pass.key -out server.key -$ openssl req -new -key server.key -out server.csr -$ openssl x509 -req -days 365 -in server.csr -signkey server.key -out server.crt +> openssl genrsa -des3 -passout pass:x -out serer.pass.key 2048 +> openssl rsa -passin pass:x -in server.pass.key -out server.key +> openssl req -new -key server.key -out server.csr +> openssl x509 -req -days 365 -in server.csr -signkey server.key -out server.crt On the last step when prompted for a challenge password it can be left empty. @@ -38,7 +38,8 @@ for these instructions. Keystore configuration ====================== -If using a local client such as RestClient for testing the Rest server +If using a local client such as RestClient +(https://github.com/wiztools/rest-client) for testing the Rest server then a keystore needs to be established. These commands should be bundled with your Java installation. From e4b44dd989e2e9f9fdb8c32f1d53c964b1e7591a Mon Sep 17 00:00:00 2001 From: Chris Love Date: Fri, 29 Aug 2014 21:18:50 -0700 Subject: [PATCH 093/623] Add copyright info to example service.cpp --- examples/README.md | 8 ++++---- examples/service.cpp | 22 +++++++++++++++++++++- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/examples/README.md b/examples/README.md index 3777fbcf..686880b4 100644 --- a/examples/README.md +++ b/examples/README.md @@ -25,10 +25,10 @@ Creating Certificates Self-signed certificates can be created using OpenSSL using the following steps: -> openssl genrsa -des3 -passout pass:x -out serer.pass.key 2048 -> openssl rsa -passin pass:x -in server.pass.key -out server.key -> openssl req -new -key server.key -out server.csr -> openssl x509 -req -days 365 -in server.csr -signkey server.key -out server.crt + $ openssl genrsa -des3 -passout pass:x -out serer.pass.key 2048 + $ openssl rsa -passin pass:x -in server.pass.key -out server.key + $ openssl req -new -key server.key -out server.csr + $ openssl x509 -req -days 365 -in server.csr -signkey server.key -out server.crt On the last step when prompted for a challenge password it can be left empty. diff --git a/examples/service.cpp b/examples/service.cpp index fee6e9d4..50d459fd 100644 --- a/examples/service.cpp +++ b/examples/service.cpp @@ -1,3 +1,23 @@ +/* + This file is part of libhttpserver + Copyright (C) 2014 Chris Love + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + #include #include #include @@ -106,7 +126,7 @@ service_resource::render_DELETE(const http_request &req, http_response** res) int main(int argc, char **argv) { - uint16_t port=8090; + uint16_t port=8080; int c; const char *key="key.pem"; const char *cert="cert.pem"; From bd184f1d5377dd73f8607f2e7a614714d19ae5e3 Mon Sep 17 00:00:00 2001 From: Chris Love Date: Sat, 30 Aug 2014 16:06:01 -0700 Subject: [PATCH 094/623] Add operator<<() to http_request and http_response --- examples/service.cpp | 65 +++++++++++++++++++++++++++----- src/http_request.cpp | 20 +++++++++- src/http_response.cpp | 12 ++++++ src/http_utils.cpp | 32 ++++++++++++++++ src/httpserver/http_request.hpp | 5 +++ src/httpserver/http_response.hpp | 5 +++ src/httpserver/http_utils.hpp | 21 +++++++++++ 7 files changed, 149 insertions(+), 11 deletions(-) diff --git a/examples/service.cpp b/examples/service.cpp index 50d459fd..537ebf97 100644 --- a/examples/service.cpp +++ b/examples/service.cpp @@ -1,6 +1,6 @@ /* This file is part of libhttpserver - Copyright (C) 2014 Chris Love + Copyright (C) 2014 Sebastiano Merlino This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public @@ -25,6 +25,8 @@ using namespace httpserver; +bool verbose=false; + class service_resource: public http_resource { public: service_resource(); @@ -63,7 +65,11 @@ service_resource::render_GET(const http_request &req, http_response** res) { std::cout << "service_resource::render_GET()" << std::endl; - *res = new http_response(http_response_builder("GET response", 200).string_response()); + if (verbose) std::cout << req; + + *res = new http_response(http_response_builder("GET response", 200).string_response()); + + if (verbose) std::cout << **res; } @@ -72,7 +78,11 @@ service_resource::render_PUT(const http_request &req, http_response** res) { std::cout << "service_resource::render_PUT()" << std::endl; - *res = new http_response(http_response_builder("PUT response", 200).string_response()); + if (verbose) std::cout << req; + + *res = new http_response(http_response_builder("PUT response", 200).string_response()); + + if (verbose) std::cout << **res; } @@ -81,14 +91,22 @@ service_resource::render_POST(const http_request &req, http_response** res) { std::cout << "service_resource::render_POST()" << std::endl; - *res = new http_response(http_response_builder("POST response", 200).string_response()); + if (verbose) std::cout << req; + + *res = new http_response(http_response_builder("POST response", 200).string_response()); + + if (verbose) std::cout << **res; } void service_resource::render(const http_request &req, http_response** res) { std::cout << "service_resource::render()" << std::endl; - *res = new http_response(http_response_builder("generic response", 200).string_response()); + if (verbose) std::cout << req; + + *res = new http_response(http_response_builder("generic response", 200).string_response()); + + if (verbose) std::cout << **res; } @@ -97,7 +115,11 @@ service_resource::render_HEAD(const http_request &req, http_response** res) { std::cout << "service_resource::render_HEAD()" << std::endl; - *res = new http_response(http_response_builder("HEAD response", 200).string_response()); + if (verbose) std::cout << req; + + *res = new http_response(http_response_builder("HEAD response", 200).string_response()); + + if (verbose) std::cout << **res; } void @@ -105,7 +127,11 @@ service_resource::render_OPTIONS(const http_request &req, http_response** res) { std::cout << "service_resource::render_OPTIONS()" << std::endl; - *res = new http_response(http_response_builder("OPTIONS response", 200).string_response()); + if (verbose) std::cout << req; + + *res = new http_response(http_response_builder("OPTIONS response", 200).string_response()); + + if (verbose) std::cout << **res; } void @@ -113,7 +139,11 @@ service_resource::render_CONNECT(const http_request &req, http_response** res) { std::cout << "service_resource::render_CONNECT()" << std::endl; - *res = new http_response(http_response_builder("CONNECT response", 200).string_response()); + if (verbose) std::cout << req; + + *res = new http_response(http_response_builder("CONNECT response", 200).string_response()); + + if (verbose) std::cout << **res; } void @@ -121,7 +151,17 @@ service_resource::render_DELETE(const http_request &req, http_response** res) { std::cout << "service_resource::render_DELETE()" << std::endl; - *res = new http_response(http_response_builder("DELETE response", 200).string_response()); + if (verbose) std::cout << req; + + *res = new http_response(http_response_builder("DELETE response", 200).string_response()); + + if (verbose) std::cout << **res; +} + +void usage() +{ + std::cout << "Usage:" << std::endl + << "service [-p ][-s [-k ][-c ]][-v]" << std::endl; } int main(int argc, char **argv) @@ -132,7 +172,7 @@ int main(int argc, char **argv) const char *cert="cert.pem"; bool secure=false; - while ((c = getopt(argc,argv,"p:k:c:s")) != EOF) { + while ((c = getopt(argc,argv,"p:k:c:sv?")) != EOF) { switch (c) { case 'p': port=strtoul(optarg,NULL,10); @@ -145,8 +185,13 @@ int main(int argc, char **argv) break; case 's': secure=true; + break; + case 'v': + verbose=true; break; default: + usage(); + exit(1); break; } } diff --git a/src/http_request.cpp b/src/http_request.cpp index 3d7990ce..87de8eeb 100644 --- a/src/http_request.cpp +++ b/src/http_request.cpp @@ -22,6 +22,7 @@ #include "http_utils.hpp" #include "http_request.hpp" #include "string_utilities.hpp" +#include using namespace std; @@ -85,4 +86,21 @@ size_t http_request::get_args(std::map return result.size(); } -}; +std::ostream &operator<< (std::ostream &os, const http_request &r) +{ + os << r.method << " Request [user:\"" << r.user << "\" pass:\"" << r.pass << "\"] path:\"" + << r.path << "\"" << std::endl; + + http::dump_header_map(os,"Headers",r.headers); + http::dump_header_map(os,"Footers",r.footers); + http::dump_header_map(os,"Cookies",r.cookies); + http::dump_arg_map(os,"Query Args",r.args); + + os << " Version [ " << r.version << " ] Requestor [ " << r.requestor + << " ] Port [ " << r.requestor_port << " ]" << std::endl; + + return os; +} + + +} diff --git a/src/http_response.cpp b/src/http_response.cpp index d086e15e..75d1082b 100644 --- a/src/http_response.cpp +++ b/src/http_response.cpp @@ -289,4 +289,16 @@ void http_response::get_raw_response_lp_send( ws->send_message_to_topic(send_topic, content); } +std::ostream &operator<< (std::ostream &os, const http_response &r) +{ + os << "Response [response_code:" << r.response_code << "]" << std::endl; + + http::dump_header_map(os,"Headers",r.headers); + http::dump_header_map(os,"Footers",r.footers); + http::dump_header_map(os,"Cookies",r.cookies); + + return os; +} + + }; diff --git a/src/http_utils.cpp b/src/http_utils.cpp index 96c05df0..ea418ecc 100644 --- a/src/http_utils.cpp +++ b/src/http_utils.cpp @@ -33,6 +33,7 @@ #include #include #include +#include #include "string_utilities.hpp" #include "http_utils.hpp" @@ -541,5 +542,36 @@ char* load_file (const char *filename) return content; } +void dump_header_map(std::ostream &os, const std::string &prefix, + const std::map &map) +{ + std::map::const_iterator it = map.begin(); + std::map::const_iterator end = map.end(); + + if (map.size()) { + os << " " << prefix << " ["; + for (; it != end; ++it) { + os << (*it).first << ":\"" << (*it).second << "\" "; + } + os << "]" << std::endl; + } +} + +void dump_arg_map(std::ostream &os, const std::string &prefix, + const std::map &map) +{ + std::map::const_iterator it = map.begin(); + std::map::const_iterator end = map.end(); + + if (map.size()) { + os << " " << prefix << " ["; + for (; it != end; ++it) { + os << (*it).first << ":\"" << (*it).second << "\" "; + } + os << "]" << std::endl; + } +} + + }; }; diff --git a/src/httpserver/http_request.hpp b/src/httpserver/http_request.hpp index d702f35f..90def576 100644 --- a/src/httpserver/http_request.hpp +++ b/src/httpserver/http_request.hpp @@ -29,6 +29,7 @@ #include #include #include +#include struct MHD_Connection; @@ -355,6 +356,8 @@ class http_request const std::string& password, int nonce_timeout, bool& reload_nonce ) const; + + friend std::ostream &operator<< (std::ostream &os, const http_request &r); private: /** * Default constructor of the class. It is a specific responsibility of apis to initialize this type of objects. @@ -585,5 +588,7 @@ class http_request friend class webserver; }; +std::ostream &operator<< (std::ostream &os, const http_request &r); + }; #endif diff --git a/src/httpserver/http_response.hpp b/src/httpserver/http_response.hpp index a8854116..d7b57df3 100644 --- a/src/httpserver/http_response.hpp +++ b/src/httpserver/http_response.hpp @@ -27,6 +27,7 @@ #include #include #include +#include #include "httpserver/details/http_resource_mirror.hpp" @@ -69,6 +70,7 @@ typedef ssize_t(*cycle_callback_ptr)(const std::string&); class http_response { public: + http_response(const http_response_builder& builder); /** @@ -296,11 +298,14 @@ class http_response friend class http_response_builder; friend void clone_response(const http_response& hr, http_response** dhr); friend ssize_t details::cb(void* cls, uint64_t pos, char* buf, size_t max); + friend std::ostream &operator<< (std::ostream &os, const http_response &r); private: http_response& operator=(const http_response& b); static ssize_t data_generator (void* cls, uint64_t pos, char* buf, size_t max); }; +std::ostream &operator<< (std::ostream &os, const http_response &r); + }; #endif diff --git a/src/httpserver/http_utils.hpp b/src/httpserver/http_utils.hpp index 0ba884d1..51b97ffd 100644 --- a/src/httpserver/http_utils.hpp +++ b/src/httpserver/http_utils.hpp @@ -29,8 +29,10 @@ #include #include #include +#include #include #include +#include #ifdef HAVE_GNUTLS #include #endif @@ -327,6 +329,25 @@ std::string get_ip_str_new(const struct sockaddr* sa, * @return short representing the port **/ short get_port(const struct sockaddr* sa); + +/** + * Method to output the contents of a headers map to a std::ostream + * @param os The ostream + * @param prefix Prefix to identify the map + * @param map +**/ +void dump_header_map(std::ostream &os, const std::string &prefix, + const std::map &map); + +/** + * Method to output the contents of an arguments map to a std::ostream + * @param os The ostream + * @param prefix Prefix to identify the map + * @param map +**/ +void dump_arg_map(std::ostream &os, const std::string &prefix, + const std::map &map); + /** * Process escape sequences ('+'=space, %HH) Updates val in place; the * result should be UTF-8 encoded and cannot be larger than the input. From 3e35cb0378d5c412bfec2fdae27eeb3d9062d3f8 Mon Sep 17 00:00:00 2001 From: Chris Love Date: Wed, 3 Sep 2014 20:29:36 -0700 Subject: [PATCH 095/623] typo fix --- examples/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/README.md b/examples/README.md index 686880b4..d2faca33 100644 --- a/examples/README.md +++ b/examples/README.md @@ -25,7 +25,7 @@ Creating Certificates Self-signed certificates can be created using OpenSSL using the following steps: - $ openssl genrsa -des3 -passout pass:x -out serer.pass.key 2048 + $ openssl genrsa -des3 -passout pass:x -out server.pass.key 2048 $ openssl rsa -passin pass:x -in server.pass.key -out server.key $ openssl req -new -key server.key -out server.csr $ openssl x509 -req -days 365 -in server.csr -signkey server.key -out server.crt From 4e4814cf8846730561cef252c0c9d0e0b88779af Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Mon, 8 Sep 2014 23:13:39 +0100 Subject: [PATCH 096/623] Updating AUTHORS file --- AUTHORS | 3 +++ 1 file changed, 3 insertions(+) diff --git a/AUTHORS b/AUTHORS index cba524df..b5b9b0ec 100644 --- a/AUTHORS +++ b/AUTHORS @@ -8,3 +8,6 @@ Jeff Waller - Support for building on MinGW/Cygwin systems Shane Peelar + +- Example of SSL usage and operator<< on http_request and http_response +Chris Love From 21112a9cdaf48ca8a4f4fca654d64aea8a5f0b1b Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 21 Sep 2014 14:41:51 +0100 Subject: [PATCH 097/623] Adding coverage options to travis build --- .travis.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 71a01ec0..1bb8e2a7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,6 +5,7 @@ compiler: # Change this to your needs before_install: - sudo apt-get install texinfo + - sudo pip install cpp-coveralls - wget ftp://ftp.gnu.org/gnu/libmicrohttpd/libmicrohttpd-0.9.37.tar.gz - tar -xvzf libmicrohttpd-0.9.37.tar.gz - cd libmicrohttpd-0.9.37 @@ -16,7 +17,7 @@ script: - ./bootstrap - mkdir build - cd build - - ../configure + - ../configure --enable-gcov - make - make check - cd .. @@ -27,3 +28,5 @@ script: # - make # - make check # - cd .. +after_success: + - coveralls --exclude lib --exclude tests --gcov-options '\-lp' From 4f89a8449d2a42311da42160c33058c1a1b72ab3 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 21 Sep 2014 14:53:07 +0100 Subject: [PATCH 098/623] Removed useless directory exit --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 1bb8e2a7..fb37f54f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,7 +20,6 @@ script: - ../configure --enable-gcov - make - make check - - cd .. # - rm -rf build # - mkdir build # - cd build From 7c79eefe7ee9b7367bc7f2b9888e154709189465 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 21 Sep 2014 16:55:43 +0100 Subject: [PATCH 099/623] Changed root directory for code coverage checks --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index fb37f54f..a59f5951 100644 --- a/.travis.yml +++ b/.travis.yml @@ -28,4 +28,4 @@ script: # - make check # - cd .. after_success: - - coveralls --exclude lib --exclude tests --gcov-options '\-lp' + - coveralls --build-root src/ --exclude lib --exclude tests --gcov-options '\-lp' From 3cc82cf55c5366212eeb0bb0ade61e29141ebe61 Mon Sep 17 00:00:00 2001 From: Jan Klimke Date: Thu, 23 Oct 2014 14:32:47 +0200 Subject: [PATCH 100/623] * enabled MAC OS build by adding an if state to execute glibtoolize instead of libtoolize for building * added a header that was necessary to made "free" function beeing imported under MacOS --- bootstrap | 6 +++++- src/httpserver/webserver.hpp | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/bootstrap b/bootstrap index 0b50547f..dbff50c3 100755 --- a/bootstrap +++ b/bootstrap @@ -19,7 +19,11 @@ aclocal -I m4 autoheader -libtoolize --automake +if [[ "$OSTYPE" == "darwin"* ]]; then + glibtoolize --automake +else + libtoolize --automake +fi automake --add-missing autoconf diff --git a/src/httpserver/webserver.hpp b/src/httpserver/webserver.hpp index 55591524..401a0e10 100644 --- a/src/httpserver/webserver.hpp +++ b/src/httpserver/webserver.hpp @@ -36,6 +36,7 @@ #include #include #include +#include #include #include From d0d7857812955d2f81eb47339f9e7c3fbb38b0db Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Mon, 27 Oct 2014 23:44:00 +0000 Subject: [PATCH 101/623] Enabling gcov production when debug is active --- .travis.yml | 2 +- Makefile.am | 2 +- bootstrap | 2 +- configure.ac | 4 +++- test/Makefile.am | 13 +++++++++++++ test/gcov | 7 +++++++ test/rmgcda | 4 ++++ 7 files changed, 30 insertions(+), 4 deletions(-) create mode 100755 test/gcov create mode 100755 test/rmgcda diff --git a/.travis.yml b/.travis.yml index a59f5951..940dbdd8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,7 +17,7 @@ script: - ./bootstrap - mkdir build - cd build - - ../configure --enable-gcov + - ../configure --enable-debug - make - make check # - rm -rf build diff --git a/Makefile.am b/Makefile.am index cee644e1..982cf3f1 100644 --- a/Makefile.am +++ b/Makefile.am @@ -28,7 +28,7 @@ SUBDIRS = src test examples DIST_SUBDIRS = src test examples EXTRA_DIST = libhttpserver.pc.in debian/changelog.in debian/control.in debian/copyright.in debian/rules.in debian/libhttpserver-dev.install.in debian/libhttpserver.install.in redhat/libhttpserver.SPEC.in $(DX_CONFIG) -MOSTLYCLEANFILES = $(DX_CLEANFILES) redhat/SOURCES/* +MOSTLYCLEANFILES = $(DX_CLEANFILES) redhat/SOURCES/* *.gcda *.gcno *.gcov DISTCLEANFILES = redhat/SOURCES/* redhat/SPEC/* redhat/RPMS/* redhat/SRPMS/* redhat/* debian/* DIST_REVISION pkgconfigdir = $(libdir)/pkgconfig diff --git a/bootstrap b/bootstrap index dbff50c3..02a223f9 100755 --- a/bootstrap +++ b/bootstrap @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # # This file is part of libhttpserver # Copyright (C) 2011, 2012, 2013, 2014 Sebastiano Merlino diff --git a/configure.ac b/configure.ac index 93b65d9b..7c6f06c6 100644 --- a/configure.ac +++ b/configure.ac @@ -122,9 +122,11 @@ AC_ARG_ENABLE([debug], [debugit=no]) AC_MSG_RESULT([$debugit]) +AM_CONDITIONAL([COND_GCOV],[test x"$debugit" = x"yes"]) + if test x"$debugit" = x"yes"; then AC_DEFINE([DEBUG],[],[Debug Mode]) - AM_CXXFLAGS="$AM_CXXFLAGS -DDEBUG -g -Wall -Wno-uninitialized -O0 -fprofile-arcs -ftest-coverage" + AM_CXXFLAGS="$AM_CXXFLAGS -DDEBUG -g -Wall -Wno-uninitialized -O0 -fprofile-arcs -ftest-coverage --coverage --no-inline" LDFLAGS="-lgcov $LDFLAGS" else AC_DEFINE([NDEBUG],[],[No-debug Mode]) diff --git a/test/Makefile.am b/test/Makefile.am index c79ae25b..fc6e8ffc 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -21,10 +21,23 @@ AM_CPPFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/src/httpserver/ METASOURCES = AUTO check_PROGRAMS = basic http_utils +MOSTLYCLEANFILES = *.gcda *.gcno *.gcov + basic_SOURCES = integ/basic.cpp http_utils_SOURCES = unit/http_utils_test.cpp noinst_HEADERS = littletest.hpp AM_CXXFLAGS += -lcurl -Wall -fPIC +if COND_GCOV + +TESTS = rmgcda $(check_PROGRAMS) gcov +#basic.log: rmgcda.log +#http_utils.log: rmgcda.log +#gcov.log: basic.log http_utils.log + +else + TESTS = $(check_PROGRAMS) + +endif diff --git a/test/gcov b/test/gcov new file mode 100755 index 00000000..d01192d8 --- /dev/null +++ b/test/gcov @@ -0,0 +1,7 @@ +#!/bin/sh + +for srcfile in ../src/*.cpp +do + ( gcov -r $srcfile || gcov $srcfile ) > /dev/null 2> /dev/null +done +exit 0 diff --git a/test/rmgcda b/test/rmgcda new file mode 100755 index 00000000..571e9fa4 --- /dev/null +++ b/test/rmgcda @@ -0,0 +1,4 @@ +#!/bin/sh + +rm -f *.gcov *.gcda +exit 0 From 0f70628114f80cff8d8ccdb7a0fcb9afc1d64197 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Mon, 27 Oct 2014 23:58:10 +0000 Subject: [PATCH 102/623] Remove clang incompatible option from configure.ac --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 7c6f06c6..40b9864f 100644 --- a/configure.ac +++ b/configure.ac @@ -126,7 +126,7 @@ AM_CONDITIONAL([COND_GCOV],[test x"$debugit" = x"yes"]) if test x"$debugit" = x"yes"; then AC_DEFINE([DEBUG],[],[Debug Mode]) - AM_CXXFLAGS="$AM_CXXFLAGS -DDEBUG -g -Wall -Wno-uninitialized -O0 -fprofile-arcs -ftest-coverage --coverage --no-inline" + AM_CXXFLAGS="$AM_CXXFLAGS -DDEBUG -g -Wall -Wno-uninitialized -O0 -fprofile-arcs -ftest-coverage --coverage" LDFLAGS="-lgcov $LDFLAGS" else AC_DEFINE([NDEBUG],[],[No-debug Mode]) From 63ae78d26fdb9f2643ab1bad19ef823ea44c9e34 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Tue, 28 Oct 2014 00:10:39 +0000 Subject: [PATCH 103/623] Changed travis.yml to use different coveralls opt --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 940dbdd8..2c70fd56 100644 --- a/.travis.yml +++ b/.travis.yml @@ -28,4 +28,4 @@ script: # - make check # - cd .. after_success: - - coveralls --build-root src/ --exclude lib --exclude tests --gcov-options '\-lp' + - coveralls --no-gcov --extension cpp --build-root src/ --exclude lib --exclude tests --gcov-options '\-lp' From ad8f8ec353258c25b239d1d33bc57a854c36d5d8 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Tue, 28 Oct 2014 00:14:57 +0000 Subject: [PATCH 104/623] Removed clang incompatible option --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 40b9864f..4a70c441 100644 --- a/configure.ac +++ b/configure.ac @@ -126,7 +126,7 @@ AM_CONDITIONAL([COND_GCOV],[test x"$debugit" = x"yes"]) if test x"$debugit" = x"yes"; then AC_DEFINE([DEBUG],[],[Debug Mode]) - AM_CXXFLAGS="$AM_CXXFLAGS -DDEBUG -g -Wall -Wno-uninitialized -O0 -fprofile-arcs -ftest-coverage --coverage" + AM_CXXFLAGS="$AM_CXXFLAGS -DDEBUG -g -Wall -Wno-uninitialized -O0 -fprofile-arcs -ftest-coverage" LDFLAGS="-lgcov $LDFLAGS" else AC_DEFINE([NDEBUG],[],[No-debug Mode]) From c13927c15c927e4caa7a90ea16e803ee907cefaf Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Tue, 28 Oct 2014 00:51:50 +0000 Subject: [PATCH 105/623] temporary disabling gcov --- configure.ac | 7 ++++--- test/Makefile.am | 7 ------- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/configure.ac b/configure.ac index 4a70c441..400459b5 100644 --- a/configure.ac +++ b/configure.ac @@ -33,7 +33,7 @@ AC_CONFIG_MACRO_DIR([m4]) OLD_CXXFLAGS=$CXXFLAGS LT_INIT AC_PROG_CC -AC_PROG_CXX +AC_PROG_CXX([clang]) AC_PROG_LN_S CXXFLAGS=$OLD_CXXFLAGS AC_LANG([C++]) @@ -126,8 +126,9 @@ AM_CONDITIONAL([COND_GCOV],[test x"$debugit" = x"yes"]) if test x"$debugit" = x"yes"; then AC_DEFINE([DEBUG],[],[Debug Mode]) - AM_CXXFLAGS="$AM_CXXFLAGS -DDEBUG -g -Wall -Wno-uninitialized -O0 -fprofile-arcs -ftest-coverage" - LDFLAGS="-lgcov $LDFLAGS" + AM_CXXFLAGS="$AM_CXXFLAGS -DDEBUG -g -Wall -Wno-uninitialized -O0" +# LDFLAGS="$LDFLAGS -fprofile-arcs -ftest-coverage" +# LIBS="-lgcov" else AC_DEFINE([NDEBUG],[],[No-debug Mode]) AM_CXXFLAGS="$AM_CXXFLAGS -O3" diff --git a/test/Makefile.am b/test/Makefile.am index fc6e8ffc..102d4fe6 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -30,14 +30,7 @@ noinst_HEADERS = littletest.hpp AM_CXXFLAGS += -lcurl -Wall -fPIC if COND_GCOV - TESTS = rmgcda $(check_PROGRAMS) gcov -#basic.log: rmgcda.log -#http_utils.log: rmgcda.log -#gcov.log: basic.log http_utils.log - else - TESTS = $(check_PROGRAMS) - endif From cc24bfe030e2a8efc46190fff219b14a75ab1d4a Mon Sep 17 00:00:00 2001 From: jklimke Date: Thu, 6 Nov 2014 18:02:44 +0100 Subject: [PATCH 106/623] Changed executing shell to bin/bash since sh does not understand [[ commands my previous changed unfortunately broke the bootstrapping on systems where /bin/sh is not a link to bash. I changed the script to run with bash by default --- bootstrap | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bootstrap b/bootstrap index dbff50c3..02a223f9 100755 --- a/bootstrap +++ b/bootstrap @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # # This file is part of libhttpserver # Copyright (C) 2011, 2012, 2013, 2014 Sebastiano Merlino From f18712d1ceffbe13a9726080d03e34c34b7e2950 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Thu, 6 Nov 2014 22:36:48 +0000 Subject: [PATCH 107/623] Removed useless comments --- configure.ac | 2 -- 1 file changed, 2 deletions(-) diff --git a/configure.ac b/configure.ac index 400459b5..b7e8ba98 100644 --- a/configure.ac +++ b/configure.ac @@ -95,8 +95,6 @@ AC_CHECK_HEADER([gnutls/gnutls.h],[have_gnutls="yes"],[AC_MSG_WARN("gnutls/gnutl # Checks for libmicrohttpd PKG_CHECK_MODULES([LIBMICROHTTPD],[libmicrohttpd >= 0.9.37],[],[AC_MSG_ERROR("libmicrohttpd not present or too old - install libmicrohttpd >= 0.9.37")]) -#AC_CHECK_HEADER([microhttpd.h],[],[AC_MSG_ERROR("Microhttpd header files not found. Please use a version >= 0.9.9.")]) -#AC_CHECK_LIB([microhttpd],[MHD_start_daemon],[],[AC_MSG_ERROR("Microhttpd header files not found. Please use a version >= 0.9.9.")]) CXXFLAGS="-DHTTPSERVER_COMPILATION -D_REENTRANT $LIBMICROHTTPD_CFLAGS $CXXFLAGS" LDFLAGS="$LIBMICROHTTPD_LIBS $REGEX_LIBS $LD_FLAGS" From 56d22f0945fc17f098d4048c60f2c188b10d20ca Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Thu, 6 Nov 2014 22:38:17 +0000 Subject: [PATCH 108/623] Added pthread to the list of libraries on windows --- configure.ac | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index b7e8ba98..94c63a0e 100644 --- a/configure.ac +++ b/configure.ac @@ -56,11 +56,11 @@ fi case "$host" in *-mingw*) NETWORK_HEADER="winsock2.h" - REGEX_LIBS="-lregex -no-undefined" + REGEX_LIBS="-lregex -lpthread -no-undefined" ;; *-cygwin*) NETWORK_HEADER="winsock2.h" - REGEX_LIBS="-lregex -no-undefined" + REGEX_LIBS="-lregex -lpthread -no-undefined" ;; *) NETWORK_HEADER="arpa/inet.h" From b6e7cbd9871cc53783f00717ce4eb169610fb834 Mon Sep 17 00:00:00 2001 From: Guo Xiao Date: Sat, 3 Jan 2015 16:05:12 +0800 Subject: [PATCH 109/623] Fix error: use of undeclared identifier 'free' --- src/httpserver/create_webserver.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/httpserver/create_webserver.hpp b/src/httpserver/create_webserver.hpp index 262a02e4..6813519d 100644 --- a/src/httpserver/create_webserver.hpp +++ b/src/httpserver/create_webserver.hpp @@ -25,6 +25,7 @@ #ifndef _CREATE_WEBSERVER_HPP_ #define _CREATE_WEBSERVER_HPP_ +#include #include "httpserver/http_utils.hpp" #define DEFAULT_WS_TIMEOUT 180 From 104c396106e3e83230265c21f57f8bfd73c96724 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philipp=20Cla=C3=9Fen?= Date: Thu, 8 Jan 2015 01:11:25 +0100 Subject: [PATCH 110/623] Deprecation warning fixed --- src/httpserver/http_utils.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/httpserver/http_utils.hpp b/src/httpserver/http_utils.hpp index 51b97ffd..edc19552 100644 --- a/src/httpserver/http_utils.hpp +++ b/src/httpserver/http_utils.hpp @@ -303,7 +303,7 @@ struct ip_representation const int weight() const { //variable-precision SWAR algorithm - register unsigned int x = mask; + unsigned int x = mask; x = x - ((x >> 1) & 0x55555555); x = (x & 0x33333333) + ((x >> 2) & 0x33333333); return (((x + (x >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24; From 57a23a1035442b236afe30be7577939cfc6c6115 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Tue, 13 Jan 2015 01:01:15 +0000 Subject: [PATCH 111/623] Trying to make coverage work with coveralls --- configure.ac | 16 +++++++++++++--- src/Makefile.am | 12 +++++++++++- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/configure.ac b/configure.ac index 94c63a0e..27dc8716 100644 --- a/configure.ac +++ b/configure.ac @@ -33,7 +33,7 @@ AC_CONFIG_MACRO_DIR([m4]) OLD_CXXFLAGS=$CXXFLAGS LT_INIT AC_PROG_CC -AC_PROG_CXX([clang]) +AC_PROG_CXX([clang++]) AC_PROG_LN_S CXXFLAGS=$OLD_CXXFLAGS AC_LANG([C++]) @@ -122,18 +122,25 @@ AC_MSG_RESULT([$debugit]) AM_CONDITIONAL([COND_GCOV],[test x"$debugit" = x"yes"]) +AM_LDFLAGS="-lstdc++" + if test x"$debugit" = x"yes"; then + AC_CHECK_PROG(GCOV, gcov, gcov) + AC_DEFINE([DEBUG],[],[Debug Mode]) AM_CXXFLAGS="$AM_CXXFLAGS -DDEBUG -g -Wall -Wno-uninitialized -O0" -# LDFLAGS="$LDFLAGS -fprofile-arcs -ftest-coverage" -# LIBS="-lgcov" + AM_CFLAGS="$AM_CXXFLAGS -DDEBUG -g -Wall -Wno-uninitialized -O0" + + AC_SUBST(GCOV) else AC_DEFINE([NDEBUG],[],[No-debug Mode]) AM_CXXFLAGS="$AM_CXXFLAGS -O3" + AM_CFLAGS="$AM_CXXFLAGS -O3" fi if test x"have_gnutls" = x"yes"; then AM_CXXFLAGS="$AM_CXXFLAGS -DHAVE_GNUTLS" + AM_CFLAGS="$AM_CXXFLAGS -DHAVE_GNUTLS" fi AC_MSG_CHECKING([whether to use c++0x std classes]) @@ -147,6 +154,7 @@ AC_MSG_RESULT([$cpp11]) if test x"$cpp11" = x"yes"; then AC_DEFINE([CPP11],[],[c++11 Mode]) AM_CXXFLAGS="$AM_CXXFLAGS -DUSE_CPP_11 --std=c++11" + AM_CFLAGS="$AM_CXXFLAGS -DUSE_CPP_11 --std=c++11" else AC_DEFINE([NCPP11],[],[standard Mode]) fi @@ -165,6 +173,8 @@ LDFLAGS="$LDFLAGS -version-number libhttpserver_LDF_VERSION" AC_SUBST(LHT_LIBDEPS) AC_SUBST(AM_CXXFLAGS) +AC_SUBST(AM_CFLAGS) +AC_SUBST(AM_LDFLAGS) AC_SUBST(CPPFLAGS) AC_SUBST(LIBS) AC_SUBST(LDFLAGS) diff --git a/src/Makefile.am b/src/Makefile.am index c88305f1..7c2250d3 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -22,9 +22,19 @@ lib_LTLIBRARIES = libhttpserver.la libhttpserver_la_SOURCES = string_utilities.cpp webserver.cpp http_utils.cpp http_request.cpp http_response.cpp http_resource.cpp details/comet_manager.cpp details/http_endpoint.cpp noinst_HEADERS = httpserver/string_utilities.hpp httpserver/details/modded_request.hpp httpserver/details/http_response_ptr.hpp httpserver/details/cache_entry.hpp httpserver/details/comet_manager.hpp gettext.h nobase_include_HEADERS = httpserver.hpp httpserver/create_webserver.hpp httpserver/webserver.hpp httpserver/http_utils.hpp httpserver/details/http_endpoint.hpp httpserver/http_request.hpp httpserver/http_response.hpp httpserver/http_resource.hpp httpserver/binders.hpp httpserver/event_supplier.hpp httpserver/details/event_tuple.hpp httpserver/details/http_resource_mirror.hpp httpserver/http_response_builder.hpp + AM_CXXFLAGS += -fPIC -Wall + +if COND_GCOV +AM_CFLAGS += -O0 --coverage +AM_CXXFLAGS += -O0 --coverage +AM_LDFLAGS += -O0 --coverage -lgcov +endif + libhttpserver_la_LIBADD = -lmicrohttpd -libhttpserver_la_LDFLAGS = +libhttpserver_la_CFLAGS = $(AM_CFLAGS) +libhttpserver_la_CXXFLAGS = $(AM_CXXFLAGS) +libhttpserver_la_LDFLAGS = $(AM_LDFLAGS) -no-undefined install-data-hook: (mkdir -p $(DESTDIR)$(includedir) && cd $(DESTDIR)$(includedir) && $(LN_S) -f httpserver.hpp httpserverpp) From 5db5ffaab4a66fe70d8b03a6a87a341106ede604 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Tue, 13 Jan 2015 01:27:44 +0000 Subject: [PATCH 112/623] avoid to enable coverage on darwin --- configure.ac | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/configure.ac b/configure.ac index 27dc8716..27349519 100644 --- a/configure.ac +++ b/configure.ac @@ -29,6 +29,7 @@ AC_INIT([libhttpserver], libhttpserver_PKG_VERSION, [electrictwister2000@gmail.c AM_INIT_AUTOMAKE([subdir-objects]) AC_CONFIG_HEADERS([config.h]) AC_CONFIG_MACRO_DIR([m4]) +AC_CANONICAL_HOST OLD_CXXFLAGS=$CXXFLAGS LT_INIT @@ -120,6 +121,12 @@ AC_ARG_ENABLE([debug], [debugit=no]) AC_MSG_RESULT([$debugit]) +case $host_os in + darwin* ) + debugit="no" + ;; +esac + AM_CONDITIONAL([COND_GCOV],[test x"$debugit" = x"yes"]) AM_LDFLAGS="-lstdc++" From 703d38441838e897378ff91af9cb98d17419693b Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Tue, 13 Jan 2015 02:13:47 +0000 Subject: [PATCH 113/623] avoid debug option with clang compiler --- .travis.yml | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/.travis.yml b/.travis.yml index 2c70fd56..535304ee 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,7 +2,9 @@ language: cpp compiler: - gcc - clang -# Change this to your needs +env: + - CONF_COMMAND=../configure --enable-debug; DEBUG="debug" + - CONF_COMMAND=../configure before_install: - sudo apt-get install texinfo - sudo pip install cpp-coveralls @@ -17,15 +19,12 @@ script: - ./bootstrap - mkdir build - cd build - - ../configure --enable-debug + - $(echo $CONF_COMMAND) - make - make check - # - rm -rf build - # - mkdir build - # - cd build - # - ../configure --enable-cpp11 - # - make - # - make check - # - cd .. after_success: - - coveralls --no-gcov --extension cpp --build-root src/ --exclude lib --exclude tests --gcov-options '\-lp' + - if [ $DEBUG = "debug" ]; then coveralls --no-gcov --extension cpp --build-root src/ --exclude lib --exclude tests --gcov-options '\-lp'; fi +matrix: + exclude: + - compiler: clang + env: CONF_COMMAND=../configure --enable-debug From 3739da309cc4bf820810d9445a07d6f3392926d5 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Tue, 13 Jan 2015 02:15:24 +0000 Subject: [PATCH 114/623] Solving error with travis build --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 535304ee..e43fbdf2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -27,4 +27,4 @@ after_success: matrix: exclude: - compiler: clang - env: CONF_COMMAND=../configure --enable-debug + env: CONF_COMMAND=../configure --enable-debug; DEBUG="debug" From 6e820abb7a8b1ee1799d0c1707649ff832e5c72f Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Tue, 13 Jan 2015 02:26:51 +0000 Subject: [PATCH 115/623] Trying to report coverage data to coveralls --- .travis.yml | 2 +- ci-report-coverage | 12 ++++++++++++ coveralls-debug | 32 ++++++++++++++++++++++++++++++++ 3 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 ci-report-coverage create mode 100644 coveralls-debug diff --git a/.travis.yml b/.travis.yml index e43fbdf2..6fb6a45d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,7 +23,7 @@ script: - make - make check after_success: - - if [ $DEBUG = "debug" ]; then coveralls --no-gcov --extension cpp --build-root src/ --exclude lib --exclude tests --gcov-options '\-lp'; fi + - if [ $DEBUG = "debug" ]; then ./ci-report-coverage; fi matrix: exclude: - compiler: clang diff --git a/ci-report-coverage b/ci-report-coverage new file mode 100644 index 00000000..99a1a520 --- /dev/null +++ b/ci-report-coverage @@ -0,0 +1,12 @@ +#!/bin/bash + +export EXCLUDE_COVERAGE="--exclude .libs" + +gcov -v +gcovr -r . + +coveralls $EXCLUDE_COVERAGE --gcov-options '\-lp' --dump coveralls.json + +./coveralls-debug + +coveralls $EXCLUDE_COVERAGE --gcov-options '\-lp' diff --git a/coveralls-debug b/coveralls-debug new file mode 100644 index 00000000..11a32b1d --- /dev/null +++ b/coveralls-debug @@ -0,0 +1,32 @@ +#!/usr/bin/perl + +use strict; +use warnings; +use JSON; +use File::Slurp; +my $json = JSON->new; +my $file = read_file('coveralls.json', { binmode => ':utf8' }); +my $rv = $json->decode($file); +my $sources = $rv->{'source_files'}; +print STDERR join ", ", keys %{$rv}, "\n"; +foreach my $source (sort { + $a->{'name'} cmp $b->{'name'} +} @{$sources}) +{ + my $sum = 0; + my $undefs = 0; + my $coverages = $source->{'coverage'}; + foreach my $coverage (@{$coverages}) + { + if (defined $coverage) + { $sum += $coverage } + else { $undefs ++; } + } + if ($sum > 0) + { + print STDERR $source->{'name'}; + print STDERR " [sum: $sum]"; + print STDERR " [undefs: $undefs]"; + print STDERR "\n"; + } +} From ffbd66ea4068a039b4b197b609574744a05900ee Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Tue, 13 Jan 2015 02:32:31 +0000 Subject: [PATCH 116/623] Solving path of coveralls scripts --- .travis.yml | 2 +- ci-report-coverage | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 6fb6a45d..6f421f02 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,7 +23,7 @@ script: - make - make check after_success: - - if [ $DEBUG = "debug" ]; then ./ci-report-coverage; fi + - if [ $DEBUG = "debug" ]; then ../ci-report-coverage; fi matrix: exclude: - compiler: clang diff --git a/ci-report-coverage b/ci-report-coverage index 99a1a520..d5f40703 100644 --- a/ci-report-coverage +++ b/ci-report-coverage @@ -7,6 +7,6 @@ gcovr -r . coveralls $EXCLUDE_COVERAGE --gcov-options '\-lp' --dump coveralls.json -./coveralls-debug +../coveralls-debug coveralls $EXCLUDE_COVERAGE --gcov-options '\-lp' From ca883e31e0e40d078fb190d0449e0b48aa1ba14d Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Tue, 13 Jan 2015 02:36:53 +0000 Subject: [PATCH 117/623] changed permissions on coveralls executables --- ci-report-coverage | 0 coveralls-debug | 0 2 files changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 ci-report-coverage mode change 100644 => 100755 coveralls-debug diff --git a/ci-report-coverage b/ci-report-coverage old mode 100644 new mode 100755 diff --git a/coveralls-debug b/coveralls-debug old mode 100644 new mode 100755 From 643663acfe4ae1f6563292498621d67d8e85d20e Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Tue, 13 Jan 2015 02:42:01 +0000 Subject: [PATCH 118/623] Added coveralls dependencies --- .travis.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.travis.yml b/.travis.yml index 6f421f02..f959b7d9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,6 +8,10 @@ env: before_install: - sudo apt-get install texinfo - sudo pip install cpp-coveralls + - sudo pip install gcovr + - sudo apt-get install libjson-perl + - sudo apt-get install libjson-xs-perl + - sudo apt-get install libfile-slurp-perl - wget ftp://ftp.gnu.org/gnu/libmicrohttpd/libmicrohttpd-0.9.37.tar.gz - tar -xvzf libmicrohttpd-0.9.37.tar.gz - cd libmicrohttpd-0.9.37 From 34fc28d970b3e8cc82bbb18d94a2d1419736c09d Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Tue, 13 Jan 2015 02:52:42 +0000 Subject: [PATCH 119/623] changed gcovr root dir --- ci-report-coverage | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci-report-coverage b/ci-report-coverage index d5f40703..185907d1 100755 --- a/ci-report-coverage +++ b/ci-report-coverage @@ -3,7 +3,7 @@ export EXCLUDE_COVERAGE="--exclude .libs" gcov -v -gcovr -r . +gcovr -r ../ coveralls $EXCLUDE_COVERAGE --gcov-options '\-lp' --dump coveralls.json From 3c34d4106248c8c22a6d09528bf95856d5c76153 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Tue, 13 Jan 2015 02:57:56 +0000 Subject: [PATCH 120/623] using src dir as a root for gcov --- ci-report-coverage | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci-report-coverage b/ci-report-coverage index 185907d1..6ffc8ca4 100755 --- a/ci-report-coverage +++ b/ci-report-coverage @@ -3,7 +3,7 @@ export EXCLUDE_COVERAGE="--exclude .libs" gcov -v -gcovr -r ../ +gcovr -r ../src coveralls $EXCLUDE_COVERAGE --gcov-options '\-lp' --dump coveralls.json From 8c4742eb3a95a0206314f8179d6dd31b431b755f Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Tue, 13 Jan 2015 03:08:41 +0000 Subject: [PATCH 121/623] gcov dir src in build --- ci-report-coverage | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci-report-coverage b/ci-report-coverage index 6ffc8ca4..0d240ee8 100755 --- a/ci-report-coverage +++ b/ci-report-coverage @@ -3,7 +3,7 @@ export EXCLUDE_COVERAGE="--exclude .libs" gcov -v -gcovr -r ../src +gcovr -r src coveralls $EXCLUDE_COVERAGE --gcov-options '\-lp' --dump coveralls.json From 2deff67e2a0b07b4ca6cce12b771a1c488843ab7 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Tue, 13 Jan 2015 03:15:10 +0000 Subject: [PATCH 122/623] exporting cond_gcov variable from configure.ac --- configure.ac | 1 + 1 file changed, 1 insertion(+) diff --git a/configure.ac b/configure.ac index 27349519..b086e2ee 100644 --- a/configure.ac +++ b/configure.ac @@ -187,6 +187,7 @@ AC_SUBST(LIBS) AC_SUBST(LDFLAGS) AC_SUBST(EXT_LIB_PATH) AC_SUBST(EXT_LIBS) +AC_SUBST(COND_GCOV) AC_OUTPUT( libhttpserver.pc From 2ec7696663e4dede0b22612c571a05d7cedd52a5 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Tue, 13 Jan 2015 03:23:35 +0000 Subject: [PATCH 123/623] using a better script to trigger travis ci --- .travis.yml | 8 ++++---- ci-configure | 3 +++ 2 files changed, 7 insertions(+), 4 deletions(-) create mode 100755 ci-configure diff --git a/.travis.yml b/.travis.yml index f959b7d9..a324c2b7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,8 +3,8 @@ compiler: - gcc - clang env: - - CONF_COMMAND=../configure --enable-debug; DEBUG="debug" - - CONF_COMMAND=../configure + - DEBUG="debug" + - DEBUG="nodebug" before_install: - sudo apt-get install texinfo - sudo pip install cpp-coveralls @@ -23,7 +23,7 @@ script: - ./bootstrap - mkdir build - cd build - - $(echo $CONF_COMMAND) + - ../ci-configure - make - make check after_success: @@ -31,4 +31,4 @@ after_success: matrix: exclude: - compiler: clang - env: CONF_COMMAND=../configure --enable-debug; DEBUG="debug" + env: DEBUG="debug" diff --git a/ci-configure b/ci-configure new file mode 100755 index 00000000..47aaaa8a --- /dev/null +++ b/ci-configure @@ -0,0 +1,3 @@ +#!/bin/bash + +if [ $DEBUG = "debug" ]; then ../configure --enable-debug; else ../configure; fi From 3fd2f4b6dc521a36a6bd0ab6000c780ce3c447a4 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Tue, 13 Jan 2015 03:40:17 +0000 Subject: [PATCH 124/623] inlining options into travis yml file --- .travis.yml | 2 +- ci-configure | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) delete mode 100755 ci-configure diff --git a/.travis.yml b/.travis.yml index a324c2b7..4a6df1e0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,7 +23,7 @@ script: - ./bootstrap - mkdir build - cd build - - ../ci-configure + - if [ $DEBUG = "debug" ]; then ../configure --enable-debug; else ../configure; fi - make - make check after_success: diff --git a/ci-configure b/ci-configure deleted file mode 100755 index 47aaaa8a..00000000 --- a/ci-configure +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash - -if [ $DEBUG = "debug" ]; then ../configure --enable-debug; else ../configure; fi From d326ee7b3fd42f3c63c2255270a1d70a583935ee Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Tue, 13 Jan 2015 03:46:22 +0000 Subject: [PATCH 125/623] changing gcovr root to be the same as build dir --- ci-report-coverage | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci-report-coverage b/ci-report-coverage index 0d240ee8..d5f40703 100755 --- a/ci-report-coverage +++ b/ci-report-coverage @@ -3,7 +3,7 @@ export EXCLUDE_COVERAGE="--exclude .libs" gcov -v -gcovr -r src +gcovr -r . coveralls $EXCLUDE_COVERAGE --gcov-options '\-lp' --dump coveralls.json From 61aad4c1c240ffb22d5e88d0bc710376da2e700c Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Tue, 13 Jan 2015 03:50:12 +0000 Subject: [PATCH 126/623] changed root for gcovr to be the root of the project --- ci-report-coverage | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci-report-coverage b/ci-report-coverage index d5f40703..185907d1 100755 --- a/ci-report-coverage +++ b/ci-report-coverage @@ -3,7 +3,7 @@ export EXCLUDE_COVERAGE="--exclude .libs" gcov -v -gcovr -r . +gcovr -r ../ coveralls $EXCLUDE_COVERAGE --gcov-options '\-lp' --dump coveralls.json From 294d8a41ac09f67e8995c345ea2d19077339d2ba Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Tue, 13 Jan 2015 04:25:13 +0000 Subject: [PATCH 127/623] forcing gcovr to find src files --- .travis.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.travis.yml b/.travis.yml index 4a6df1e0..5618a3cd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -27,6 +27,9 @@ script: - make - make check after_success: + - cp ../src/* ./src/ + - cp ../src/details/* ./src/details/ + - cp ../src/httpserver/* ./src/httpserver/ - if [ $DEBUG = "debug" ]; then ../ci-report-coverage; fi matrix: exclude: From 10a4ddc3356d5cddd6b6ff2f5bc718fc8f77f762 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Tue, 13 Jan 2015 04:28:59 +0000 Subject: [PATCH 128/623] copying files for real this round --- .travis.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 5618a3cd..add04dab 100644 --- a/.travis.yml +++ b/.travis.yml @@ -27,9 +27,9 @@ script: - make - make check after_success: - - cp ../src/* ./src/ - - cp ../src/details/* ./src/details/ - - cp ../src/httpserver/* ./src/httpserver/ + - cp -R ../src/* ./src/ + - cp -R ../src/details/* ./src/details/ + - cp -R ../src/httpserver/* ./src/httpserver/ - if [ $DEBUG = "debug" ]; then ../ci-report-coverage; fi matrix: exclude: From 0e6bd48c5a4763da8251817c03f0781770982f8d Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 18 Jan 2015 02:22:28 +0000 Subject: [PATCH 129/623] Avoid server to crash on empty resources. This prevents the server to crash in case of a resource not building any response. --- src/webserver.cpp | 9 ++++++--- test/integ/basic.cpp | 27 +++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/src/webserver.cpp b/src/webserver.cpp index e5122cd0..9502422c 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -357,11 +357,11 @@ MHD_socket create_socket (int domain, int type, int protocol) { int sock_cloexec = SOCK_CLOEXEC; int ctype = SOCK_STREAM | sock_cloexec; - + /* use SOCK_STREAM rather than ai_socktype: some getaddrinfo * implementations do not set ai_socktype, e.g. RHL6.2. */ MHD_socket fd = socket(domain, ctype, protocol); - + #ifdef _WINDOWS if (fd == INVALID_SOCKET) #else @@ -521,7 +521,7 @@ bool webserver::start(bool blocking) } #ifdef _WINDOWS unsigned long ioarg = 1; - ioctlsocket(bind_socket, FIONBIO, &ioarg); + ioctlsocket(bind_socket, FIONBIO, &ioarg); #else int flags = fcntl (bind_socket, F_GETFL); flags |= O_NONBLOCK; @@ -1132,7 +1132,10 @@ int webserver::finalize_answer( try { if(hrm->is_allowed(method)) + { ((hrm)->*(mr->callback))(*mr->dhr, &dhrs); + if (dhrs == 0x0) internal_error_page(&dhrs, mr); + } else { method_not_allowed_page(&dhrs, mr); diff --git a/test/integ/basic.cpp b/test/integ/basic.cpp index c61c7c0c..1ddb4297 100644 --- a/test/integ/basic.cpp +++ b/test/integ/basic.cpp @@ -132,6 +132,14 @@ class nok_resource : public http_resource } }; +class no_response_resource : public http_resource +{ + public: + void render_GET(const http_request& req, http_response** res) + { + } +}; + LT_BEGIN_SUITE(basic_suite) webserver* ws; @@ -373,6 +381,25 @@ LT_BEGIN_AUTO_TEST(basic_suite, empty_arg) curl_easy_cleanup(curl); LT_END_AUTO_TEST(empty_arg) +LT_BEGIN_AUTO_TEST(basic_suite, no_response) + no_response_resource* resource = new no_response_resource(); + ws->register_resource("base", resource); + curl_global_init(CURL_GLOBAL_ALL); + std::string s; + CURL* curl; + CURLcode res; + + curl = curl_easy_init(); + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + long http_code = 0; + curl_easy_getinfo (curl, CURLINFO_RESPONSE_CODE, &http_code); + LT_ASSERT_EQ(http_code, 500); + curl_easy_cleanup(curl); +LT_END_AUTO_TEST(no_response) + LT_BEGIN_AUTO_TEST_ENV() AUTORUN_TESTS() LT_END_AUTO_TEST_ENV() From 11a9aa612339e85e95e7e456004838f91b5bf9ad Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 18 Jan 2015 02:32:43 +0000 Subject: [PATCH 130/623] Removing useless partial gcov solutions --- test/Makefile.am | 4 ---- test/gcov | 7 ------- test/rmgcda | 4 ---- 3 files changed, 15 deletions(-) delete mode 100755 test/gcov delete mode 100755 test/rmgcda diff --git a/test/Makefile.am b/test/Makefile.am index 102d4fe6..e240245d 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -29,8 +29,4 @@ http_utils_SOURCES = unit/http_utils_test.cpp noinst_HEADERS = littletest.hpp AM_CXXFLAGS += -lcurl -Wall -fPIC -if COND_GCOV -TESTS = rmgcda $(check_PROGRAMS) gcov -else TESTS = $(check_PROGRAMS) -endif diff --git a/test/gcov b/test/gcov deleted file mode 100755 index d01192d8..00000000 --- a/test/gcov +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/sh - -for srcfile in ../src/*.cpp -do - ( gcov -r $srcfile || gcov $srcfile ) > /dev/null 2> /dev/null -done -exit 0 diff --git a/test/rmgcda b/test/rmgcda deleted file mode 100755 index 571e9fa4..00000000 --- a/test/rmgcda +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/sh - -rm -f *.gcov *.gcda -exit 0 From dc3d74ed11f9f3182d3dd6c9dad7a22c87a925a1 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 18 Jan 2015 03:08:56 +0000 Subject: [PATCH 131/623] Changing gcov root to the build root only --- ci-report-coverage | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci-report-coverage b/ci-report-coverage index 185907d1..d5f40703 100755 --- a/ci-report-coverage +++ b/ci-report-coverage @@ -3,7 +3,7 @@ export EXCLUDE_COVERAGE="--exclude .libs" gcov -v -gcovr -r ../ +gcovr -r . coveralls $EXCLUDE_COVERAGE --gcov-options '\-lp' --dump coveralls.json From f42f13122fda8c779e344854cea1f25bb8a4c0f3 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 18 Jan 2015 03:41:04 +0000 Subject: [PATCH 132/623] Reporting phases of coveralls analysis --- ci-report-coverage | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ci-report-coverage b/ci-report-coverage index d5f40703..95d35bc5 100755 --- a/ci-report-coverage +++ b/ci-report-coverage @@ -3,10 +3,15 @@ export EXCLUDE_COVERAGE="--exclude .libs" gcov -v + +echo "Running gcovr" gcovr -r . +echo "Creating coveralls json report" coveralls $EXCLUDE_COVERAGE --gcov-options '\-lp' --dump coveralls.json +echo "Analyzing coveralls report" ../coveralls-debug +echo "Sending coveralls json report" coveralls $EXCLUDE_COVERAGE --gcov-options '\-lp' From bede797bbcf7e326244d5a4889885f827aa10865 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 18 Jan 2015 03:50:12 +0000 Subject: [PATCH 133/623] Simplifying coveralls sender script --- ci-report-coverage | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/ci-report-coverage b/ci-report-coverage index 95d35bc5..95c49286 100755 --- a/ci-report-coverage +++ b/ci-report-coverage @@ -2,16 +2,5 @@ export EXCLUDE_COVERAGE="--exclude .libs" -gcov -v - -echo "Running gcovr" -gcovr -r . - -echo "Creating coveralls json report" -coveralls $EXCLUDE_COVERAGE --gcov-options '\-lp' --dump coveralls.json - -echo "Analyzing coveralls report" -../coveralls-debug - echo "Sending coveralls json report" coveralls $EXCLUDE_COVERAGE --gcov-options '\-lp' From 620a4692515ab46901cbd648d632fcb03871d2fd Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 18 Jan 2015 04:03:33 +0000 Subject: [PATCH 134/623] Using compiler path for coveralls --- ci-report-coverage | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci-report-coverage b/ci-report-coverage index 95c49286..7ff4a596 100755 --- a/ci-report-coverage +++ b/ci-report-coverage @@ -3,4 +3,4 @@ export EXCLUDE_COVERAGE="--exclude .libs" echo "Sending coveralls json report" -coveralls $EXCLUDE_COVERAGE --gcov-options '\-lp' +coveralls $EXCLUDE_COVERAGE --gcov-options '\-lp' -b . From 91e41a8a4f273f94d8f5b27ced6aaa3aa095df72 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 18 Jan 2015 04:10:12 +0000 Subject: [PATCH 135/623] Build src path for coveralls --- ci-report-coverage | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci-report-coverage b/ci-report-coverage index 7ff4a596..9e86c348 100755 --- a/ci-report-coverage +++ b/ci-report-coverage @@ -3,4 +3,4 @@ export EXCLUDE_COVERAGE="--exclude .libs" echo "Sending coveralls json report" -coveralls $EXCLUDE_COVERAGE --gcov-options '\-lp' -b . +coveralls $EXCLUDE_COVERAGE --gcov-options '\-lp' -b ./src From db2ecdd107cfb8514e3f6cdf1a5286482b7c018a Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 18 Jan 2015 04:28:01 +0000 Subject: [PATCH 136/623] Search for gcda files on travis host --- ci-report-coverage | 1 + 1 file changed, 1 insertion(+) diff --git a/ci-report-coverage b/ci-report-coverage index 9e86c348..89bcfd72 100755 --- a/ci-report-coverage +++ b/ci-report-coverage @@ -2,5 +2,6 @@ export EXCLUDE_COVERAGE="--exclude .libs" +find ../ -name *.gcda echo "Sending coveralls json report" coveralls $EXCLUDE_COVERAGE --gcov-options '\-lp' -b ./src From 22b9b72c8c99f304b2c7cadc602d579b315e1109 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 18 Jan 2015 04:33:50 +0000 Subject: [PATCH 137/623] copying gcda files where coderalls expects them --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index add04dab..ff17ea87 100644 --- a/.travis.yml +++ b/.travis.yml @@ -27,6 +27,7 @@ script: - make - make check after_success: + - cp src/.libs/*.gcda ./src/ - cp -R ../src/* ./src/ - cp -R ../src/details/* ./src/details/ - cp -R ../src/httpserver/* ./src/httpserver/ From 64ac7f2e3ae710b71dbc27cefc5bc978d6fea9b4 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 18 Jan 2015 05:15:05 +0000 Subject: [PATCH 138/623] calculating coverage from test --- .travis.yml | 1 - ci-report-coverage | 2 +- src/Makefile.am | 6 ------ test/Makefile.am | 6 ++++++ 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/.travis.yml b/.travis.yml index ff17ea87..add04dab 100644 --- a/.travis.yml +++ b/.travis.yml @@ -27,7 +27,6 @@ script: - make - make check after_success: - - cp src/.libs/*.gcda ./src/ - cp -R ../src/* ./src/ - cp -R ../src/details/* ./src/details/ - cp -R ../src/httpserver/* ./src/httpserver/ diff --git a/ci-report-coverage b/ci-report-coverage index 89bcfd72..b417b959 100755 --- a/ci-report-coverage +++ b/ci-report-coverage @@ -4,4 +4,4 @@ export EXCLUDE_COVERAGE="--exclude .libs" find ../ -name *.gcda echo "Sending coveralls json report" -coveralls $EXCLUDE_COVERAGE --gcov-options '\-lp' -b ./src +coveralls $EXCLUDE_COVERAGE --gcov-options '\-lp' diff --git a/src/Makefile.am b/src/Makefile.am index 7c2250d3..7215aaa7 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -25,12 +25,6 @@ nobase_include_HEADERS = httpserver.hpp httpserver/create_webserver.hpp httpserv AM_CXXFLAGS += -fPIC -Wall -if COND_GCOV -AM_CFLAGS += -O0 --coverage -AM_CXXFLAGS += -O0 --coverage -AM_LDFLAGS += -O0 --coverage -lgcov -endif - libhttpserver_la_LIBADD = -lmicrohttpd libhttpserver_la_CFLAGS = $(AM_CFLAGS) libhttpserver_la_CXXFLAGS = $(AM_CXXFLAGS) diff --git a/test/Makefile.am b/test/Makefile.am index e240245d..fd6042a8 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -29,4 +29,10 @@ http_utils_SOURCES = unit/http_utils_test.cpp noinst_HEADERS = littletest.hpp AM_CXXFLAGS += -lcurl -Wall -fPIC +if COND_GCOV +AM_CFLAGS += -O0 --coverage +AM_CXXFLAGS += -O0 --coverage +AM_LDFLAGS += -O0 --coverage -lgcov +endif + TESTS = $(check_PROGRAMS) From d8e3b9b1bffeb3c1d90f8812d4d5150ef74a21df Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 18 Jan 2015 05:21:17 +0000 Subject: [PATCH 139/623] copying src to dest --- .travis.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.travis.yml b/.travis.yml index add04dab..26c7fcd2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -30,6 +30,9 @@ after_success: - cp -R ../src/* ./src/ - cp -R ../src/details/* ./src/details/ - cp -R ../src/httpserver/* ./src/httpserver/ + - cp -R ../test/* .test/ + - cp -R ../test/integ/* .test/integ/ + - cp -R ../test/unit/* .test/unit/ - if [ $DEBUG = "debug" ]; then ../ci-report-coverage; fi matrix: exclude: From e1e03124a0fabd678ac2fdc3fa024537c25823f9 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 18 Jan 2015 05:24:17 +0000 Subject: [PATCH 140/623] Adjusted path (being sleepy does not help here) --- .travis.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 26c7fcd2..f1e12db3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -30,9 +30,9 @@ after_success: - cp -R ../src/* ./src/ - cp -R ../src/details/* ./src/details/ - cp -R ../src/httpserver/* ./src/httpserver/ - - cp -R ../test/* .test/ - - cp -R ../test/integ/* .test/integ/ - - cp -R ../test/unit/* .test/unit/ + - cp -R ../test/* ./test/ + - cp -R ../test/integ/* ./test/integ/ + - cp -R ../test/unit/* ./test/unit/ - if [ $DEBUG = "debug" ]; then ../ci-report-coverage; fi matrix: exclude: From 8aa8811e7166817b32b6773848bcd2b708249e44 Mon Sep 17 00:00:00 2001 From: apavlov at home Date: Sun, 18 Jan 2015 18:25:15 +0500 Subject: [PATCH 141/623] Remove useless code --- src/webserver.cpp | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/webserver.cpp b/src/webserver.cpp index 9502422c..c385cb88 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -534,12 +534,6 @@ bool webserver::start(bool blocking) iov.push_back(gen(MHD_OPTION_END, 0, NULL )); - struct MHD_OptionItem ops[iov.size()]; - for(unsigned int i = 0; i < iov.size(); i++) - { - ops[i] = iov[i]; - } - int start_conf = start_method; if(use_ssl) start_conf |= MHD_USE_SSL; @@ -563,7 +557,7 @@ bool webserver::start(bool blocking) ( start_conf, this->port, &policy_callback, this, &answer_to_connection, this, MHD_OPTION_ARRAY, - ops, MHD_OPTION_END + &iov[0], MHD_OPTION_END ); if(NULL == daemon) { @@ -595,7 +589,7 @@ bool webserver::start(bool blocking) ( start_conf, this->port, &policy_callback, this, &answer_to_connection, this, MHD_OPTION_ARRAY, - ops, MHD_OPTION_END + &iov[0], MHD_OPTION_END ); if(NULL == daemon) { From 9bae50c80ddb88e11cfb923075e0abf3e515e4eb Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 18 Jan 2015 13:28:07 +0000 Subject: [PATCH 142/623] Including shared lib in gcov analysis --- .travis.yml | 9 +-------- ci-report-coverage | 10 ++++++++++ src/Makefile.am | 6 ++++++ 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/.travis.yml b/.travis.yml index f1e12db3..3073264a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -24,15 +24,8 @@ script: - mkdir build - cd build - if [ $DEBUG = "debug" ]; then ../configure --enable-debug; else ../configure; fi - - make - - make check + - if [ $DEBUG = "debug" ]; then make check; else make; make check; fi after_success: - - cp -R ../src/* ./src/ - - cp -R ../src/details/* ./src/details/ - - cp -R ../src/httpserver/* ./src/httpserver/ - - cp -R ../test/* ./test/ - - cp -R ../test/integ/* ./test/integ/ - - cp -R ../test/unit/* ./test/unit/ - if [ $DEBUG = "debug" ]; then ../ci-report-coverage; fi matrix: exclude: diff --git a/ci-report-coverage b/ci-report-coverage index b417b959..6667d983 100755 --- a/ci-report-coverage +++ b/ci-report-coverage @@ -3,5 +3,15 @@ export EXCLUDE_COVERAGE="--exclude .libs" find ../ -name *.gcda + +echo "Copying files to the correct locations for correct gcov analysis" +cp -R ../src/* ./src/ +cp -R ../src/details/* ./src/details/ +cp -R ../src/httpserver/* ./src/httpserver/ +cp -R ./src/.libs/*.gcda ./src/ +cp -R ../test/* ./test/ +cp -R ../test/integ/* ./test/integ/ +cp -R ../test/unit/* ./test/unit/ + echo "Sending coveralls json report" coveralls $EXCLUDE_COVERAGE --gcov-options '\-lp' diff --git a/src/Makefile.am b/src/Makefile.am index 7215aaa7..7c2250d3 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -25,6 +25,12 @@ nobase_include_HEADERS = httpserver.hpp httpserver/create_webserver.hpp httpserv AM_CXXFLAGS += -fPIC -Wall +if COND_GCOV +AM_CFLAGS += -O0 --coverage +AM_CXXFLAGS += -O0 --coverage +AM_LDFLAGS += -O0 --coverage -lgcov +endif + libhttpserver_la_LIBADD = -lmicrohttpd libhttpserver_la_CFLAGS = $(AM_CFLAGS) libhttpserver_la_CXXFLAGS = $(AM_CXXFLAGS) From c67a0c3dfcbde464bb8c386031c733b0d1c09d31 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 18 Jan 2015 13:45:41 +0000 Subject: [PATCH 143/623] Avoid inlining when calculating coverage --- src/Makefile.am | 6 +++--- test/Makefile.am | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Makefile.am b/src/Makefile.am index 7c2250d3..6067c317 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -26,9 +26,9 @@ nobase_include_HEADERS = httpserver.hpp httpserver/create_webserver.hpp httpserv AM_CXXFLAGS += -fPIC -Wall if COND_GCOV -AM_CFLAGS += -O0 --coverage -AM_CXXFLAGS += -O0 --coverage -AM_LDFLAGS += -O0 --coverage -lgcov +AM_CFLAGS += -O0 --coverage --no-inline +AM_CXXFLAGS += -O0 --coverage --no-inline +AM_LDFLAGS += -O0 --coverage -lgcov --no-inline endif libhttpserver_la_LIBADD = -lmicrohttpd diff --git a/test/Makefile.am b/test/Makefile.am index fd6042a8..c97f4d02 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -30,9 +30,9 @@ noinst_HEADERS = littletest.hpp AM_CXXFLAGS += -lcurl -Wall -fPIC if COND_GCOV -AM_CFLAGS += -O0 --coverage -AM_CXXFLAGS += -O0 --coverage -AM_LDFLAGS += -O0 --coverage -lgcov +AM_CFLAGS += -O0 --coverage --no-inline +AM_CXXFLAGS += -O0 --coverage --no-inline +AM_LDFLAGS += -O0 --coverage -lgcov --no-inline endif TESTS = $(check_PROGRAMS) From a042759a1a09f03b07f095f78189fa4ecc448c2a Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 18 Jan 2015 14:21:44 +0000 Subject: [PATCH 144/623] coverall-cpp seems able to work with libtool --- ci-report-coverage | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/ci-report-coverage b/ci-report-coverage index 6667d983..1b017d6c 100755 --- a/ci-report-coverage +++ b/ci-report-coverage @@ -1,17 +1,14 @@ #!/bin/bash -export EXCLUDE_COVERAGE="--exclude .libs" - find ../ -name *.gcda echo "Copying files to the correct locations for correct gcov analysis" cp -R ../src/* ./src/ cp -R ../src/details/* ./src/details/ cp -R ../src/httpserver/* ./src/httpserver/ -cp -R ./src/.libs/*.gcda ./src/ cp -R ../test/* ./test/ cp -R ../test/integ/* ./test/integ/ cp -R ../test/unit/* ./test/unit/ echo "Sending coveralls json report" -coveralls $EXCLUDE_COVERAGE --gcov-options '\-lp' +coveralls --gcov-options '\-lp' From f1885276592114016074c57efd1867868fbb8c64 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 18 Jan 2015 14:40:56 +0000 Subject: [PATCH 145/623] Avoid using libtool when calculating coverage --- .travis.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 3073264a..ba2f2005 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,8 +23,9 @@ script: - ./bootstrap - mkdir build - cd build - - if [ $DEBUG = "debug" ]; then ../configure --enable-debug; else ../configure; fi - - if [ $DEBUG = "debug" ]; then make check; else make; make check; fi + - if [ $DEBUG = "debug" ]; then ../configure --enable-debug --disable-shared; else ../configure; fi + - make + - make check after_success: - if [ $DEBUG = "debug" ]; then ../ci-report-coverage; fi matrix: From faef2b9d5581ee58696a35b29d0b0859eac4702f Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 18 Jan 2015 15:29:51 +0000 Subject: [PATCH 146/623] Updating test library to the latest version --- test/littletest.hpp | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/test/littletest.hpp b/test/littletest.hpp index 36490ac6..681d47f4 100644 --- a/test/littletest.hpp +++ b/test/littletest.hpp @@ -52,8 +52,6 @@ littletest::auto_test_runner((*__lt_autorun_it__)); \ int __lt_result__ = littletest::auto_test_runner(); -#define LT_TEST(__lt_name__) __lt_name__ ## _obj - #define LT_CREATE_RUNNER(__lt_suite_name__, __lt_runner_name__) \ std::cout << "** Initializing Runner \"" << #__lt_runner_name__ << "\" **" << std::endl; \ littletest::test_runner __lt_runner_name__ @@ -70,9 +68,9 @@ #define LT_CHECKPOINT() __lt_tr__->set_checkpoint(__FILE__, __LINE__) #define LT_BEGIN_TEST(__lt_suite_name__, __lt_test_name__) \ - struct __lt_test_name__ : public __lt_suite_name__, littletest::test<__lt_test_name__> \ + struct __lt_test_name__ ## _class: public __lt_suite_name__, littletest::test<__lt_test_name__ ## _class> \ { \ - __lt_test_name__() \ + __lt_test_name__ ## _class() \ { \ __lt_name__ = #__lt_test_name__; \ littletest::auto_test_vector.push_back(this); \ @@ -83,7 +81,7 @@ #define LT_END_TEST(__lt_test_name__) \ } \ }; \ - __lt_test_name__ __lt_test_name__ ## _obj; \ + __lt_test_name__ ## _class __lt_test_name__; \ #define LT_BEGIN_AUTO_TEST(__lt_suite_name__, __lt_test_name__) LT_BEGIN_TEST(__lt_suite_name__, __lt_test_name__) @@ -348,7 +346,7 @@ class test_base; std::vector auto_test_vector; -struct test_runner +class test_runner { public: test_runner() : @@ -377,6 +375,12 @@ struct test_runner return *this; } + template + test_runner& operator()(test_impl& t) + { + return run(&t); + } + template test_runner& operator()(test_impl* t) { @@ -403,7 +407,7 @@ struct test_runner std::cout << (failures_counter + success_counter) << " checks" << std::endl; std::cout << "-> " << success_counter << " successes" << std::endl; std::cout << "-> " << failures_counter << " failures" << std::endl; - std::cout << "Total run time: " << total_time << std::endl; + std::cout << "Total run time: " << total_time << " ms"<< std::endl; std::cout << "Total time spent in tests: " << good_time_total << " ms" << std::endl; std::cout << "Average set up time: " << (total_set_up_time / test_counter) << " ms" << std::endl; std::cout << "Average tear down time: " << (total_tear_down_time / test_counter) << " ms" << std::endl; @@ -553,7 +557,7 @@ class test : public test_base tr->add_good_time(test_duration); - std::cout << "- Time spent during \"" << static_cast(this)->__lt_name__ << "\": " << test_duration << std::endl; + std::cout << "- Time spent during \"" << static_cast(this)->__lt_name__ << "\": " << test_duration << " ms"<< std::endl; try { @@ -579,7 +583,7 @@ class test : public test_base test() { } test(const test& t) { } - friend struct test_runner; + friend class test_runner; }; }; From f2078d8c9510384a434fc002352308a343be99c7 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 18 Jan 2015 19:20:15 +0000 Subject: [PATCH 147/623] enabling debug on mac os x --- configure.ac | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/configure.ac b/configure.ac index b086e2ee..147c191c 100644 --- a/configure.ac +++ b/configure.ac @@ -121,30 +121,32 @@ AC_ARG_ENABLE([debug], [debugit=no]) AC_MSG_RESULT([$debugit]) -case $host_os in - darwin* ) - debugit="no" - ;; -esac - -AM_CONDITIONAL([COND_GCOV],[test x"$debugit" = x"yes"]) - AM_LDFLAGS="-lstdc++" if test x"$debugit" = x"yes"; then - AC_CHECK_PROG(GCOV, gcov, gcov) + case $host_os in + darwin* ) + echo "Coverage not supported on OSX" + cond_gcov="no" + ;; + *) + AC_CHECK_PROG(GCOV, gcov, gcov) + cond_gcov="yes" + ;; + esac AC_DEFINE([DEBUG],[],[Debug Mode]) AM_CXXFLAGS="$AM_CXXFLAGS -DDEBUG -g -Wall -Wno-uninitialized -O0" AM_CFLAGS="$AM_CXXFLAGS -DDEBUG -g -Wall -Wno-uninitialized -O0" - - AC_SUBST(GCOV) else AC_DEFINE([NDEBUG],[],[No-debug Mode]) AM_CXXFLAGS="$AM_CXXFLAGS -O3" AM_CFLAGS="$AM_CXXFLAGS -O3" fi +AM_CONDITIONAL([COND_GCOV],[test x"$cond_gcov" = x"yes"]) +AC_SUBST(COND_GCOV) + if test x"have_gnutls" = x"yes"; then AM_CXXFLAGS="$AM_CXXFLAGS -DHAVE_GNUTLS" AM_CFLAGS="$AM_CXXFLAGS -DHAVE_GNUTLS" @@ -187,7 +189,6 @@ AC_SUBST(LIBS) AC_SUBST(LDFLAGS) AC_SUBST(EXT_LIB_PATH) AC_SUBST(EXT_LIBS) -AC_SUBST(COND_GCOV) AC_OUTPUT( libhttpserver.pc From 38698c397fd93f178b5298233ab78a4213344463 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Tue, 3 Feb 2015 01:58:10 +0000 Subject: [PATCH 148/623] Resolve self-assignment bug caused by copy-paste --- src/httpserver/http_request.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/httpserver/http_request.hpp b/src/httpserver/http_request.hpp index 90def576..1a03978c 100644 --- a/src/httpserver/http_request.hpp +++ b/src/httpserver/http_request.hpp @@ -357,7 +357,7 @@ class http_request int nonce_timeout, bool& reload_nonce ) const; - friend std::ostream &operator<< (std::ostream &os, const http_request &r); + friend std::ostream &operator<< (std::ostream &os, const http_request &r); private: /** * Default constructor of the class. It is a specific responsibility of apis to initialize this type of objects. @@ -572,7 +572,7 @@ class http_request { this->user = user; } - void set_digested_user(const std::string& user) + void set_digested_user(const std::string& digested_user) { this->digested_user = digested_user; } From 27a60128bf38923a00b014ed5697c77e535d62c3 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Fri, 6 Feb 2015 00:32:22 +0000 Subject: [PATCH 149/623] Added detail in doc on how to build the documentation --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index dd358c56..3248becf 100644 --- a/README.md +++ b/README.md @@ -149,6 +149,7 @@ involves running > ../configure > make > make install +> make doxygen-doc # if you want to build documentation Optional parameters to configure script --------------------------------------- From 51f4ce9e8ad48419a0b3a2780950d0fa3e832dc1 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Fri, 6 Feb 2015 00:33:28 +0000 Subject: [PATCH 150/623] Added detail on how to build the code reference in the README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3248becf..1bd46b18 100644 --- a/README.md +++ b/README.md @@ -149,7 +149,7 @@ involves running > ../configure > make > make install -> make doxygen-doc # if you want to build documentation +> make doxygen-doc # if you want to build the code reference Optional parameters to configure script --------------------------------------- From 4399d38717e2cf820ae5bbe1c825e2c524a58a28 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sat, 14 Feb 2015 01:07:53 +0000 Subject: [PATCH 151/623] Solved bug on event_supplier registering. This includes additional binders having being crafted to support the use case. --- src/httpserver/binders.hpp | 164 +++++++++++++++++++++++++ src/httpserver/details/event_tuple.hpp | 17 ++- src/httpserver/webserver.hpp | 2 +- 3 files changed, 172 insertions(+), 11 deletions(-) diff --git a/src/httpserver/binders.hpp b/src/httpserver/binders.hpp index 02a76b88..ccf40503 100644 --- a/src/httpserver/binders.hpp +++ b/src/httpserver/binders.hpp @@ -119,6 +119,53 @@ namespace details } }; + template + class functor_zero + { + private: + typedef RET_TYPE (*static_function)(); + typedef RET_TYPE (*void_static_function)(); + typedef RET_TYPE (generic_class::*generic_mem)(); + typedef binder binder_type; + binder_type _binder; + + RET_TYPE exec_static() const + { + return (*(_binder.get_static_func()))(); + } + + functor_zero& operator=(const functor_zero&) + { + return *this; + } + public: + typedef functor_zero type; + functor_zero() { } + + template + functor_zero(Y* pmem, RET_TYPE(X::*func)()): + _binder(reinterpret_cast(pmem), func) + { + } + + template + functor_zero(Y* pmem, RET_TYPE(X::*func)() const ): + _binder(reinterpret_cast(pmem), func) + { + } + + functor_zero(RET_TYPE(*func)() ): + _binder(this, &functor_zero::exec_static, func) + { + } + + RET_TYPE operator() () const + { + return (_binder.exec()->*(_binder.get_mem_ptr()))(); + } + }; + template class functor_one { @@ -149,6 +196,12 @@ namespace details { } + template + functor_one(Y* pmem, RET_TYPE(X::*func)(PAR1 p1) const ): + _binder(reinterpret_cast(pmem), func) + { + } + functor_one(RET_TYPE(*func)(PAR1 p1) ): _binder(this, &functor_one::exec_static, func) { @@ -189,11 +242,18 @@ namespace details { } + template + functor_two(Y* pmem, RET_TYPE(X::*func)(PAR1 p1, PAR2 p2) const ): + _binder(reinterpret_cast(pmem), func) + { + } + template functor_two(Y* pmem, RET_TYPE(X::*func)(PAR1 p1, PAR2 p2) ): _binder(reinterpret_cast(pmem), func) { } + functor_two(RET_TYPE(*func)(PAR1 p1, PAR2 p2) ): _binder(this, &functor_two::exec_static, func) { @@ -204,6 +264,110 @@ namespace details return (_binder.exec()->*(_binder.get_mem_ptr()))(p1, p2); } }; + + template + class functor_three + { + private: + typedef RET_TYPE (*static_function)(PAR1 p1, PAR2 p2, PAR3 p3); + typedef RET_TYPE (*void_static_function)(PAR1 p1, PAR2 p2, PAR3 p3); + + typedef RET_TYPE + (generic_class::*generic_mem)(PAR1 p1, PAR2 p2, PAR3 p3); + + typedef binder< + generic_mem, static_function, void_static_function + > binder_type; + + binder_type _binder; + + RET_TYPE exec_static(PAR1 p1, PAR2 p2, PAR3 p3) const + { + return (*(_binder.get_static_func()))(p1, p2, p3); + } + public: + typedef functor_three type; + functor_three() { } + + functor_three(const functor_three& o): + _binder(o._binder) + { + } + + template + functor_three(Y* pmem, RET_TYPE(X::*func)(PAR1 p1, PAR2 p2, PAR3 p3) const ): + _binder(reinterpret_cast(pmem), func) + { + } + + template + functor_three(Y* pmem, RET_TYPE(X::*func)(PAR1 p1, PAR2 p2, PAR3 p3) ): + _binder(reinterpret_cast(pmem), func) + { + } + + functor_three(RET_TYPE(*func)(PAR1 p1, PAR2 p2, PAR3 p3) ): + _binder(this, &functor_three::exec_static, func) + { + } + + RET_TYPE operator() (PAR1 p1, PAR2 p2, PAR3 p3) const + { + return (_binder.exec()->*(_binder.get_mem_ptr()))(p1, p2, p3); + } + }; + + template + class functor_four + { + private: + typedef RET_TYPE (*static_function)(PAR1 p1, PAR2 p2, PAR3 p3, PAR4 p4); + typedef RET_TYPE (*void_static_function)(PAR1 p1, PAR2 p2, PAR3 p3, PAR4 p4); + + typedef RET_TYPE + (generic_class::*generic_mem)(PAR1 p1, PAR2 p2, PAR3 p3, PAR4 p4); + + typedef binder< + generic_mem, static_function, void_static_function + > binder_type; + + binder_type _binder; + + RET_TYPE exec_static(PAR1 p1, PAR2 p2, PAR3 p3, PAR4 p4) const + { + return (*(_binder.get_static_func()))(p1, p2, p3, p4); + } + public: + typedef functor_four type; + functor_four() { } + + functor_four(const functor_four& o): + _binder(o._binder) + { + } + + template + functor_four(Y* pmem, RET_TYPE(X::*func)(PAR1 p1, PAR2 p2, PAR3 p3, PAR4 p4) const ): + _binder(reinterpret_cast(pmem), func) + { + } + + template + functor_four(Y* pmem, RET_TYPE(X::*func)(PAR1 p1, PAR2 p2, PAR3 p3, PAR4 p4) ): + _binder(reinterpret_cast(pmem), func) + { + } + + functor_four(RET_TYPE(*func)(PAR1 p1, PAR2 p2, PAR3 p3, PAR4 p4) ): + _binder(this, &functor_four::exec_static, func) + { + } + + RET_TYPE operator() (PAR1 p1, PAR2 p2, PAR3 p3, PAR4 p4) const + { + return (_binder.exec()->*(_binder.get_mem_ptr()))(p1, p2, p3, p4); + } + }; } }} diff --git a/src/httpserver/details/event_tuple.hpp b/src/httpserver/details/event_tuple.hpp index a81ca7b9..df552d49 100644 --- a/src/httpserver/details/event_tuple.hpp +++ b/src/httpserver/details/event_tuple.hpp @@ -26,6 +26,7 @@ #define _EVENT_TUPLE_HPP_ #include "httpserver/event_supplier.hpp" +#include "httpserver/binders.hpp" namespace httpserver { @@ -45,11 +46,9 @@ namespace details MHD_socket* ); - typedef struct timeval(*get_timeout_ptr)(); - typedef void(*dispatch_events_ptr)(); - supply_events_ptr supply_events; - get_timeout_ptr get_timeout; - dispatch_events_ptr dispatch_events; + binders::functor_four supply_events; + binders::functor_zero get_timeout; + binders::functor_zero dispatch_events; event_tuple(); @@ -57,11 +56,9 @@ namespace details public: template event_tuple(event_supplier* es): - supply_events(std::bind1st(std::mem_fun(&T::supply_events),es)), - get_timeout(std::bind1st(std::mem_fun(&T::get_timeout), es)), - dispatch_events(std::bind1st( - std::mem_fun(&T::dispatch_events), es) - ) + supply_events(binders::functor_four(es, &T::supply_events)), + get_timeout(binders::functor_zero(es, &T::get_timeout)), + dispatch_events(binders::functor_zero(es, &T::dispatch_events)) { } }; diff --git a/src/httpserver/webserver.hpp b/src/httpserver/webserver.hpp index 401a0e10..d56da986 100644 --- a/src/httpserver/webserver.hpp +++ b/src/httpserver/webserver.hpp @@ -175,7 +175,7 @@ class webserver event_supplier* ev_supplier ) { - register_event_supplier(id, details::event_tuple(&ev_supplier)); + register_event_supplier(id, details::event_tuple(ev_supplier)); } void remove_event_supplier(const std::string& id); From 9181e3e7da0ac32088ee926e0ae92c889b6680e3 Mon Sep 17 00:00:00 2001 From: Craig Minihan Date: Tue, 24 Feb 2015 14:57:04 +0000 Subject: [PATCH 152/623] Return the status from the call webserver::register_resource --- src/httpserver/webserver.hpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/httpserver/webserver.hpp b/src/httpserver/webserver.hpp index d56da986..0792f903 100644 --- a/src/httpserver/webserver.hpp +++ b/src/httpserver/webserver.hpp @@ -95,19 +95,20 @@ class webserver **/ bool is_running(); /** - * Method used to registrate a resource to the webserver. + * Method used to register a resource with the webserver. * @param resource The url pointing to the resource. This url could be also parametrized in the form /path/to/url/{par1}/and/{par2} * or a regular expression. * @param http_resource http_resource pointer to register. * @param family boolean indicating whether the resource is registered for the endpoint and its child or not. + * @return true if the resource was registered **/ template - void register_resource(const std::string& resource, + bool register_resource(const std::string& resource, http_resource* res, bool family = false ) { details::http_resource_mirror hrm(res); - register_resource(resource, hrm, family); + return register_resource(resource, hrm, family); } void unregister_resource(const std::string& resource); @@ -201,8 +202,8 @@ class webserver validator_ptr validator; unescaper_ptr unescaper; const struct sockaddr* bind_address; - /* Changed type to MHD_socket because this type will always reflect the - platform's actual socket type (e.g. SOCKET on windows, int on unixes)*/ + /* Changed type to MHD_socket because this type will always reflect the + platform's actual socket type (e.g. SOCKET on windows, int on unixes)*/ MHD_socket bind_socket; const int max_thread_stack_size; const bool use_ssl; From 1a57e2ce2e3dca06a86405920b191c258229a789 Mon Sep 17 00:00:00 2001 From: Craig Minihan Date: Mon, 23 Feb 2015 22:28:13 +0000 Subject: [PATCH 153/623] standardize_url doesn't remove the root / now --- src/http_utils.cpp | 14 ++++++++------ test/unit/http_utils_test.cpp | 22 ++++++++++++++++++++++ 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/src/http_utils.cpp b/src/http_utils.cpp index ea418ecc..3dde0b7f 100644 --- a/src/http_utils.cpp +++ b/src/http_utils.cpp @@ -224,9 +224,11 @@ void http_utils::standardize_url(const std::string& url, std::string& result) { std::string n_url; string_utilities::regex_replace(url, "(\\/)+", "/", n_url); - if(n_url[n_url.size() - 1] == '/') + std::string::size_type n_url_length = n_url.length(); + + if (n_url_length > 1 && n_url[n_url_length - 1] == '/') { - result = n_url.substr(0, n_url.size() -1); + result = n_url.substr(0, n_url_length - 1); } else { @@ -545,7 +547,7 @@ char* load_file (const char *filename) void dump_header_map(std::ostream &os, const std::string &prefix, const std::map &map) { - std::map::const_iterator it = map.begin(); + std::map::const_iterator it = map.begin(); std::map::const_iterator end = map.end(); if (map.size()) { @@ -560,7 +562,7 @@ void dump_header_map(std::ostream &os, const std::string &prefix, void dump_arg_map(std::ostream &os, const std::string &prefix, const std::map &map) { - std::map::const_iterator it = map.begin(); + std::map::const_iterator it = map.begin(); std::map::const_iterator end = map.end(); if (map.size()) { @@ -571,7 +573,7 @@ void dump_arg_map(std::ostream &os, const std::string &prefix, os << "]" << std::endl; } } - - + + }; }; diff --git a/test/unit/http_utils_test.cpp b/test/unit/http_utils_test.cpp index 09d2767e..8736a2c3 100644 --- a/test/unit/http_utils_test.cpp +++ b/test/unit/http_utils_test.cpp @@ -47,6 +47,28 @@ LT_BEGIN_AUTO_TEST(http_utils_suite, unescape) LT_CHECK_EQ(expected_size, 3); LT_END_AUTO_TEST(unescape) +LT_BEGIN_AUTO_TEST(http_utils_suite, standardize_url) + string url = "/", result; + http::http_utils::standardize_url(url, result); + LT_CHECK_EQ(result, "/"); + + url = "/abc/", result = ""; + http::http_utils::standardize_url(url, result); + LT_CHECK_EQ(result, "/abc"); + + url = "/abc", result = ""; + http::http_utils::standardize_url(url, result); + LT_CHECK_EQ(result, "/abc"); + + url = "/abc/pqr/", result = ""; + http::http_utils::standardize_url(url, result); + LT_CHECK_EQ(result, "/abc/pqr"); + + url = "/abc/pqr", result = ""; + http::http_utils::standardize_url(url, result); + LT_CHECK_EQ(result, "/abc/pqr"); +LT_END_AUTO_TEST(standardize_url) + LT_BEGIN_AUTO_TEST_ENV() AUTORUN_TESTS() LT_END_AUTO_TEST_ENV() From b6dc904b8c8262c41ed2ec8c590dc97d47ab1d69 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Wed, 25 Mar 2015 19:52:54 +0000 Subject: [PATCH 154/623] Simplifying configure.ac Removed unused flag c++11. Removed check for presence of standard toolchain libs --- configure.ac | 27 --------------------------- 1 file changed, 27 deletions(-) diff --git a/configure.ac b/configure.ac index 147c191c..85788471 100644 --- a/configure.ac +++ b/configure.ac @@ -71,18 +71,8 @@ esac # Checks for header files. AC_HEADER_STDC -AC_CHECK_HEADER([string],[],[AC_MSG_ERROR("C++ strings not found")]) -AC_CHECK_HEADER([vector],[],[AC_MSG_ERROR("C++ vector not found")]) -AC_CHECK_HEADER([map],[],[AC_MSG_ERROR("C++ map not found")]) -AC_CHECK_HEADER([algorithm],[],[AC_MSG_ERROR("C++ algorithm not found")]) -AC_CHECK_HEADER([istream],[],[AC_MSG_ERROR("C++ istream not found")]) -AC_CHECK_HEADER([sstream],[],[AC_MSG_ERROR("C++ sstream not found")]) -AC_CHECK_HEADER([iostream],[],[AC_MSG_ERROR("C++ iostream not found")]) AC_CHECK_HEADER([stdint.h],[],[AC_MSG_ERROR("stdint.h not found")]) AC_CHECK_HEADER([inttypes.h],[],[AC_MSG_ERROR("inttypes.h not found")]) -AC_CHECK_HEADER([stdlib.h],[],[AC_MSG_ERROR("stdlib.h not found")]) -AC_CHECK_HEADER([string.h],[],[AC_MSG_ERROR("cstring not found")]) -AC_CHECK_HEADER([stdio.h],[],[AC_MSG_ERROR("cstdio not found")]) AC_CHECK_HEADER([errno.h],[],[AC_MSG_ERROR("errno.h not found")]) AC_CHECK_HEADER([unistd.h],[],[AC_MSG_ERROR("unistd.h not found")]) AC_CHECK_HEADER([ctype.h],[],[AC_MSG_ERROR("cctype not found")]) @@ -152,22 +142,6 @@ if test x"have_gnutls" = x"yes"; then AM_CFLAGS="$AM_CXXFLAGS -DHAVE_GNUTLS" fi -AC_MSG_CHECKING([whether to use c++0x std classes]) -AC_ARG_ENABLE([cpp11], - [AS_HELP_STRING([--enable-cpp11], - [enable c++11 std classes (def=no)])], - [cpp11="$enableval"], - [cpp11=no]) -AC_MSG_RESULT([$cpp11]) - -if test x"$cpp11" = x"yes"; then - AC_DEFINE([CPP11],[],[c++11 Mode]) - AM_CXXFLAGS="$AM_CXXFLAGS -DUSE_CPP_11 --std=c++11" - AM_CFLAGS="$AM_CXXFLAGS -DUSE_CPP_11 --std=c++11" -else - AC_DEFINE([NCPP11],[],[standard Mode]) -fi - DX_HTML_FEATURE(ON) DX_CHM_FEATURE(OFF) DX_CHI_FEATURE(OFF) @@ -212,5 +186,4 @@ AC_MSG_NOTICE([Configuration Summary: License : LGPL only Debug : ${debugit} TLS Enabled : ${have_gnutls} - C++11 : ${cpp11} ]) From ffa28e2725ae1d6f7909057e3fb913f9542db0dd Mon Sep 17 00:00:00 2001 From: Vitaut Bajaryn Date: Thu, 2 Apr 2015 20:31:29 +0200 Subject: [PATCH 155/623] http_response: Change cycle_callback_ptr so that buffer can be modified --- src/http_response.cpp | 2 +- src/httpserver/http_response.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/http_response.cpp b/src/http_response.cpp index 75d1082b..0d111255 100644 --- a/src/http_response.cpp +++ b/src/http_response.cpp @@ -207,7 +207,7 @@ namespace details ssize_t cb(void* cls, uint64_t pos, char* buf, size_t max) { - ssize_t val = static_cast(cls)->cycle_callback(buf); + ssize_t val = static_cast(cls)->cycle_callback(buf, max); if(val == -1) static_cast(cls)->completed = true; return val; diff --git a/src/httpserver/http_response.hpp b/src/httpserver/http_response.hpp index d7b57df3..942bf1c8 100644 --- a/src/httpserver/http_response.hpp +++ b/src/httpserver/http_response.hpp @@ -62,7 +62,7 @@ class bad_caching_attempt: public std::exception } }; -typedef ssize_t(*cycle_callback_ptr)(const std::string&); +typedef ssize_t(*cycle_callback_ptr)(char*, size_t); /** * Class representing an abstraction for an Http Response. It is used from classes using these apis to send information through http protocol. From dafcc65088483b1100cb8fea6cbf3edb2bc9681e Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Fri, 10 Apr 2015 00:21:07 +0100 Subject: [PATCH 156/623] avoid usage of ntop and pton functions. --- src/http_utils.cpp | 49 +++-------------------------------- src/httpserver/http_utils.hpp | 4 +-- test/unit/http_utils_test.cpp | 24 +++++++++++++++++ 3 files changed, 29 insertions(+), 48 deletions(-) diff --git a/src/http_utils.cpp b/src/http_utils.cpp index 3dde0b7f..3f856f65 100644 --- a/src/http_utils.cpp +++ b/src/http_utils.cpp @@ -28,7 +28,8 @@ #include #include #else -#include +#include +#include #endif #include #include @@ -244,36 +245,8 @@ void get_ip_str( { if(sa) { - char to_ret[INET6_ADDRSTRLEN] = { '\0' }; - switch(sa->sa_family) - { - case AF_INET: - if(maxlen == 0) - maxlen = INET_ADDRSTRLEN; - - inet_ntop(AF_INET, - &(((struct sockaddr_in *)sa)->sin_addr), - to_ret, - maxlen - ); - - break; - - case AF_INET6: - if(maxlen == 0) - maxlen = INET6_ADDRSTRLEN; - - inet_ntop(AF_INET6, - &(((struct sockaddr_in6 *)sa)->sin6_addr), - to_ret, - maxlen - ); - - break; - default: - strncpy(to_ret, "Unknown AF", 11); - return; - } + char to_ret[NI_MAXHOST]; + getnameinfo(sa, sizeof (struct sockaddr), to_ret, NI_MAXHOST, NULL, 0, NI_NUMERICHOST); result = to_ret; } } @@ -288,20 +261,6 @@ std::string get_ip_str_new( return to_ret; } -const struct sockaddr str_to_ip(const std::string& src) -{ - struct sockaddr s; - if(src.find(":") != std::string::npos) - { - inet_pton(AF_INET6, src.c_str(), (void*) &s); - } - else - { - inet_pton(AF_INET, src.c_str(), (void*) &s); - } - return s; -} - short get_port(const struct sockaddr* sa) { if(sa) diff --git a/src/httpserver/http_utils.hpp b/src/httpserver/http_utils.hpp index edc19552..441efe2f 100644 --- a/src/httpserver/http_utils.hpp +++ b/src/httpserver/http_utils.hpp @@ -347,7 +347,7 @@ void dump_header_map(std::ostream &os, const std::string &prefix, **/ void dump_arg_map(std::ostream &os, const std::string &prefix, const std::map &map); - + /** * Process escape sequences ('+'=space, %HH) Updates val in place; the * result should be UTF-8 encoded and cannot be larger than the input. @@ -359,8 +359,6 @@ void dump_arg_map(std::ostream &os, const std::string &prefix, */ size_t http_unescape (char *val); -const struct sockaddr str_to_ip(const std::string& src); - char* load_file (const char *filename); size_t load_file (const char* filename, char** content); diff --git a/test/unit/http_utils_test.cpp b/test/unit/http_utils_test.cpp index 8736a2c3..4b058144 100644 --- a/test/unit/http_utils_test.cpp +++ b/test/unit/http_utils_test.cpp @@ -18,8 +18,19 @@ USA */ +#if defined(__MINGW32__) || defined(__CYGWIN32__) +#define _WINDOWS +#undef _WIN32_WINNT +#define _WIN32_WINNT 0x600 +#include +#include +#else +#include +#endif + #include "littletest.hpp" #include "http_utils.hpp" + #include using namespace httpserver; @@ -69,6 +80,19 @@ LT_BEGIN_AUTO_TEST(http_utils_suite, standardize_url) LT_CHECK_EQ(result, "/abc/pqr"); LT_END_AUTO_TEST(standardize_url) +LT_BEGIN_AUTO_TEST(http_utils_suite, ip_to_str) + struct sockaddr_in ip4addr; + + ip4addr.sin_family = AF_INET; + ip4addr.sin_port = htons(3490); + ip4addr.sin_addr.s_addr = inet_addr("127.0.0.1"); + + string result = ""; + http::get_ip_str((struct sockaddr *) &ip4addr, result); + + LT_CHECK_EQ(result, "127.0.0.1"); +LT_END_AUTO_TEST(ip_to_str) + LT_BEGIN_AUTO_TEST_ENV() AUTORUN_TESTS() LT_END_AUTO_TEST_ENV() From 3ee93603af191b447baec506e243cbe5a7ad30e9 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Wed, 15 Apr 2015 01:37:15 +0100 Subject: [PATCH 157/623] Updated authors file --- AUTHORS | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/AUTHORS b/AUTHORS index b5b9b0ec..8bfdb858 100644 --- a/AUTHORS +++ b/AUTHORS @@ -5,9 +5,16 @@ Sebastiano Merlino (maintainer) Dario Mazza Andrea Nicotra Jeff Waller +Craig Minihan +Guo Xiao +Philipp Claßen +Vitaut Bajaryn - Support for building on MinGW/Cygwin systems Shane Peelar +- Support for building on MaxOsX +Jan Klimke + - Example of SSL usage and operator<< on http_request and http_response Chris Love From 0e08b6b911281491d97cd4b212811daca15bacbb Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Wed, 15 Apr 2015 02:04:59 +0100 Subject: [PATCH 158/623] Updated changelogs and version --- ChangeLog | 8 ++++++++ configure.ac | 4 ++-- debian/changelog.in | 10 ++++++++++ debian/copyright.in | 4 ++-- 4 files changed, 22 insertions(+), 4 deletions(-) diff --git a/ChangeLog b/ChangeLog index bae73d79..8db1d398 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +Wed Apr 15 01:40:11 2015 +0000 + Support build on MacOsX + Improved support for CI on travis + Solved bug on event_supplier registering + Solved bug on standardize_url to avoid removing root + Change cycle_callback_ptr so that buffer can be modified + Moved to version 0.9.0 + Sun Jul 23 02:46:20 2014 +0100 Support for building on MinGW/Cygwin systems min libmicrohttpd version moved to 0.9.37 diff --git a/configure.ac b/configure.ac index 85788471..46d16f61 100644 --- a/configure.ac +++ b/configure.ac @@ -21,8 +21,8 @@ AC_PREREQ(2.57) m4_define([libhttpserver_MAJOR_VERSION],[0])dnl -m4_define([libhttpserver_MINOR_VERSION],[8])dnl -m4_define([libhttpserver_REVISION],[2])dnl +m4_define([libhttpserver_MINOR_VERSION],[9])dnl +m4_define([libhttpserver_REVISION],[0])dnl m4_define([libhttpserver_PKG_VERSION],[libhttpserver_MAJOR_VERSION.libhttpserver_MINOR_VERSION.libhttpserver_REVISION])dnl m4_define([libhttpserver_LDF_VERSION],[libhttpserver_MAJOR_VERSION:libhttpserver_MINOR_VERSION:libhttpserver_REVISION])dnl AC_INIT([libhttpserver], libhttpserver_PKG_VERSION, [electrictwister2000@gmail.com]) diff --git a/debian/changelog.in b/debian/changelog.in index 117db801..1f7b46f1 100644 --- a/debian/changelog.in +++ b/debian/changelog.in @@ -1,3 +1,13 @@ +libhttpserver (0.9.0) unstable; urgency=low + * Support build on MacOsX + * Improved support for CI on travis + * Solved bug on event_supplier registering + * Solved bug on standardize_url to avoid removing root + * Change cycle_callback_ptr so that buffer can be modified + * Moved to version 0.9.0 + + -- Sebastiano Merlino Wed, 15 Apr 2015 01:40:11 +0000 + libhttpserver (0.8.0) unstable; urgency=low * Support for building on MinGW/Cygwin systems * min libmicrohttpd version moved to 0.9.37 diff --git a/debian/copyright.in b/debian/copyright.in index 238d2bd7..89a1f7a3 100644 --- a/debian/copyright.in +++ b/debian/copyright.in @@ -1,9 +1,9 @@ Files: * -Copyright: (C) 2011-2012 Sebastiano Merlino +Copyright: (C) 2011-2015 Sebastiano Merlino License: LGPL-2.1+ Files: doc/* -Copyright: (C) 2011-2012 Sebastiano Merlino +Copyright: (C) 2011-2015 Sebastiano Merlino License: GFDL-1.3+ Permission is granted to copy, distribute and/or modify this document From 4cbfc1faa1ee46a7d94cfe6d418e9c65492ae9b0 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Wed, 15 Apr 2015 02:14:35 +0100 Subject: [PATCH 159/623] Updated copyrights --- Makefile.am | 2 +- Makefile.cvs | 2 +- bootstrap | 2 +- configure.ac | 2 +- doc/Makefile.am | 2 +- examples/Makefile.am | 2 +- examples/hello_world.cpp | 2 +- src/Makefile.am | 2 +- src/details/comet_manager.cpp | 2 +- src/details/http_endpoint.cpp | 2 +- src/http_request.cpp | 2 +- src/http_resource.cpp | 2 +- src/http_response.cpp | 2 +- src/http_utils.cpp | 2 +- src/httpserver.hpp | 2 +- src/httpserver/binders.hpp | 2 +- src/httpserver/create_webserver.hpp | 2 +- src/httpserver/details/cache_entry.hpp | 2 +- src/httpserver/details/comet_manager.hpp | 2 +- src/httpserver/details/event_tuple.hpp | 2 +- src/httpserver/details/http_endpoint.hpp | 2 +- src/httpserver/details/http_resource_mirror.hpp | 2 +- src/httpserver/details/http_response_ptr.hpp | 2 +- src/httpserver/details/modded_request.hpp | 2 +- src/httpserver/event_supplier.hpp | 2 +- src/httpserver/http_request.hpp | 2 +- src/httpserver/http_resource.hpp | 2 +- src/httpserver/http_response.hpp | 2 +- src/httpserver/http_utils.hpp | 2 +- src/httpserver/string_utilities.hpp | 2 +- src/httpserver/webserver.hpp | 2 +- src/string_utilities.cpp | 2 +- src/webserver.cpp | 2 +- test/Makefile.am | 2 +- test/integ/basic.cpp | 2 +- test/unit/http_utils_test.cpp | 2 +- 36 files changed, 36 insertions(+), 36 deletions(-) diff --git a/Makefile.am b/Makefile.am index 982cf3f1..605e3fc6 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,6 +1,6 @@ # # This file is part of libhttpserver -# Copyright (C) 2011, 2012, 2013, 2014 Sebastiano Merlino +# Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public diff --git a/Makefile.cvs b/Makefile.cvs index 9d45cc6d..e655e6d8 100644 --- a/Makefile.cvs +++ b/Makefile.cvs @@ -1,6 +1,6 @@ # # This file is part of libhttpserver -# Copyright (C) 2011, 2012, 2013, 2014 Sebastiano Merlino +# Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public diff --git a/bootstrap b/bootstrap index 02a223f9..67425d32 100755 --- a/bootstrap +++ b/bootstrap @@ -1,7 +1,7 @@ #!/bin/bash # # This file is part of libhttpserver -# Copyright (C) 2011, 2012, 2013, 2014 Sebastiano Merlino +# Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public diff --git a/configure.ac b/configure.ac index 46d16f61..2f399e81 100644 --- a/configure.ac +++ b/configure.ac @@ -1,6 +1,6 @@ # # This file is part of libhttpserver -# Copyright (C) 2011, 2012, 2013, 2014 Sebastiano Merlino +# Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public diff --git a/doc/Makefile.am b/doc/Makefile.am index c490c1c1..3598b431 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -1,6 +1,6 @@ # # This file is part of libhttpserver -# Copyright (C) 2011, 2012, 2013, 2014 Sebastiano Merlino +# Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public diff --git a/examples/Makefile.am b/examples/Makefile.am index a419f400..e0c44ac6 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -1,6 +1,6 @@ # # This file is part of libhttpserver -# Copyright (C) 2011, 2012, 2013, 2014 Sebastiano Merlino +# Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public diff --git a/examples/hello_world.cpp b/examples/hello_world.cpp index be091578..e5d37d9a 100755 --- a/examples/hello_world.cpp +++ b/examples/hello_world.cpp @@ -1,6 +1,6 @@ /* This file is part of libhttpserver - Copyright (C) 2011, 2012, 2013, 2014 Sebastiano Merlino + Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public diff --git a/src/Makefile.am b/src/Makefile.am index 6067c317..51299bf3 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,6 +1,6 @@ # # This file is part of libhttpserver -# Copyright (C) 2011, 2012, 2013, 2014 Sebastiano Merlino +# Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public diff --git a/src/details/comet_manager.cpp b/src/details/comet_manager.cpp index d9d13adf..a6eb8708 100644 --- a/src/details/comet_manager.cpp +++ b/src/details/comet_manager.cpp @@ -1,6 +1,6 @@ /* This file is part of libhttpserver - Copyright (C) 2011, 2012, 2013, 2014 Sebastiano Merlino + Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public diff --git a/src/details/http_endpoint.cpp b/src/details/http_endpoint.cpp index 1fa7e059..3e5a6431 100644 --- a/src/details/http_endpoint.cpp +++ b/src/details/http_endpoint.cpp @@ -1,6 +1,6 @@ /* This file is part of libhttpserver - Copyright (C) 2011, 2012, 2013, 2014 Sebastiano Merlino + Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public diff --git a/src/http_request.cpp b/src/http_request.cpp index 87de8eeb..19fb3f32 100644 --- a/src/http_request.cpp +++ b/src/http_request.cpp @@ -1,6 +1,6 @@ /* This file is part of libhttpserver - Copyright (C) 2011, 2012, 2013, 2014 Sebastiano Merlino + Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public diff --git a/src/http_resource.cpp b/src/http_resource.cpp index 03437315..693f33cf 100644 --- a/src/http_resource.cpp +++ b/src/http_resource.cpp @@ -1,6 +1,6 @@ /* This file is part of libhttpserver - Copyright (C) 2011, 2012, 2013, 2014 Sebastiano Merlino + Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public diff --git a/src/http_response.cpp b/src/http_response.cpp index 0d111255..1bff428f 100644 --- a/src/http_response.cpp +++ b/src/http_response.cpp @@ -1,6 +1,6 @@ /* This file is part of libhttpserver - Copyright (C) 2011, 2012, 2013, 2014 Sebastiano Merlino + Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public diff --git a/src/http_utils.cpp b/src/http_utils.cpp index 3f856f65..fec42643 100644 --- a/src/http_utils.cpp +++ b/src/http_utils.cpp @@ -1,6 +1,6 @@ /* This file is part of libhttpserver - Copyright (C) 2011, 2012, 2013, 2014 Sebastiano Merlino + Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public diff --git a/src/httpserver.hpp b/src/httpserver.hpp index 2149d70a..74470d37 100644 --- a/src/httpserver.hpp +++ b/src/httpserver.hpp @@ -1,6 +1,6 @@ /* This file is part of libhttpserver - Copyright (C) 2011, 2012, 2013, 2014 Sebastiano Merlino + Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public diff --git a/src/httpserver/binders.hpp b/src/httpserver/binders.hpp index ccf40503..c1ce3007 100644 --- a/src/httpserver/binders.hpp +++ b/src/httpserver/binders.hpp @@ -1,6 +1,6 @@ /* This file is part of libhttpserver - Copyright (C) 2011, 2012, 2013, 2014 Sebastiano Merlino + Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public diff --git a/src/httpserver/create_webserver.hpp b/src/httpserver/create_webserver.hpp index 6813519d..02d2ae8a 100644 --- a/src/httpserver/create_webserver.hpp +++ b/src/httpserver/create_webserver.hpp @@ -1,6 +1,6 @@ /* This file is part of libhttpserver - Copyright (C) 2011, 2012, 2013, 2014 Sebastiano Merlino + Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public diff --git a/src/httpserver/details/cache_entry.hpp b/src/httpserver/details/cache_entry.hpp index b8186bb2..3bf18816 100644 --- a/src/httpserver/details/cache_entry.hpp +++ b/src/httpserver/details/cache_entry.hpp @@ -1,6 +1,6 @@ /* This file is part of libhttpserver - Copyright (C) 2011, 2012, 2013, 2014 Sebastiano Merlino + Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public diff --git a/src/httpserver/details/comet_manager.hpp b/src/httpserver/details/comet_manager.hpp index dff82f62..936b5484 100644 --- a/src/httpserver/details/comet_manager.hpp +++ b/src/httpserver/details/comet_manager.hpp @@ -1,6 +1,6 @@ /* This file is part of libhttpserver - Copyright (C) 2011, 2012, 2013, 2014 Sebastiano Merlino + Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public diff --git a/src/httpserver/details/event_tuple.hpp b/src/httpserver/details/event_tuple.hpp index df552d49..8a005c6e 100644 --- a/src/httpserver/details/event_tuple.hpp +++ b/src/httpserver/details/event_tuple.hpp @@ -1,6 +1,6 @@ /* This file is part of libhttpserver - Copyright (C) 2011, 2012, 2013, 2014 Sebastiano Merlino + Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public diff --git a/src/httpserver/details/http_endpoint.hpp b/src/httpserver/details/http_endpoint.hpp index 19d3fc7a..a514f7e7 100644 --- a/src/httpserver/details/http_endpoint.hpp +++ b/src/httpserver/details/http_endpoint.hpp @@ -1,6 +1,6 @@ /* This file is part of libhttpserver - Copyright (C) 2011, 2012, 2013, 2014 Sebastiano Merlino + Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public diff --git a/src/httpserver/details/http_resource_mirror.hpp b/src/httpserver/details/http_resource_mirror.hpp index 64a394b7..e8b9d12a 100644 --- a/src/httpserver/details/http_resource_mirror.hpp +++ b/src/httpserver/details/http_resource_mirror.hpp @@ -1,6 +1,6 @@ /* This file is part of libhttpserver - Copyright (C) 2011, 2012, 2013, 2014 Sebastiano Merlino + Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public diff --git a/src/httpserver/details/http_response_ptr.hpp b/src/httpserver/details/http_response_ptr.hpp index 62d3f72a..6435c449 100644 --- a/src/httpserver/details/http_response_ptr.hpp +++ b/src/httpserver/details/http_response_ptr.hpp @@ -1,6 +1,6 @@ /* This file is part of libhttpserver - Copyright (C) 2011, 2012, 2013, 2014 Sebastiano Merlino + Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public diff --git a/src/httpserver/details/modded_request.hpp b/src/httpserver/details/modded_request.hpp index 78ed2151..a3850dd2 100644 --- a/src/httpserver/details/modded_request.hpp +++ b/src/httpserver/details/modded_request.hpp @@ -1,6 +1,6 @@ /* This file is part of libhttpserver - Copyright (C) 2011, 2012, 2013, 2014 Sebastiano Merlino + Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public diff --git a/src/httpserver/event_supplier.hpp b/src/httpserver/event_supplier.hpp index 9c7e223b..64a0bba6 100644 --- a/src/httpserver/event_supplier.hpp +++ b/src/httpserver/event_supplier.hpp @@ -1,6 +1,6 @@ /* This file is part of libhttpserver - Copyright (C) 2011, 2012, 2013, 2014 Sebastiano Merlino + Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public diff --git a/src/httpserver/http_request.hpp b/src/httpserver/http_request.hpp index 1a03978c..b367aa52 100644 --- a/src/httpserver/http_request.hpp +++ b/src/httpserver/http_request.hpp @@ -1,6 +1,6 @@ /* This file is part of libhttpserver - Copyright (C) 2011, 2012, 2013, 2014 Sebastiano Merlino + Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public diff --git a/src/httpserver/http_resource.hpp b/src/httpserver/http_resource.hpp index 42076d23..3755c32d 100644 --- a/src/httpserver/http_resource.hpp +++ b/src/httpserver/http_resource.hpp @@ -1,6 +1,6 @@ /* This file is part of libhttpserver - Copyright (C) 2011, 2012, 2013, 2014 Sebastiano Merlino + Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public diff --git a/src/httpserver/http_response.hpp b/src/httpserver/http_response.hpp index 942bf1c8..53711e78 100644 --- a/src/httpserver/http_response.hpp +++ b/src/httpserver/http_response.hpp @@ -1,6 +1,6 @@ /* This file is part of libhttpserver - Copyright (C) 2011, 2012, 2013, 2014 Sebastiano Merlino + Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public diff --git a/src/httpserver/http_utils.hpp b/src/httpserver/http_utils.hpp index 441efe2f..58bc8f6e 100644 --- a/src/httpserver/http_utils.hpp +++ b/src/httpserver/http_utils.hpp @@ -1,6 +1,6 @@ /* This file is part of libhttpserver - Copyright (C) 2011, 2012, 2013, 2014 Sebastiano Merlino + Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public diff --git a/src/httpserver/string_utilities.hpp b/src/httpserver/string_utilities.hpp index 92aa9716..655daaf7 100644 --- a/src/httpserver/string_utilities.hpp +++ b/src/httpserver/string_utilities.hpp @@ -1,6 +1,6 @@ /* This file is part of libhttpserver - Copyright (C) 2011, 2012, 2013, 2014 Sebastiano Merlino + Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public diff --git a/src/httpserver/webserver.hpp b/src/httpserver/webserver.hpp index 0792f903..9a8e172f 100644 --- a/src/httpserver/webserver.hpp +++ b/src/httpserver/webserver.hpp @@ -1,6 +1,6 @@ /* This file is part of libhttpserver - Copyright (C) 2011, 2012, 2013, 2014 Sebastiano Merlino + Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public diff --git a/src/string_utilities.cpp b/src/string_utilities.cpp index 1d146275..eaa321ea 100644 --- a/src/string_utilities.cpp +++ b/src/string_utilities.cpp @@ -1,6 +1,6 @@ /* This file is part of libhttpserver - Copyright (C) 2011, 2012, 2013, 2014 Sebastiano Merlino + Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public diff --git a/src/webserver.cpp b/src/webserver.cpp index c385cb88..d1a3c930 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -1,6 +1,6 @@ /* This file is part of libhttpserver - Copyright (C) 2011, 2012, 2013, 2014 Sebastiano Merlino + Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public diff --git a/test/Makefile.am b/test/Makefile.am index c97f4d02..9e706ef6 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -1,6 +1,6 @@ # # This file is part of libhttpserver -# Copyright (C) 2011, 2012, 2013, 2014 Sebastiano Merlino +# Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public diff --git a/test/integ/basic.cpp b/test/integ/basic.cpp index 1ddb4297..597067f3 100644 --- a/test/integ/basic.cpp +++ b/test/integ/basic.cpp @@ -1,6 +1,6 @@ /* This file is part of libhttpserver - Copyright (C) 2011, 2012, 2013, 2014 Sebastiano Merlino + Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public diff --git a/test/unit/http_utils_test.cpp b/test/unit/http_utils_test.cpp index 4b058144..e721f4bd 100644 --- a/test/unit/http_utils_test.cpp +++ b/test/unit/http_utils_test.cpp @@ -1,6 +1,6 @@ /* This file is part of libhttpserver - Copyright (C) 2011, 2012, 2013, 2014 Sebastiano Merlino + Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public From 8d8e035d371c0fe8790704b6e877fa721dd73535 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Fri, 17 Jul 2015 21:43:02 +0100 Subject: [PATCH 160/623] Modified configure.ac to eliminate build-dependency on pkg-config --- ChangeLog | 3 +++ configure.ac | 25 +++++++++++++++++++++++-- debian/changelog.in | 5 +++++ 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/ChangeLog b/ChangeLog index 8db1d398..4bd29c93 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,6 @@ +Fri Jul 17 21:38:54 2015 +0000 + Removed build dependency on pkg-config + Wed Apr 15 01:40:11 2015 +0000 Support build on MacOsX Improved support for CI on travis diff --git a/configure.ac b/configure.ac index 2f399e81..1beb4292 100644 --- a/configure.ac +++ b/configure.ac @@ -22,7 +22,7 @@ AC_PREREQ(2.57) m4_define([libhttpserver_MAJOR_VERSION],[0])dnl m4_define([libhttpserver_MINOR_VERSION],[9])dnl -m4_define([libhttpserver_REVISION],[0])dnl +m4_define([libhttpserver_REVISION],[1])dnl m4_define([libhttpserver_PKG_VERSION],[libhttpserver_MAJOR_VERSION.libhttpserver_MINOR_VERSION.libhttpserver_REVISION])dnl m4_define([libhttpserver_LDF_VERSION],[libhttpserver_MAJOR_VERSION:libhttpserver_MINOR_VERSION:libhttpserver_REVISION])dnl AC_INIT([libhttpserver], libhttpserver_PKG_VERSION, [electrictwister2000@gmail.com]) @@ -40,6 +40,8 @@ CXXFLAGS=$OLD_CXXFLAGS AC_LANG([C++]) AC_SYS_LARGEFILE +native_srcdir=$srcdir + AC_MSG_CHECKING([whether it is possible to compile in the same directory]) AC_ARG_ENABLE([same-directory-build], [AS_HELP_STRING([--enable-same-directory-build], @@ -58,6 +60,7 @@ case "$host" in *-mingw*) NETWORK_HEADER="winsock2.h" REGEX_LIBS="-lregex -lpthread -no-undefined" + native_srcdir=$(cd $srcdir; pwd -W) ;; *-cygwin*) NETWORK_HEADER="winsock2.h" @@ -85,7 +88,25 @@ AC_CHECK_HEADER([signal.h],[],[AC_MSG_ERROR("signal.h not found")]) AC_CHECK_HEADER([gnutls/gnutls.h],[have_gnutls="yes"],[AC_MSG_WARN("gnutls/gnutls.h not found. TLS will be disabled"); have_gnutls="no"]) # Checks for libmicrohttpd -PKG_CHECK_MODULES([LIBMICROHTTPD],[libmicrohttpd >= 0.9.37],[],[AC_MSG_ERROR("libmicrohttpd not present or too old - install libmicrohttpd >= 0.9.37")]) +AC_CHECK_HEADER([microhttpd.h], + AC_CHECK_LIB([microhttpd], [MHD_get_fdset2], + [AC_MSG_CHECKING([for libmicrohttpd >= 0.9.37]) + AC_COMPILE_IFELSE( + [AC_LANG_SOURCE([ + #include + #if (MHD_VERSION < 0x0093700) + #error needs at least version 0.9.37 + #endif + int main () { return 0; } + ])], + [], + [AC_MSG_ERROR("libmicrohttpd is too old - install libmicrohttpd >= 0.9.37")] + ) + ], + [AC_MSG_ERROR(["libmicrohttpd not found"])] + ), + [AC_MSG_ERROR(["microhttpd.h not found"])] +) CXXFLAGS="-DHTTPSERVER_COMPILATION -D_REENTRANT $LIBMICROHTTPD_CFLAGS $CXXFLAGS" LDFLAGS="$LIBMICROHTTPD_LIBS $REGEX_LIBS $LD_FLAGS" diff --git a/debian/changelog.in b/debian/changelog.in index 1f7b46f1..6f63bcda 100644 --- a/debian/changelog.in +++ b/debian/changelog.in @@ -1,3 +1,8 @@ +libhttpserver (0.9.1) unstable; urgency=low + * Eliminated build dependency on pkg-config + + -- Sebastiano Merlino Fri, 17 Jul 2015 21:38:54 +0000 + libhttpserver (0.9.0) unstable; urgency=low * Support build on MacOsX * Improved support for CI on travis From fa32c211947b7d0baa1936f9fc914c71d4401e31 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Mon, 24 Aug 2015 00:51:58 +0100 Subject: [PATCH 161/623] Avoid usage of 'using' in headers --- src/http_request.cpp | 10 ++++----- src/http_response.cpp | 10 ++++----- src/httpserver/http_request.hpp | 20 ++++++++---------- src/httpserver/http_response.hpp | 22 +++++++++----------- src/httpserver/http_response_builder.hpp | 26 +++++++++++------------- 5 files changed, 41 insertions(+), 47 deletions(-) diff --git a/src/http_request.cpp b/src/http_request.cpp index 19fb3f32..a6e3112f 100644 --- a/src/http_request.cpp +++ b/src/http_request.cpp @@ -62,25 +62,25 @@ bool http_request::check_digest_auth( return true; } -size_t http_request::get_headers(std::map& result) const +size_t http_request::get_headers(std::map& result) const { result = this->headers; return result.size(); } -size_t http_request::get_footers(std::map& result) const +size_t http_request::get_footers(std::map& result) const { result = this->footers; return result.size(); } -size_t http_request::get_cookies(std::map& result) const +size_t http_request::get_cookies(std::map& result) const { result = this->cookies; return result.size(); } -size_t http_request::get_args(std::map& result) const +size_t http_request::get_args(std::map& result) const { result = this->args; return result.size(); @@ -101,6 +101,6 @@ std::ostream &operator<< (std::ostream &os, const http_request &r) return os; } - + } diff --git a/src/http_response.cpp b/src/http_response.cpp index 1bff428f..cddf3cbd 100644 --- a/src/http_response.cpp +++ b/src/http_response.cpp @@ -72,19 +72,19 @@ http_response::~http_response() webserver::unlock_cache_entry(ce); } -size_t http_response::get_headers(std::map& result) const +size_t http_response::get_headers(std::map& result) const { result = this->headers; return result.size(); } -size_t http_response::get_footers(std::map& result) const +size_t http_response::get_footers(std::map& result) const { result = this->footers; return result.size(); } -size_t http_response::get_cookies(std::map& result) const +size_t http_response::get_cookies(std::map& result) const { result = this->cookies; return result.size(); @@ -103,7 +103,7 @@ void http_response::get_raw_response_str(MHD_Response** response, webserver* ws) void http_response::decorate_response_str(MHD_Response* response) { - map::iterator it; + map::iterator it; for (it=headers.begin() ; it != headers.end(); ++it) MHD_add_response_header( @@ -300,5 +300,5 @@ std::ostream &operator<< (std::ostream &os, const http_response &r) return os; } - + }; diff --git a/src/httpserver/http_request.hpp b/src/httpserver/http_request.hpp index b367aa52..a217356d 100644 --- a/src/httpserver/http_request.hpp +++ b/src/httpserver/http_request.hpp @@ -44,8 +44,6 @@ namespace http class arg_comparator; }; -using namespace http; - /** * Class representing an abstraction for an Http Request. It is used from classes using these apis to receive information through http protocol. **/ @@ -194,25 +192,25 @@ class http_request * @param result a map > that will be filled with all headers * @result the size of the map **/ - size_t get_headers(std::map& result) const; + size_t get_headers(std::map& result) const; /** * Method used to get all footers passed with the request. * @param result a map > that will be filled with all footers * @result the size of the map **/ - size_t get_footers(std::map& result) const; + size_t get_footers(std::map& result) const; /** * Method used to get all cookies passed with the request. * @param result a map > that will be filled with all cookies * @result the size of the map **/ - size_t get_cookies(std::map& result) const; + size_t get_cookies(std::map& result) const; /** * Method used to get all args passed with the request. * @param result a map > that will be filled with all args * @result the size of the map **/ - size_t get_args(std::map& result) const; + size_t get_args(std::map& result) const; /** * Method used to get a specific header passed with the request. * @param key the specific header to get the value from @@ -394,10 +392,10 @@ class http_request std::string digested_user; std::string method; std::vector post_path; - std::map headers; - std::map footers; - std::map cookies; - std::map args; + std::map headers; + std::map footers; + std::map cookies; + std::map args; std::string querystring; std::string content; std::string version; @@ -481,7 +479,7 @@ class http_request { this->path = path; std::vector complete_path; - http_utils::tokenize_url(this->path, complete_path); + http::http_utils::tokenize_url(this->path, complete_path); for(unsigned int i = 0; i < complete_path.size(); i++) { this->post_path.push_back(complete_path[i]); diff --git a/src/httpserver/http_response.hpp b/src/httpserver/http_response.hpp index 53711e78..cc55e2de 100644 --- a/src/httpserver/http_response.hpp +++ b/src/httpserver/http_response.hpp @@ -52,8 +52,6 @@ namespace details struct cache_entry; }; -using namespace http; - class bad_caching_attempt: public std::exception { virtual const char* what() const throw() @@ -70,7 +68,7 @@ typedef ssize_t(*cycle_callback_ptr)(char*, size_t); class http_response { public: - + http_response(const http_response_builder& builder); /** @@ -167,7 +165,7 @@ class http_response * @return a map containing all headers. **/ size_t get_headers( - std::map& result + std::map& result ) const; /** @@ -175,11 +173,11 @@ class http_response * @return a map containing all footers. **/ size_t get_footers( - std::map& result + std::map& result ) const; size_t get_cookies( - std::map& result + std::map& result ) const; /** @@ -248,9 +246,9 @@ class http_response bool reload_nonce; int fp; std::string filename; - std::map headers; - std::map footers; - std::map cookies; + std::map headers; + std::map footers; + std::map cookies; std::vector topics; int keepalive_secs; std::string keepalive_msg; @@ -268,7 +266,7 @@ class http_response bool completed; webserver* ws; - struct httpserver_ska connection_id; + struct http::httpserver_ska connection_id; void get_raw_response_str(MHD_Response** res, webserver* ws = 0x0); void get_raw_response_file(MHD_Response** res, webserver* ws = 0x0); @@ -298,14 +296,14 @@ class http_response friend class http_response_builder; friend void clone_response(const http_response& hr, http_response** dhr); friend ssize_t details::cb(void* cls, uint64_t pos, char* buf, size_t max); - friend std::ostream &operator<< (std::ostream &os, const http_response &r); + friend std::ostream &operator<< (std::ostream &os, const http_response &r); private: http_response& operator=(const http_response& b); static ssize_t data_generator (void* cls, uint64_t pos, char* buf, size_t max); }; -std::ostream &operator<< (std::ostream &os, const http_response &r); +std::ostream &operator<< (std::ostream &os, const http_response &r); }; #endif diff --git a/src/httpserver/http_response_builder.hpp b/src/httpserver/http_response_builder.hpp index c405e234..e1bc9108 100644 --- a/src/httpserver/http_response_builder.hpp +++ b/src/httpserver/http_response_builder.hpp @@ -45,8 +45,6 @@ namespace details struct cache_entry; }; -using namespace http; - struct byte_string { public: @@ -86,9 +84,9 @@ class http_response_builder _realm(""), _opaque(""), _reload_nonce(false), - _headers(std::map()), - _footers(std::map()), - _cookies(std::map()), + _headers(std::map()), + _footers(std::map()), + _cookies(std::map()), _topics(std::vector()), _keepalive_secs(-1), _keepalive_msg(""), @@ -98,7 +96,7 @@ class http_response_builder _decorate_response(&http_response::decorate_response_str), _enqueue_response(&http_response::enqueue_response_str) { - _headers[http_utils::http_header_content_type] = content_type; + _headers[http::http_utils::http_header_content_type] = content_type; } http_response_builder( @@ -113,9 +111,9 @@ class http_response_builder _realm(""), _opaque(""), _reload_nonce(false), - _headers(std::map()), - _footers(std::map()), - _cookies(std::map()), + _headers(std::map()), + _footers(std::map()), + _cookies(std::map()), _topics(std::vector()), _keepalive_secs(-1), _keepalive_msg(""), @@ -125,7 +123,7 @@ class http_response_builder _decorate_response(&http_response::decorate_response_str), _enqueue_response(&http_response::enqueue_response_str) { - _headers[http_utils::http_header_content_type] = content_type; + _headers[http::http_utils::http_header_content_type] = content_type; } http_response_builder(const http_response_builder& b): @@ -243,7 +241,7 @@ class http_response_builder http_response_builder& shoutCAST_response() { - _response_code |= http_utils::shoutcast_response; + _response_code |= http::http_utils::shoutcast_response; return *this; } @@ -269,9 +267,9 @@ class http_response_builder std::string _realm; std::string _opaque; bool _reload_nonce; - std::map _headers; - std::map _footers; - std::map _cookies; + std::map _headers; + std::map _footers; + std::map _cookies; std::vector _topics; int _keepalive_secs; std::string _keepalive_msg; From 4e4b6f9f08d0bfcd604bd21309223bf267de69b3 Mon Sep 17 00:00:00 2001 From: Vitaut Bajaryn Date: Sun, 5 Jul 2015 23:18:38 +0200 Subject: [PATCH 162/623] Add CMake module to Makefile.am to be installed --- Makefile.am | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Makefile.am b/Makefile.am index 605e3fc6..9968f6ea 100644 --- a/Makefile.am +++ b/Makefile.am @@ -34,6 +34,9 @@ DISTCLEANFILES = redhat/SOURCES/* redhat/SPEC/* redhat/RPMS/* redhat/SRPMS/* red pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = libhttpserver.pc +cmakemoduledir = $(datadir)/cmake/Modules +cmakemodule_DATA = cmakemodule/FindLibHttpServer.cmake + include $(top_srcdir)/aminclude.am # Update libtool, if needed. From 6d5f5d1c4412a3c9b57030ed29d9c400ea4a8ad4 Mon Sep 17 00:00:00 2001 From: Julian Picht Date: Fri, 2 Oct 2015 13:32:24 +0200 Subject: [PATCH 163/623] FIX: regex-matching was broken if (!family_url) When creating endpoints like ws.register_resource("/hello/{test|[0-9]+}", &hwr, false); the match never succeeded, because the regex was matched against the modded request URL which contained '^' and '$'. --- src/details/http_endpoint.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/details/http_endpoint.cpp b/src/details/http_endpoint.cpp index 3e5a6431..ef62d197 100644 --- a/src/details/http_endpoint.cpp +++ b/src/details/http_endpoint.cpp @@ -226,7 +226,7 @@ bool http_endpoint::match(const http_endpoint& url) const } else return regexec(&(this->re_url_modded), - url.url_modded.c_str(), 0, NULL, 0) == 0; + url.url_complete.c_str(), 0, NULL, 0) == 0; } }; From 6e98ee05aa018d435124c5e923036d579fb19f90 Mon Sep 17 00:00:00 2001 From: Julian Picht Date: Fri, 2 Oct 2015 17:04:05 +0200 Subject: [PATCH 164/623] FIX: do not call abort, throw an exception instead Calling abort is a really bad idea inside a library, because the whole application will die when just the HTTP server module has a problem. --- src/httpserver/webserver.hpp | 10 ++++++++++ src/webserver.cpp | 8 ++++---- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/httpserver/webserver.hpp b/src/httpserver/webserver.hpp index 9a8e172f..06c58fb9 100644 --- a/src/httpserver/webserver.hpp +++ b/src/httpserver/webserver.hpp @@ -41,6 +41,7 @@ #include #include +#include #include "httpserver/create_webserver.hpp" @@ -66,6 +67,15 @@ namespace details { class comet_manager; } +class webserver_exception : public std::runtime_error +{ +public: + webserver_exception() + : std::runtime_error("httpserver runtime error") + { + } +}; + /** * Class representing the webserver. Main class of the apis. **/ diff --git a/src/webserver.cpp b/src/webserver.cpp index d1a3c930..fdf97ca0 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -280,7 +280,7 @@ void* webserver::select(void* self) FD_ZERO (&ws); FD_ZERO (&es); if (MHD_YES != MHD_get_fdset (di->daemon, &rs, &ws, &es, &max)) - abort(); /* fatal internal error */ + throw ::httpserver::webserver_exception(); /* fatal internal error */ unsigned long long timeout_microsecs = 0; unsigned long long timeout_secs = 0; @@ -563,7 +563,7 @@ bool webserver::start(bool blocking) { cout << gettext("Unable to connect daemon to port: ") << this->port << endl; - abort(); + throw ::httpserver::webserver_exception(); } details::daemon_item* di = new details::daemon_item(this, daemon); daemons.push_back(di); @@ -579,7 +579,7 @@ bool webserver::start(bool blocking) static_cast(di) )) { - abort(); + throw ::httpserver::webserver_exception(); } } } @@ -595,7 +595,7 @@ bool webserver::start(bool blocking) { cout << gettext("Unable to connect daemon to port: ") << this->port << endl; - abort(); + throw ::httpserver::webserver_exception(); } details::daemon_item* di = new details::daemon_item(this, daemon); daemons.push_back(di); From 517e4c1cde50ca43feef39cc8c7398c4d98f5225 Mon Sep 17 00:00:00 2001 From: Julian Picht Date: Mon, 5 Oct 2015 10:02:55 +0200 Subject: [PATCH 165/623] webserver: stop leaking pthreads on startup failure --- src/webserver.cpp | 53 +++++++++++++++++++++++++++-------------------- 1 file changed, 31 insertions(+), 22 deletions(-) diff --git a/src/webserver.cpp b/src/webserver.cpp index fdf97ca0..84843c13 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -551,36 +551,45 @@ bool webserver::start(bool blocking) this->running = true; if(start_method == http_utils::INTERNAL_SELECT) { - for(int i = 0; i < num_threads; i++) - { - struct MHD_Daemon* daemon = MHD_start_daemon - ( - start_conf, this->port, &policy_callback, this, - &answer_to_connection, this, MHD_OPTION_ARRAY, - &iov[0], MHD_OPTION_END - ); - if(NULL == daemon) + int i; + try { + for(i = 0; i < num_threads; i++) { - cout << gettext("Unable to connect daemon to port: ") << - this->port << endl; - throw ::httpserver::webserver_exception(); - } - details::daemon_item* di = new details::daemon_item(this, daemon); - daemons.push_back(di); + struct MHD_Daemon* daemon = MHD_start_daemon + ( + start_conf, this->port, &policy_callback, this, + &answer_to_connection, this, MHD_OPTION_ARRAY, + &iov[0], MHD_OPTION_END + ); + if(NULL == daemon) + { + cout << gettext("Unable to connect daemon to port: ") << this->port << endl; + throw ::httpserver::webserver_exception(); + } + details::daemon_item* di = new details::daemon_item(this, daemon); + daemons.push_back(di); - //RUN SELECT THREADS - pthread_t t; - threads.push_back(t); + //RUN SELECT THREADS + pthread_t t; + threads.push_back(t); - if(pthread_create( + if(pthread_create( &threads[i], NULL, &webserver::select, static_cast(di) - )) - { - throw ::httpserver::webserver_exception(); + )) + { + throw ::httpserver::webserver_exception(); + } + } + } catch (::httpserver::webserver_exception &e) { + this->running = false; + for (;i >= 0; --i) { + pthread_kill(&threads[i], 9); } + threads.clear(); + throw e; } } else From 84ec6cc5f8c4b10c52b36850f2337a6d8cb7de85 Mon Sep 17 00:00:00 2001 From: Julian Picht Date: Mon, 5 Oct 2015 12:11:40 +0200 Subject: [PATCH 166/623] FIX: src/webserver.cpp pthread_kill call --- src/webserver.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/webserver.cpp b/src/webserver.cpp index 84843c13..a5f74ba6 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -586,7 +586,7 @@ bool webserver::start(bool blocking) } catch (::httpserver::webserver_exception &e) { this->running = false; for (;i >= 0; --i) { - pthread_kill(&threads[i], 9); + pthread_kill(threads[i], 9); } threads.clear(); throw e; From 35fd51c75f51639665a84f615c9ec952863c5d15 Mon Sep 17 00:00:00 2001 From: Marcel Pursche Date: Thu, 19 Nov 2015 13:23:19 +0100 Subject: [PATCH 167/623] Added proper error handling to tcp socket creation and binding. --- src/webserver.cpp | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/src/webserver.cpp b/src/webserver.cpp index a5f74ba6..df07cc27 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -457,6 +457,7 @@ bool webserver::start(bool blocking) { int on = 1; bool bind_settled = true; + int result = 0; if(!bind_socket) { bind_settled = false; @@ -501,11 +502,23 @@ bool webserver::start(bool blocking) else bind_socket = create_socket (PF_INET, SOCK_STREAM, 0); - setsockopt (bind_socket, + if (bind_socket == -1) + { + cout << "Unable to create socket" << endl; + throw ::httpserver::webserver_exception(); + } + + result = setsockopt (bind_socket, SOL_SOCKET, SO_REUSEADDR, (const char*) &on, sizeof (on)); + if (result != 0) + { + cout << "Unable to set socket option SO_REUSEADDR" << endl; + throw ::httpserver::webserver_exception(); + } + if(use_ipv6) { #ifdef IPPROTO_IPV6 @@ -517,7 +530,13 @@ bool webserver::start(bool blocking) #endif #endif } - bind(bind_socket, servaddr, addrlen); + result = bind(bind_socket, servaddr, addrlen); + + if (result != 0) + { + cout << gettext("Unable to bind socket to port: ") << port << endl; + throw ::httpserver::webserver_exception(); + } } #ifdef _WINDOWS unsigned long ioarg = 1; @@ -528,7 +547,14 @@ bool webserver::start(bool blocking) fcntl (bind_socket, F_SETFL, flags); #endif if(!bind_settled) - listen(bind_socket, 1); + { + result = listen(bind_socket, 1); + if (result != 0) + { + cout << gettext("Unable to listen on port: ") << port << endl; + throw ::httpserver::webserver_exception(); + } + } iov.push_back(gen(MHD_OPTION_LISTEN_SOCKET, bind_socket)); } From 4c9b4a7cd1bb7b1585bc81c5850a513f03a67aaf Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sat, 26 Dec 2015 15:13:18 +0100 Subject: [PATCH 168/623] Changed http_resource to use virtual istead of CRTP --- ChangeLog | 3 + configure.ac | 4 +- debian/changelog.in | 5 + examples/hello_world.cpp | 2 +- examples/service.cpp | 2 +- src/Makefile.am | 2 +- src/http_resource.cpp | 11 + src/http_response.cpp | 3 +- src/httpserver.hpp | 1 - src/httpserver/details/cache_entry.hpp | 1 - src/httpserver/details/event_tuple.hpp | 2 + src/httpserver/details/http_endpoint.hpp | 5 +- .../details/http_resource_mirror.hpp | 206 ------------------ src/httpserver/details/http_response_ptr.hpp | 2 - src/httpserver/details/modded_request.hpp | 4 +- src/httpserver/http_resource.hpp | 58 ++--- src/httpserver/http_response.hpp | 4 +- src/httpserver/webserver.hpp | 20 +- src/webserver.cpp | 92 ++------ test/integ/basic.cpp | 16 +- 20 files changed, 97 insertions(+), 346 deletions(-) delete mode 100644 src/httpserver/details/http_resource_mirror.hpp diff --git a/ChangeLog b/ChangeLog index 4bd29c93..bc606900 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,6 @@ +Sat Jan 26 15:08:22 2016 +0100 + Changed http_resource to use classic C++ polymorphism using virtual instead of CRTP + Fri Jul 17 21:38:54 2015 +0000 Removed build dependency on pkg-config diff --git a/configure.ac b/configure.ac index 1beb4292..8a838ea7 100644 --- a/configure.ac +++ b/configure.ac @@ -21,8 +21,8 @@ AC_PREREQ(2.57) m4_define([libhttpserver_MAJOR_VERSION],[0])dnl -m4_define([libhttpserver_MINOR_VERSION],[9])dnl -m4_define([libhttpserver_REVISION],[1])dnl +m4_define([libhttpserver_MINOR_VERSION],[10])dnl +m4_define([libhttpserver_REVISION],[0])dnl m4_define([libhttpserver_PKG_VERSION],[libhttpserver_MAJOR_VERSION.libhttpserver_MINOR_VERSION.libhttpserver_REVISION])dnl m4_define([libhttpserver_LDF_VERSION],[libhttpserver_MAJOR_VERSION:libhttpserver_MINOR_VERSION:libhttpserver_REVISION])dnl AC_INIT([libhttpserver], libhttpserver_PKG_VERSION, [electrictwister2000@gmail.com]) diff --git a/debian/changelog.in b/debian/changelog.in index 6f63bcda..b06c4594 100644 --- a/debian/changelog.in +++ b/debian/changelog.in @@ -1,3 +1,8 @@ +libhttpserver (0.10.0) unstable; urgency=low + * Changed http_resource to use classic C++ polymorphism using virtual instead of CRTP + + -- Sebastiano Merlino Sat, 26 Jan 2016 15:08:22 +0100 + libhttpserver (0.9.1) unstable; urgency=low * Eliminated build dependency on pkg-config diff --git a/examples/hello_world.cpp b/examples/hello_world.cpp index e5d37d9a..93a8f790 100755 --- a/examples/hello_world.cpp +++ b/examples/hello_world.cpp @@ -23,7 +23,7 @@ using namespace httpserver; -class hello_world_resource : public http_resource { +class hello_world_resource : public http_resource { public: void render(const http_request&, http_response**); void set_some_data(const std::string &s) {data = s;} diff --git a/examples/service.cpp b/examples/service.cpp index 537ebf97..b374c0de 100644 --- a/examples/service.cpp +++ b/examples/service.cpp @@ -27,7 +27,7 @@ using namespace httpserver; bool verbose=false; -class service_resource: public http_resource { +class service_resource: public http_resource { public: service_resource(); diff --git a/src/Makefile.am b/src/Makefile.am index 51299bf3..4e4b989f 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -21,7 +21,7 @@ METASOURCES = AUTO lib_LTLIBRARIES = libhttpserver.la libhttpserver_la_SOURCES = string_utilities.cpp webserver.cpp http_utils.cpp http_request.cpp http_response.cpp http_resource.cpp details/comet_manager.cpp details/http_endpoint.cpp noinst_HEADERS = httpserver/string_utilities.hpp httpserver/details/modded_request.hpp httpserver/details/http_response_ptr.hpp httpserver/details/cache_entry.hpp httpserver/details/comet_manager.hpp gettext.h -nobase_include_HEADERS = httpserver.hpp httpserver/create_webserver.hpp httpserver/webserver.hpp httpserver/http_utils.hpp httpserver/details/http_endpoint.hpp httpserver/http_request.hpp httpserver/http_response.hpp httpserver/http_resource.hpp httpserver/binders.hpp httpserver/event_supplier.hpp httpserver/details/event_tuple.hpp httpserver/details/http_resource_mirror.hpp httpserver/http_response_builder.hpp +nobase_include_HEADERS = httpserver.hpp httpserver/create_webserver.hpp httpserver/webserver.hpp httpserver/http_utils.hpp httpserver/details/http_endpoint.hpp httpserver/http_request.hpp httpserver/http_response.hpp httpserver/http_resource.hpp httpserver/binders.hpp httpserver/event_supplier.hpp httpserver/details/event_tuple.hpp httpserver/http_response_builder.hpp AM_CXXFLAGS += -fPIC -Wall diff --git a/src/http_resource.cpp b/src/http_resource.cpp index 693f33cf..73840745 100644 --- a/src/http_resource.cpp +++ b/src/http_resource.cpp @@ -27,6 +27,7 @@ #include "details/event_tuple.hpp" #include "webserver.hpp" #include "string_utilities.hpp" +#include "http_response_builder.hpp" using namespace std; @@ -45,4 +46,14 @@ void resource_init(map& allowed_methods) allowed_methods[MHD_HTTP_METHOD_OPTIONS] = true; } +namespace details +{ + +void empty_render(const http_request& r, http_response** res) +{ + *res = new http_response(http_response_builder("", 200).string_response()); +} + +}; + }; diff --git a/src/http_response.cpp b/src/http_response.cpp index cddf3cbd..35558285 100644 --- a/src/http_response.cpp +++ b/src/http_response.cpp @@ -25,7 +25,6 @@ #include #include #include "http_utils.hpp" -#include "details/http_resource_mirror.hpp" #include "details/event_tuple.hpp" #include "webserver.hpp" #include "http_response.hpp" @@ -36,6 +35,8 @@ using namespace std; namespace httpserver { +class webserver; + http_response::http_response(const http_response_builder& builder): content(builder._content_hook), response_code(builder._response_code), diff --git a/src/httpserver.hpp b/src/httpserver.hpp index 74470d37..20f688b5 100644 --- a/src/httpserver.hpp +++ b/src/httpserver.hpp @@ -31,7 +31,6 @@ #include "httpserver/http_request.hpp" #include "httpserver/event_supplier.hpp" #include "httpserver/details/event_tuple.hpp" -#include "httpserver/details/http_resource_mirror.hpp" #include "httpserver/webserver.hpp" #endif diff --git a/src/httpserver/details/cache_entry.hpp b/src/httpserver/details/cache_entry.hpp index 3bf18816..c1fc560a 100644 --- a/src/httpserver/details/cache_entry.hpp +++ b/src/httpserver/details/cache_entry.hpp @@ -28,7 +28,6 @@ #include #include #include "httpserver/details/http_response_ptr.hpp" -#include "httpserver/http_response.hpp" namespace httpserver { diff --git a/src/httpserver/details/event_tuple.hpp b/src/httpserver/details/event_tuple.hpp index 8a005c6e..71786b4b 100644 --- a/src/httpserver/details/event_tuple.hpp +++ b/src/httpserver/details/event_tuple.hpp @@ -30,6 +30,8 @@ namespace httpserver { +class webserver; + namespace details { class http_endpoint; diff --git a/src/httpserver/details/http_endpoint.hpp b/src/httpserver/details/http_endpoint.hpp index a514f7e7..f366f7dc 100644 --- a/src/httpserver/details/http_endpoint.hpp +++ b/src/httpserver/details/http_endpoint.hpp @@ -34,12 +34,11 @@ namespace httpserver { class webserver; +class http_resource; namespace details { -class http_resource_mirror; - /** * Exception class throwed when a bad formatted http url is used **/ @@ -237,7 +236,7 @@ class http_endpoint friend void _register_resource( webserver*, const std::string&, - details::http_resource_mirror&, + const http_resource&, bool ); template friend struct std::pair; diff --git a/src/httpserver/details/http_resource_mirror.hpp b/src/httpserver/details/http_resource_mirror.hpp deleted file mode 100644 index e8b9d12a..00000000 --- a/src/httpserver/details/http_resource_mirror.hpp +++ /dev/null @@ -1,206 +0,0 @@ -/* - This file is part of libhttpserver - Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 - USA -*/ - -#if !defined (_HTTPSERVER_HPP_INSIDE_) && !defined (HTTPSERVER_COMPILATION) -#error "Only or can be included directly." -#endif - -#ifndef _HTTP_RESOURCE_MIRROR_HPP_ -#define _HTTP_RESOURCE_MIRROR_HPP_ - -#include "httpserver/binders.hpp" - -#define CREATE_METHOD_DETECTOR(X) \ - template \ - class has_##X \ - { \ - template struct Check; \ - template static char func(Check *); \ - template static int func(...); \ - public: \ - enum { value = sizeof(func(0)) == sizeof(char) }; \ - }; - -#define HAS_METHOD(X, T, RESULT, ARG1, ARG2) \ - has_##X::value - -namespace httpserver { - -template class http_resource; -class http_request; -class http_response; -class webserver; - -namespace details -{ - class http_endpoint; - struct modded_request; - struct daemon_item; - typedef bool(*is_allowed_ptr)(const std::string&); - - CREATE_METHOD_DETECTOR(render); - CREATE_METHOD_DETECTOR(render_GET); - CREATE_METHOD_DETECTOR(render_POST); - CREATE_METHOD_DETECTOR(render_PUT); - CREATE_METHOD_DETECTOR(render_HEAD); - CREATE_METHOD_DETECTOR(render_DELETE); - CREATE_METHOD_DETECTOR(render_TRACE); - CREATE_METHOD_DETECTOR(render_OPTIONS); - CREATE_METHOD_DETECTOR(render_CONNECT); - CREATE_METHOD_DETECTOR(render_not_acceptable); - - void empty_render(const http_request& r, http_response** res); - void empty_not_acceptable_render( - const http_request& r, http_response** res - ); - bool empty_is_allowed(const std::string& method); - - class http_resource_mirror - { - public: - http_resource_mirror() - { - } - - ~http_resource_mirror() - { - } - private: - typedef binders::functor_two functor; - - typedef binders::functor_one functor_allowed; - - const functor render; - const functor render_GET; - const functor render_POST; - const functor render_PUT; - const functor render_HEAD; - const functor render_DELETE; - const functor render_TRACE; - const functor render_OPTIONS; - const functor render_CONNECT; - const functor_allowed is_allowed; - - functor method_not_acceptable_resource; - - http_resource_mirror& operator= (const http_resource_mirror& o) - { - return *this; - } - - template - http_resource_mirror(http_resource* res): - render( - HAS_METHOD(render, T, void, - const http_request&, http_response** - ) ? functor(res, &T::render) : functor(&empty_render) - ), - render_GET( - HAS_METHOD(render_GET, T, void, - const http_request&, http_response** - ) ? functor(res, &T::render_GET) : - ( - HAS_METHOD(render, T, void, - const http_request&, http_response** - ) ? functor(res, &T::render) : functor(&empty_render) - ) - ), - render_POST( - HAS_METHOD(render_POST, T, void, - const http_request&, http_response** - ) ? functor(res, &T::render_POST) : - ( - HAS_METHOD(render, T, void, - const http_request&, http_response** - ) ? functor(res, &T::render) : functor(&empty_render) - ) - ), - render_PUT( - HAS_METHOD(render_PUT, T, void, - const http_request&, http_response** - ) ? functor(res, &T::render_PUT) : - ( - HAS_METHOD(render, T, void, - const http_request&, http_response** - ) ? functor(res, &T::render) : functor(&empty_render) - ) - ), - render_HEAD( - HAS_METHOD(render_HEAD, T, void, - const http_request&, http_response** - ) ? functor(res, &T::render_HEAD) : - ( - HAS_METHOD(render, T, void, - const http_request&, http_response** - ) ? functor(res, &T::render) : functor(&empty_render) - ) - ), - render_DELETE( - HAS_METHOD(render_DELETE, T, void, - const http_request&, http_response** - ) ? functor(res, &T::render_DELETE) : - ( - HAS_METHOD(render, T, void, - const http_request&, http_response** - ) ? functor(res, &T::render) : functor(&empty_render) - ) - ), - render_TRACE( - HAS_METHOD(render_TRACE, T, void, - const http_request&, http_response** - ) ? functor(res, &T::render_TRACE) : - ( - HAS_METHOD(render, T, void, - const http_request&, http_response** - ) ? functor(res, &T::render) : functor(&empty_render) - ) - ), - render_OPTIONS( - HAS_METHOD(render_OPTIONS, T, void, - const http_request&, http_response** - ) ? functor(res, &T::render_OPTIONS) : - ( - HAS_METHOD(render, T, void, - const http_request&, http_response** - ) ? functor(res, &T::render) : functor(&empty_render) - ) - ), - render_CONNECT( - HAS_METHOD(render_CONNECT, T, void, - const http_request&, http_response** - ) ? functor(res, &T::render_CONNECT) : - ( - HAS_METHOD(render, T, void, - const http_request&, http_response** - ) ? functor(res, &T::render) : functor(&empty_render) - ) - ), - is_allowed(res, &T::is_allowed) - { - } - - friend class ::httpserver::webserver; - }; -} //details -} //httpserver - -#endif //_HTTP_RESOURCE_MIRROR_HPP_ diff --git a/src/httpserver/details/http_response_ptr.hpp b/src/httpserver/details/http_response_ptr.hpp index 6435c449..860e0575 100644 --- a/src/httpserver/details/http_response_ptr.hpp +++ b/src/httpserver/details/http_response_ptr.hpp @@ -25,8 +25,6 @@ #ifndef _HTTP_RESPONSE_PTR_HPP_ #define _HTTP_RESPONSE_PTR_HPP_ -#include "httpserver/http_response.hpp" - namespace httpserver { diff --git a/src/httpserver/details/modded_request.hpp b/src/httpserver/details/modded_request.hpp index a3850dd2..42819a03 100644 --- a/src/httpserver/details/modded_request.hpp +++ b/src/httpserver/details/modded_request.hpp @@ -41,9 +41,7 @@ struct modded_request std::string* standardized_url; webserver* ws; - const binders::functor_two< - const http_request&, http_response**, void - > http_resource_mirror::*callback; + void (httpserver::http_resource::*callback)(const httpserver::http_request&, httpserver::http_response**); http_request* dhr; http_response_ptr dhrs; diff --git a/src/httpserver/http_resource.hpp b/src/httpserver/http_resource.hpp index 3755c32d..72215102 100644 --- a/src/httpserver/http_resource.hpp +++ b/src/httpserver/http_resource.hpp @@ -38,96 +38,109 @@ class webserver; class http_request; class http_response; +namespace details +{ + +void empty_render(const http_request& r, http_response** res); + +}; + /** * Class representing a callable http resource. **/ void resource_init(std::map& res); -template class http_resource { public: + /** + * Class destructor + **/ + virtual ~http_resource() + { + } + /** * Method used to answer to a generic request * @param req Request passed through http * @return A http_response object **/ - void render(const http_request& r, http_response** res) + virtual void render(const http_request& r, http_response** res) { - static_cast(this)->render(r, res); + details::empty_render(r, res); } /** * Method used to answer to a GET request * @param req Request passed through http * @return A http_response object **/ - void render_GET(const http_request& req, http_response** res) + virtual void render_GET(const http_request& req, http_response** res) { - static_cast(this)->render_GET(req, res); + details::empty_render(req, res); } /** * Method used to answer to a POST request * @param req Request passed through http * @return A http_response object **/ - void render_POST(const http_request& req, http_response** res) + virtual void render_POST(const http_request& req, http_response** res) { - static_cast(this)->render_POST(req, res); + details::empty_render(req, res); } /** * Method used to answer to a PUT request * @param req Request passed through http * @return A http_response object **/ - void render_PUT(const http_request& req, http_response** res) + virtual void render_PUT(const http_request& req, http_response** res) { - static_cast(this)->render_PUT(req, res); + details::empty_render(req, res); } /** * Method used to answer to a HEAD request * @param req Request passed through http * @return A http_response object **/ - void render_HEAD(const http_request& req, http_response** res) + virtual void render_HEAD(const http_request& req, http_response** res) { - static_cast(this)->render_HEAD(req, res); + details::empty_render(req, res); } /** * Method used to answer to a DELETE request * @param req Request passed through http * @return A http_response object **/ - void render_DELETE(const http_request& req, http_response** res) + virtual void render_DELETE(const http_request& req, http_response** res) { - static_cast(this)->render_DELETE(req, res); + details::empty_render(req, res); } /** * Method used to answer to a TRACE request * @param req Request passed through http * @return A http_response object **/ - void render_TRACE(const http_request& req, http_response** res) + virtual void render_TRACE(const http_request& req, http_response** res) { - static_cast(this)->render_TRACE(req, res); + details::empty_render(req, res); } /** * Method used to answer to a OPTIONS request * @param req Request passed through http * @return A http_response object **/ - void render_OPTIONS(const http_request& req, http_response** res) + virtual void render_OPTIONS(const http_request& req, http_response** res) { - static_cast(this)->render_OPTIONS(req, res); + details::empty_render(req, res); } /** * Method used to answer to a CONNECT request * @param req Request passed through http * @return A http_response object **/ - void render_CONNECT(const http_request& req, http_response** res) + virtual void render_CONNECT(const http_request& req, http_response** res) { - static_cast(this)->render_CONNECT(req, res); + details::empty_render(req, res); } /** * Method used to set if a specific method is allowed or not on this request @@ -201,13 +214,6 @@ class http_resource return (*this); } - /** - * Class destructor - **/ - ~http_resource() - { - } - private: friend class webserver; friend void resource_init(std::map& res); diff --git a/src/httpserver/http_response.hpp b/src/httpserver/http_response.hpp index cc55e2de..21ef6344 100644 --- a/src/httpserver/http_response.hpp +++ b/src/httpserver/http_response.hpp @@ -28,8 +28,10 @@ #include #include #include +#include +#include -#include "httpserver/details/http_resource_mirror.hpp" +#include "httpserver/binders.hpp" struct MHD_Connection; diff --git a/src/httpserver/webserver.hpp b/src/httpserver/webserver.hpp index 06c58fb9..8d4310ea 100644 --- a/src/httpserver/webserver.hpp +++ b/src/httpserver/webserver.hpp @@ -47,7 +47,7 @@ namespace httpserver { -template class http_resource; +class http_resource; class http_response; template class event_supplier; class create_webserver; @@ -58,7 +58,6 @@ struct httpserver_ska; }; namespace details { - class http_resource_mirror; class event_tuple; class http_endpoint; struct daemon_item; @@ -112,14 +111,9 @@ class webserver * @param family boolean indicating whether the resource is registered for the endpoint and its child or not. * @return true if the resource was registered **/ - template bool register_resource(const std::string& resource, - http_resource* res, bool family = false - ) - { - details::http_resource_mirror hrm(res); - return register_resource(resource, hrm, family); - } + http_resource* res, bool family = false + ); void unregister_resource(const std::string& resource); void ban_ip(const std::string& ip); @@ -242,8 +236,8 @@ class webserver render_ptr method_not_allowed_resource; render_ptr method_not_acceptable_resource; render_ptr internal_error_resource; - std::map registered_resources; - std::map registered_resources_str; + std::map registered_resources; + std::map registered_resources_str; std::map response_cache; int next_to_choose; @@ -261,10 +255,6 @@ class webserver static void* select(void* self); static void* cleaner(void* self); - bool register_resource(const std::string& resource, - details::http_resource_mirror hrm, bool family = false - ); - void method_not_allowed_page(http_response** dhrs, details::modded_request* mr ); diff --git a/src/webserver.cpp b/src/webserver.cpp index df07cc27..7b6005ee 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -50,7 +50,6 @@ #include "http_response_builder.hpp" #include "details/http_endpoint.hpp" #include "string_utilities.hpp" -#include "details/http_resource_mirror.hpp" #include "details/event_tuple.hpp" #include "create_webserver.hpp" #include "details/comet_manager.hpp" @@ -87,21 +86,6 @@ struct daemon_item } }; -void empty_render(const http_request& r, http_response** res) -{ - *res = new http_response(http_response_builder("", 200).string_response()); -} - -void empty_not_acceptable_render(const http_request& r, http_response** res) -{ - *res = new http_response(http_response_builder(NOT_METHOD_ERROR, 200).string_response()); -} - -bool empty_is_allowed(const std::string& method) -{ - return true; -} - } using namespace http; @@ -240,25 +224,18 @@ void webserver::request_completed ( } } -bool webserver::register_resource( - const std::string& resource, - details::http_resource_mirror hrm, - bool family -) +bool webserver::register_resource(const std::string& resource, http_resource* hrm, bool family) { - if(method_not_acceptable_resource) - hrm.method_not_acceptable_resource = method_not_acceptable_resource; - details::http_endpoint idx(resource, family, true, regex_checking); - pair::iterator, bool> result = registered_resources.insert( - map::value_type(idx, hrm) + pair::iterator, bool> result = registered_resources.insert( + map::value_type(idx, hrm) ); if(result.second) { registered_resources_str.insert( - pair(idx.get_url_complete(), &(result.first->second)) + pair(idx.get_url_complete(), result.first->second) ); } @@ -877,27 +854,6 @@ void webserver::not_found_page( *dhrs = new http_response(http_response_builder(NOT_FOUND_ERROR, http_utils::http_not_found).string_response()); } -int webserver::method_not_acceptable_page (const void *cls, - struct MHD_Connection *connection) -{ - int ret; - struct MHD_Response *response; - - /* unsupported HTTP method */ - response = MHD_create_response_from_buffer (strlen (NOT_METHOD_ERROR), - (void *) NOT_METHOD_ERROR, - MHD_RESPMEM_PERSISTENT); - ret = MHD_queue_response (connection, - MHD_HTTP_METHOD_NOT_ACCEPTABLE, - response); - MHD_add_response_header (response, - MHD_HTTP_HEADER_CONTENT_ENCODING, - "text/plain"); - MHD_destroy_response (response); - - return ret; -} - void webserver::method_not_allowed_page( http_response** dhrs, details::modded_request* mr @@ -1068,9 +1024,9 @@ int webserver::finalize_answer( int to_ret = MHD_NO; http_response* dhrs = 0x0; - map::iterator fe; + map::iterator fe; - details::http_resource_mirror* hrm; + http_resource* hrm; bool found = false; struct MHD_Response* raw_response; @@ -1084,7 +1040,7 @@ int webserver::finalize_answer( { map< - details::http_endpoint, details::http_resource_mirror + details::http_endpoint, http_resource* >::iterator found_endpoint; details::http_endpoint endpoint( @@ -1093,7 +1049,7 @@ int webserver::finalize_answer( map< details::http_endpoint, - details::http_resource_mirror + http_resource* >::iterator it; size_t len = 0; @@ -1139,7 +1095,7 @@ int webserver::finalize_answer( mr->dhr->set_arg(url_pars[i], url_pieces[chunkes[i]]); } - hrm = &found_endpoint->second; + hrm = found_endpoint->second; } } } @@ -1151,7 +1107,7 @@ int webserver::finalize_answer( } else { - hrm = ®istered_resources.begin()->second; + hrm = registered_resources.begin()->second; found = true; } mr->dhr->set_underlying_connection(connection); @@ -1275,49 +1231,37 @@ int webserver::answer_to_connection(void* cls, MHD_Connection* connection, if( 0 == strcasecmp(method, http_utils::http_method_get.c_str())) { - mr->callback = &details::http_resource_mirror::render_GET; + mr->callback = &http_resource::render_GET; } else if (0 == strcmp(method, http_utils::http_method_post.c_str())) { - mr->callback = &details::http_resource_mirror::render_POST; + mr->callback = &http_resource::render_POST; body = true; } else if (0 == strcasecmp(method, http_utils::http_method_put.c_str())) { - mr->callback = &details::http_resource_mirror::render_PUT; + mr->callback = &http_resource::render_PUT; body = true; } else if (0 == strcasecmp(method,http_utils::http_method_delete.c_str())) { - mr->callback = &details::http_resource_mirror::render_DELETE; + mr->callback = &http_resource::render_DELETE; } else if (0 == strcasecmp(method, http_utils::http_method_head.c_str())) { - mr->callback = &details::http_resource_mirror::render_HEAD; + mr->callback = &http_resource::render_HEAD; } else if (0 ==strcasecmp(method,http_utils::http_method_connect.c_str())) { - mr->callback = &details::http_resource_mirror::render_CONNECT; + mr->callback = &http_resource::render_CONNECT; } else if (0 == strcasecmp(method, http_utils::http_method_trace.c_str())) { - mr->callback = &details::http_resource_mirror::render_TRACE; + mr->callback = &http_resource::render_TRACE; } else if (0 ==strcasecmp(method,http_utils::http_method_options.c_str())) { - mr->callback = &details::http_resource_mirror::render_OPTIONS; - } - else - { - using namespace details; - if(static_cast(cls)->method_not_acceptable_resource) - mr->callback = - &http_resource_mirror::method_not_acceptable_resource; - else - return static_cast(cls)->method_not_acceptable_page( - cls, - connection - ); + mr->callback = &http_resource::render_OPTIONS; } if(body) diff --git a/test/integ/basic.cpp b/test/integ/basic.cpp index 597067f3..a00d8abf 100644 --- a/test/integ/basic.cpp +++ b/test/integ/basic.cpp @@ -45,7 +45,7 @@ size_t headerfunc(void *ptr, size_t size, size_t nmemb, map* ss) return size*nmemb; } -class simple_resource : public http_resource +class simple_resource : public http_resource { public: void render_GET(const http_request& req, http_response** res) @@ -60,7 +60,7 @@ class simple_resource : public http_resource } }; -class long_content_resource : public http_resource +class long_content_resource : public http_resource { public: void render_GET(const http_request& req, http_response** res) @@ -69,7 +69,7 @@ class long_content_resource : public http_resource } }; -class header_test_resource : public http_resource +class header_test_resource : public http_resource { public: void render_GET(const http_request& req, http_response** res) @@ -80,7 +80,7 @@ class header_test_resource : public http_resource } }; -class complete_test_resource : public http_resource +class complete_test_resource : public http_resource { public: void render_GET(const http_request& req, http_response** res) @@ -105,7 +105,7 @@ class complete_test_resource : public http_resource } }; -class only_render_resource : public http_resource +class only_render_resource : public http_resource { public: void render(const http_request& req, http_response** res) @@ -114,7 +114,7 @@ class only_render_resource : public http_resource } }; -class ok_resource : public http_resource +class ok_resource : public http_resource { public: void render_GET(const http_request& req, http_response** res) @@ -123,7 +123,7 @@ class ok_resource : public http_resource } }; -class nok_resource : public http_resource +class nok_resource : public http_resource { public: void render_GET(const http_request& req, http_response** res) @@ -132,7 +132,7 @@ class nok_resource : public http_resource } }; -class no_response_resource : public http_resource +class no_response_resource : public http_resource { public: void render_GET(const http_request& req, http_response** res) From df63a5de0aa8c4748bde5224cc45354767396579 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sat, 26 Dec 2015 15:26:26 +0100 Subject: [PATCH 169/623] Added travis dependency packages --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index ba2f2005..b32ee0ad 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,7 +6,7 @@ env: - DEBUG="debug" - DEBUG="nodebug" before_install: - - sudo apt-get install texinfo + - sudo apt-get install info install-info - sudo pip install cpp-coveralls - sudo pip install gcovr - sudo apt-get install libjson-perl From 72dd03f90cd13eab0645a31c5db9c222d109c173 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sat, 26 Dec 2015 15:34:25 +0100 Subject: [PATCH 170/623] Fixing travis since apt dependency changed --- .travis.yml | 3 --- coveralls-debug | 32 -------------------------------- 2 files changed, 35 deletions(-) delete mode 100755 coveralls-debug diff --git a/.travis.yml b/.travis.yml index b32ee0ad..96a9e68c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,9 +9,6 @@ before_install: - sudo apt-get install info install-info - sudo pip install cpp-coveralls - sudo pip install gcovr - - sudo apt-get install libjson-perl - - sudo apt-get install libjson-xs-perl - - sudo apt-get install libfile-slurp-perl - wget ftp://ftp.gnu.org/gnu/libmicrohttpd/libmicrohttpd-0.9.37.tar.gz - tar -xvzf libmicrohttpd-0.9.37.tar.gz - cd libmicrohttpd-0.9.37 diff --git a/coveralls-debug b/coveralls-debug deleted file mode 100755 index 11a32b1d..00000000 --- a/coveralls-debug +++ /dev/null @@ -1,32 +0,0 @@ -#!/usr/bin/perl - -use strict; -use warnings; -use JSON; -use File::Slurp; -my $json = JSON->new; -my $file = read_file('coveralls.json', { binmode => ':utf8' }); -my $rv = $json->decode($file); -my $sources = $rv->{'source_files'}; -print STDERR join ", ", keys %{$rv}, "\n"; -foreach my $source (sort { - $a->{'name'} cmp $b->{'name'} -} @{$sources}) -{ - my $sum = 0; - my $undefs = 0; - my $coverages = $source->{'coverage'}; - foreach my $coverage (@{$coverages}) - { - if (defined $coverage) - { $sum += $coverage } - else { $undefs ++; } - } - if ($sum > 0) - { - print STDERR $source->{'name'}; - print STDERR " [sum: $sum]"; - print STDERR " [undefs: $undefs]"; - print STDERR "\n"; - } -} From 354daf9f475f24faf6c86104613e8ee855330627 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sat, 26 Dec 2015 15:39:01 +0100 Subject: [PATCH 171/623] Updated authors file to include latest contributors --- AUTHORS | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/AUTHORS b/AUTHORS index 8bfdb858..6a68d8b7 100644 --- a/AUTHORS +++ b/AUTHORS @@ -18,3 +18,9 @@ Jan Klimke - Example of SSL usage and operator<< on http_request and http_response Chris Love + +- Added proper error handling to tcp socket creation and binding +Marcel Pursche + +- Fixed error management and regex handling +Julian Picht From 2c99b029154c3685cb0d47885ff494903a06ec34 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sat, 26 Dec 2015 16:29:52 +0100 Subject: [PATCH 172/623] Fixed http_resource to inherit behaviour from render in all render methods --- src/httpserver/http_resource.hpp | 16 ++++++++-------- test/integ/basic.cpp | 24 ++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 8 deletions(-) diff --git a/src/httpserver/http_resource.hpp b/src/httpserver/http_resource.hpp index 72215102..8b89684e 100644 --- a/src/httpserver/http_resource.hpp +++ b/src/httpserver/http_resource.hpp @@ -77,7 +77,7 @@ class http_resource **/ virtual void render_GET(const http_request& req, http_response** res) { - details::empty_render(req, res); + render(req, res); } /** * Method used to answer to a POST request @@ -86,7 +86,7 @@ class http_resource **/ virtual void render_POST(const http_request& req, http_response** res) { - details::empty_render(req, res); + render(req, res); } /** * Method used to answer to a PUT request @@ -95,7 +95,7 @@ class http_resource **/ virtual void render_PUT(const http_request& req, http_response** res) { - details::empty_render(req, res); + render(req, res); } /** * Method used to answer to a HEAD request @@ -104,7 +104,7 @@ class http_resource **/ virtual void render_HEAD(const http_request& req, http_response** res) { - details::empty_render(req, res); + render(req, res); } /** * Method used to answer to a DELETE request @@ -113,7 +113,7 @@ class http_resource **/ virtual void render_DELETE(const http_request& req, http_response** res) { - details::empty_render(req, res); + render(req, res); } /** * Method used to answer to a TRACE request @@ -122,7 +122,7 @@ class http_resource **/ virtual void render_TRACE(const http_request& req, http_response** res) { - details::empty_render(req, res); + render(req, res); } /** * Method used to answer to a OPTIONS request @@ -131,7 +131,7 @@ class http_resource **/ virtual void render_OPTIONS(const http_request& req, http_response** res) { - details::empty_render(req, res); + render(req, res); } /** * Method used to answer to a CONNECT request @@ -140,7 +140,7 @@ class http_resource **/ virtual void render_CONNECT(const http_request& req, http_response** res) { - details::empty_render(req, res); + render(req, res); } /** * Method used to set if a specific method is allowed or not on this request diff --git a/test/integ/basic.cpp b/test/integ/basic.cpp index a00d8abf..69530c24 100644 --- a/test/integ/basic.cpp +++ b/test/integ/basic.cpp @@ -304,48 +304,72 @@ LT_BEGIN_AUTO_TEST(basic_suite, only_render) CURL* curl; CURLcode res; + s = ""; curl = curl_easy_init(); curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); res = curl_easy_perform(curl); LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "OK"); curl_easy_cleanup(curl); + s = ""; curl = curl_easy_init(); curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "DELETE"); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); res = curl_easy_perform(curl); LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "OK"); curl_easy_cleanup(curl); + s = ""; curl = curl_easy_init(); curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PUT"); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); res = curl_easy_perform(curl); LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "OK"); curl_easy_cleanup(curl); + s = ""; curl = curl_easy_init(); curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "CONNECT"); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); res = curl_easy_perform(curl); LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "OK"); curl_easy_cleanup(curl); + s = ""; curl = curl_easy_init(); curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "NOT_EXISTENT"); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); res = curl_easy_perform(curl); LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "Method not Allowed"); curl_easy_cleanup(curl); + s = ""; curl = curl_easy_init(); curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); curl_easy_setopt(curl, CURLOPT_POST, 1L); curl_easy_setopt(curl, CURLOPT_POSTFIELDS, NULL); curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, 0); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); res = curl_easy_perform(curl); LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "OK"); curl_easy_cleanup(curl); LT_END_AUTO_TEST(only_render) From a73055165b18d6818cf5e178a155ad5343868f08 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 27 Dec 2015 19:42:27 +0100 Subject: [PATCH 173/623] Updated the library to use latest libmicrohttpd improvements * Removed POLL start config. Now THREAD defaults to POLL on windows and EPOLL on linux * Use TCP_FASTOPEN on linux >= 3.6o --- ChangeLog | 4 ++++ configure.ac | 24 +++++++++++++++++------- debian/changelog.in | 6 ++++++ src/httpserver/http_utils.hpp | 7 +++++-- src/webserver.cpp | 4 ++++ 5 files changed, 36 insertions(+), 9 deletions(-) diff --git a/ChangeLog b/ChangeLog index bc606900..28c63573 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +Sun Jan 27 19:39:01 2016 +0100 + Removed POLL start configuration (THREAD now defaults to POLL or EPOLL on Linux) + Use TCP_FASTOPEN on linux >= 3.6 + Sat Jan 26 15:08:22 2016 +0100 Changed http_resource to use classic C++ polymorphism using virtual instead of CRTP diff --git a/configure.ac b/configure.ac index 8a838ea7..ef0225cc 100644 --- a/configure.ac +++ b/configure.ac @@ -22,7 +22,7 @@ AC_PREREQ(2.57) m4_define([libhttpserver_MAJOR_VERSION],[0])dnl m4_define([libhttpserver_MINOR_VERSION],[10])dnl -m4_define([libhttpserver_REVISION],[0])dnl +m4_define([libhttpserver_REVISION],[1])dnl m4_define([libhttpserver_PKG_VERSION],[libhttpserver_MAJOR_VERSION.libhttpserver_MINOR_VERSION.libhttpserver_REVISION])dnl m4_define([libhttpserver_LDF_VERSION],[libhttpserver_MAJOR_VERSION:libhttpserver_MINOR_VERSION:libhttpserver_REVISION])dnl AC_INIT([libhttpserver], libhttpserver_PKG_VERSION, [electrictwister2000@gmail.com]) @@ -56,19 +56,21 @@ if test x"$samedirectory" = x"no"; then fi fi +is_windows=yes; case "$host" in *-mingw*) - NETWORK_HEADER="winsock2.h" - REGEX_LIBS="-lregex -lpthread -no-undefined" + NETWORK_HEADER="winsock2.h" + REGEX_LIBS="-lregex -lpthread -no-undefined" native_srcdir=$(cd $srcdir; pwd -W) ;; *-cygwin*) - NETWORK_HEADER="winsock2.h" - REGEX_LIBS="-lregex -lpthread -no-undefined" + NETWORK_HEADER="winsock2.h" + REGEX_LIBS="-lregex -lpthread -no-undefined" ;; *) - NETWORK_HEADER="arpa/inet.h" - REGEX_LIBS="" + NETWORK_HEADER="arpa/inet.h" + REGEX_LIBS="" + is_windows=no ;; esac @@ -111,6 +113,14 @@ AC_CHECK_HEADER([microhttpd.h], CXXFLAGS="-DHTTPSERVER_COMPILATION -D_REENTRANT $LIBMICROHTTPD_CFLAGS $CXXFLAGS" LDFLAGS="$LIBMICROHTTPD_LIBS $REGEX_LIBS $LD_FLAGS" +if test x"$is_windows" = x"no"; then + if test `uname -r |cut -d. -f1` -ge 3; then + if test `uname -r |cut -d. -f2` -ge 6; then + CXXFLAGS="-DUSE_FASTOPEN $CXXFLAGS" + fi + fi +fi + m4_pattern_allow([AC_TYPE_SIZE_T]) m4_pattern_allow([AC_TYPE_UINT16_T]) m4_pattern_allow([AC_TYPE_UINT32_T]) diff --git a/debian/changelog.in b/debian/changelog.in index b06c4594..d4bf27ea 100644 --- a/debian/changelog.in +++ b/debian/changelog.in @@ -1,3 +1,9 @@ +libhttpserver (0.10.1) unstable; urgency=low + * Removed POLL start configuration (THREAD now defaults to POLL or EPOLL on Linux) + * Use TCP_FASTOPEN on linux >= 3.6 + + -- Sebastiano Merlino Sat, 27 Jan 2016 19:39:01 +0100 + libhttpserver (0.10.0) unstable; urgency=low * Changed http_resource to use classic C++ polymorphism using virtual instead of CRTP diff --git a/src/httpserver/http_utils.hpp b/src/httpserver/http_utils.hpp index 58bc8f6e..94aa1af8 100644 --- a/src/httpserver/http_utils.hpp +++ b/src/httpserver/http_utils.hpp @@ -77,8 +77,11 @@ class http_utils enum start_method_T { INTERNAL_SELECT = MHD_NO_FLAG, - THREADS = MHD_USE_THREAD_PER_CONNECTION, - POLL = MHD_USE_THREAD_PER_CONNECTION | MHD_USE_POLL +#if defined(__MINGW32__) || defined(__CYGWIN32__) + THREAD = MHD_USE_THREAD_PER_CONNECTION | MHD_USE_POLL +#else + THREAD = MHD_USE_THREAD_PER_CONNECTION | MHD_USE_EPOLL_LINUX_ONLY +#endif }; enum policy_T diff --git a/src/webserver.cpp b/src/webserver.cpp index 7b6005ee..f1b7a869 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -547,6 +547,10 @@ bool webserver::start(bool blocking) if(pedantic) start_conf |= MHD_USE_PEDANTIC_CHECKS; +#ifdef USE_FASTOPEN + start_conf |= MHD_USE_TCP_FASTOPEN; +#endif + int num_threads = 1; if(max_threads > num_threads) num_threads = max_threads; From 8786b2f4c910a69cec0095e4a8bc732cce919d50 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 27 Dec 2015 20:07:41 +0100 Subject: [PATCH 174/623] Enable fastopen only after 3.7 (server and client) rather than 3.6 (just client) --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index ef0225cc..7875d072 100644 --- a/configure.ac +++ b/configure.ac @@ -115,7 +115,7 @@ LDFLAGS="$LIBMICROHTTPD_LIBS $REGEX_LIBS $LD_FLAGS" if test x"$is_windows" = x"no"; then if test `uname -r |cut -d. -f1` -ge 3; then - if test `uname -r |cut -d. -f2` -ge 6; then + if test `uname -r |cut -d. -f2` -ge 7; then CXXFLAGS="-DUSE_FASTOPEN $CXXFLAGS" fi fi From 05c9c0c1ab656b04e6ab881250d5ac7dbaa7178c Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 27 Dec 2015 21:53:13 +0100 Subject: [PATCH 175/623] Temporary disabling fastopen in travis --- .travis.yml | 2 +- configure.ac | 21 +++++++++++++++++---- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 96a9e68c..5d88ec6b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,7 +20,7 @@ script: - ./bootstrap - mkdir build - cd build - - if [ $DEBUG = "debug" ]; then ../configure --enable-debug --disable-shared; else ../configure; fi + - if [ $DEBUG = "debug" ]; then ../configure --enable-debug --disable-shared --disable-fastopen; else ../configure --disable-fastopen; fi - make - make check after_success: diff --git a/configure.ac b/configure.ac index 7875d072..7e1b4fb4 100644 --- a/configure.ac +++ b/configure.ac @@ -113,10 +113,22 @@ AC_CHECK_HEADER([microhttpd.h], CXXFLAGS="-DHTTPSERVER_COMPILATION -D_REENTRANT $LIBMICROHTTPD_CFLAGS $CXXFLAGS" LDFLAGS="$LIBMICROHTTPD_LIBS $REGEX_LIBS $LD_FLAGS" -if test x"$is_windows" = x"no"; then - if test `uname -r |cut -d. -f1` -ge 3; then - if test `uname -r |cut -d. -f2` -ge 7; then - CXXFLAGS="-DUSE_FASTOPEN $CXXFLAGS" +AC_MSG_CHECKING([whether to build with TCP_FASTOPEN support]) +AC_ARG_ENABLE([fastopen], + [AS_HELP_STRING([--enable-fastopen], + [enable use of TCP_FASTOPEN (def=yes)])], + [fastopen="$enableval"], + [fastopen=yes]) +AC_MSG_RESULT([$fastopen]) + +is_fastopen_supported=no; +if test x"$fastopen" = x"yes"; then + if test x"$is_windows" = x"no"; then + if test `uname -r |cut -d. -f1` -ge 3; then + if test `uname -r |cut -d. -f2` -ge 7; then + CXXFLAGS="-DUSE_FASTOPEN $CXXFLAGS"; + is_fastopen_supported=yes; + fi fi fi fi @@ -217,4 +229,5 @@ AC_MSG_NOTICE([Configuration Summary: License : LGPL only Debug : ${debugit} TLS Enabled : ${have_gnutls} + TCP_FASTOPEN : ${is_fastopen_supported} ]) From a8797c12e4be1d8a9b905059daa66f4680f62335 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Mon, 28 Dec 2015 00:07:32 +0100 Subject: [PATCH 176/623] Avoid errors when stopping the server if using internal select --- src/webserver.cpp | 10 ++++++ test/Makefile.am | 3 +- test/integ/threaded.cpp | 74 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 86 insertions(+), 1 deletion(-) create mode 100644 test/integ/threaded.cpp diff --git a/src/webserver.cpp b/src/webserver.cpp index f1b7a869..c36170f9 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -649,9 +649,19 @@ bool webserver::stop() } threads.clear(); typedef vector::const_iterator daemon_item_it; + + if(start_method == http_utils::INTERNAL_SELECT) + { + for(daemon_item_it it = daemons.begin(); it != daemons.end(); ++it) + MHD_quiesce_daemon((*it)->daemon); + } + for(daemon_item_it it = daemons.begin(); it != daemons.end(); ++it) delete *it; daemons.clear(); + + shutdown(bind_socket, 2); + return true; } else diff --git a/test/Makefile.am b/test/Makefile.am index 9e706ef6..cb92d0d4 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -19,11 +19,12 @@ LDADD = $(top_builddir)/src/libhttpserver.la AM_CPPFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/src/httpserver/ METASOURCES = AUTO -check_PROGRAMS = basic http_utils +check_PROGRAMS = basic http_utils threaded MOSTLYCLEANFILES = *.gcda *.gcno *.gcov basic_SOURCES = integ/basic.cpp +threaded_SOURCES = integ/threaded.cpp http_utils_SOURCES = unit/http_utils_test.cpp noinst_HEADERS = littletest.hpp diff --git a/test/integ/threaded.cpp b/test/integ/threaded.cpp new file mode 100644 index 00000000..c9c8de63 --- /dev/null +++ b/test/integ/threaded.cpp @@ -0,0 +1,74 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#include "littletest.hpp" +#include +#include +#include +#include "httpserver.hpp" + +using namespace httpserver; +using namespace std; + +class ok_resource : public http_resource +{ + public: + void render_GET(const http_request& req, http_response** res) + { + *res = new http_response(http_response_builder("OK", 200, "text/plain").string_response()); + } +}; + +LT_BEGIN_SUITE(threaded_suite) + + webserver* ws; + + void set_up() + { + ws = new webserver(create_webserver(8080).max_threads(5)); + ws->start(false); + } + + void tear_down() + { + ws->stop(); + delete ws; + } +LT_END_SUITE(threaded_suite) + +LT_BEGIN_AUTO_TEST(threaded_suite, base) + ok_resource* resource = new ok_resource(); + ws->register_resource("base", resource); + curl_global_init(CURL_GLOBAL_ALL); + std::string s; + CURL* curl; + CURLcode res; + + curl = curl_easy_init(); + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + curl_easy_cleanup(curl); +LT_END_AUTO_TEST(base) + +LT_BEGIN_AUTO_TEST_ENV() + AUTORUN_TESTS() +LT_END_AUTO_TEST_ENV() From 785397c5c36d86e2168e994fca2068f34413ba62 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Mon, 28 Dec 2015 01:16:25 +0100 Subject: [PATCH 177/623] Use MHD_run_from_select to avoid double run of select inside MHD --- src/webserver.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/webserver.cpp b/src/webserver.cpp index c36170f9..ab24fd70 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -311,10 +311,10 @@ void* webserver::select(void* self) timeout_value.tv_sec = timeout_secs; timeout_value.tv_usec = timeout_microsecs; - /*On unix, MHD_socket will be an int anyway. - On windows, the cast is safe because winsock ignores first argument to select*/ + /*On unix, MHD_socket will be an int anyway. + On windows, the cast is safe because winsock ignores first argument to select*/ ::select ((int) max + 1, &rs, &ws, &es, &timeout_value); - MHD_run (di->daemon); + MHD_run_from_select (di->daemon, &rs, &ws, &es); //EVENT SUPPLIERS DISPATCHING { From 38ccaa7189776dfb18588e204425a0e731c3a693 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Mon, 28 Dec 2015 15:56:17 +0100 Subject: [PATCH 178/623] Fixed start methods --- src/httpserver/http_utils.hpp | 6 +++--- src/webserver.cpp | 7 ++++--- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/httpserver/http_utils.hpp b/src/httpserver/http_utils.hpp index 94aa1af8..b3ea90ea 100644 --- a/src/httpserver/http_utils.hpp +++ b/src/httpserver/http_utils.hpp @@ -76,12 +76,12 @@ class http_utils enum start_method_T { - INTERNAL_SELECT = MHD_NO_FLAG, #if defined(__MINGW32__) || defined(__CYGWIN32__) - THREAD = MHD_USE_THREAD_PER_CONNECTION | MHD_USE_POLL + INTERNAL_SELECT = MHD_NO_FLAG, #else - THREAD = MHD_USE_THREAD_PER_CONNECTION | MHD_USE_EPOLL_LINUX_ONLY + INTERNAL_SELECT = MHD_NO_FLAG | MHD_USE_EPOLL_LINUX_ONLY | MHD_USE_EPOLL_TURBO, #endif + THREAD_PER_CONNECTION = MHD_USE_THREAD_PER_CONNECTION | MHD_USE_POLL }; enum policy_T diff --git a/src/webserver.cpp b/src/webserver.cpp index ab24fd70..3ccee114 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -383,11 +383,12 @@ bool webserver::start(bool blocking) iov.push_back(gen(MHD_OPTION_SOCK_ADDR, (intptr_t) bind_address)); if(bind_socket != 0) iov.push_back(gen(MHD_OPTION_LISTEN_SOCKET, bind_socket)); - if(! (start_method == http_utils::INTERNAL_SELECT)) + if(start_method == http_utils::THREAD_PER_CONNECTION && max_threads != 0) { - if(max_threads != 0) - iov.push_back(gen(MHD_OPTION_THREAD_POOL_SIZE, max_threads)); + cout << "Cannot specify maximum number of threads when using a thread per connection" << endl; + throw ::httpserver::webserver_exception(); } + if(max_connections != 0) iov.push_back(gen(MHD_OPTION_CONNECTION_LIMIT, max_connections)); if(memory_limit != 0) From c49e777b50d1a1d3f18c243a3aff55c86ce56fe3 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Mon, 28 Dec 2015 16:04:29 +0100 Subject: [PATCH 179/623] Disable epoll --- src/httpserver/http_utils.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/httpserver/http_utils.hpp b/src/httpserver/http_utils.hpp index b3ea90ea..7721173a 100644 --- a/src/httpserver/http_utils.hpp +++ b/src/httpserver/http_utils.hpp @@ -79,7 +79,7 @@ class http_utils #if defined(__MINGW32__) || defined(__CYGWIN32__) INTERNAL_SELECT = MHD_NO_FLAG, #else - INTERNAL_SELECT = MHD_NO_FLAG | MHD_USE_EPOLL_LINUX_ONLY | MHD_USE_EPOLL_TURBO, + INTERNAL_SELECT = MHD_NO_FLAG/* | MHD_USE_EPOLL_LINUX_ONLY | MHD_USE_EPOLL_TURBO*/, #endif THREAD_PER_CONNECTION = MHD_USE_THREAD_PER_CONNECTION | MHD_USE_POLL }; From fb87493b54d9e4b6efbdb0f352b2ec98289f9187 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Tue, 29 Dec 2015 19:09:42 +0100 Subject: [PATCH 180/623] Simplified general structure, removed event_supplier support, comet logic now uses a lock-free implementation --- examples/comet.cpp | 66 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100755 examples/comet.cpp diff --git a/examples/comet.cpp b/examples/comet.cpp new file mode 100755 index 00000000..c5cec7d8 --- /dev/null +++ b/examples/comet.cpp @@ -0,0 +1,66 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#include +#include +#include + +using namespace httpserver; + +std::string topics_array[] = { "topic_1" }; +std::vector topics(topics_array, topics_array + sizeof(topics_array) / sizeof(std::string)); + +class comet_send_resource : public http_resource { + public: + void render(const http_request& req, http_response** res) + { + *res = new http_response(http_response_builder("Hi", 200).long_polling_send_response(topics_array[0])); + } +}; + +class comet_listen_resource : public http_resource { + public: + void render(const http_request& req, http_response** res) + { + *res = new http_response(http_response_builder("OK", 200).long_polling_receive_response(topics)); + } +}; + +int main() +{ + //it is possible to create a webserver passing a great number of parameters. + //In this case we are just passing the port and the number of thread running. + webserver ws = create_webserver(8080).start_method(http::http_utils::INTERNAL_SELECT); + + comet_send_resource csr; + comet_listen_resource clr; + //this way we are registering the hello_world_resource to answer for the endpoint + //"/hello". The requested method is called (if the request is a GET we call the render_GET + //method. In case that the specific render method is not implemented, the generic "render" + //method is called. + ws.register_resource("/send", &csr, true); + ws.register_resource("/listen", &clr, true); + + //This way we are putting the created webserver in listen. We pass true in order to have + //a blocking call; if we want the call to be non-blocking we can just pass false to the + //method. + ws.start(true); + return 0; +} From 81c46c786329bedeb4f2637f6ac99f2f0f4cdb2a Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Tue, 29 Dec 2015 19:24:22 +0100 Subject: [PATCH 181/623] Simplified general structure, removed event_supplier support, comet logic now uses a lock-free implementation --- ChangeLog | 9 +- configure.ac | 4 +- debian/changelog.in | 11 +- examples/comet.cpp | 2 +- examples/hello_world.cpp | 4 +- src/Makefile.am | 2 +- src/details/comet_manager.cpp | 266 +++--------------- src/http_resource.cpp | 1 - src/http_response.cpp | 23 +- src/httpserver.hpp | 2 - src/httpserver/create_webserver.hpp | 11 + src/httpserver/details/comet_manager.hpp | 48 +--- src/httpserver/details/event_tuple.hpp | 71 ----- src/httpserver/event_supplier.hpp | 67 ----- src/httpserver/http_response.hpp | 2 +- src/httpserver/http_utils.hpp | 57 +--- src/httpserver/webserver.hpp | 34 +-- src/webserver.cpp | 327 ++--------------------- test/integ/threaded.cpp | 2 +- 19 files changed, 113 insertions(+), 830 deletions(-) delete mode 100644 src/httpserver/details/event_tuple.hpp delete mode 100644 src/httpserver/event_supplier.hpp diff --git a/ChangeLog b/ChangeLog index 28c63573..042a2b81 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,8 +1,13 @@ -Sun Jan 27 19:39:01 2016 +0100 +Tue Dec 29 18:56:31 2015 +0100 + Removed support for event supplier (badly defined, complicated and almost useless) + Eliminated custom selection logic (simplified overall code in webserver.cpp) + Changed comet to use a lock-free implementation + +Sun Dec 27 19:39:01 2015 +0100 Removed POLL start configuration (THREAD now defaults to POLL or EPOLL on Linux) Use TCP_FASTOPEN on linux >= 3.6 -Sat Jan 26 15:08:22 2016 +0100 +Sat Dec 26 15:08:22 2015 +0100 Changed http_resource to use classic C++ polymorphism using virtual instead of CRTP Fri Jul 17 21:38:54 2015 +0000 diff --git a/configure.ac b/configure.ac index 7e1b4fb4..821ff3e0 100644 --- a/configure.ac +++ b/configure.ac @@ -21,8 +21,8 @@ AC_PREREQ(2.57) m4_define([libhttpserver_MAJOR_VERSION],[0])dnl -m4_define([libhttpserver_MINOR_VERSION],[10])dnl -m4_define([libhttpserver_REVISION],[1])dnl +m4_define([libhttpserver_MINOR_VERSION],[11])dnl +m4_define([libhttpserver_REVISION],[0])dnl m4_define([libhttpserver_PKG_VERSION],[libhttpserver_MAJOR_VERSION.libhttpserver_MINOR_VERSION.libhttpserver_REVISION])dnl m4_define([libhttpserver_LDF_VERSION],[libhttpserver_MAJOR_VERSION:libhttpserver_MINOR_VERSION:libhttpserver_REVISION])dnl AC_INIT([libhttpserver], libhttpserver_PKG_VERSION, [electrictwister2000@gmail.com]) diff --git a/debian/changelog.in b/debian/changelog.in index d4bf27ea..642e52c4 100644 --- a/debian/changelog.in +++ b/debian/changelog.in @@ -1,13 +1,20 @@ +libhttpserver (0.11.0) unstable; urgency=low + * Removed support for event supplier (badly defined, complicated and almost useless) + * Eliminated custom selection logic (simplified overall code in webserver.cpp) + * Changed comet to use a lock-free implementation + + -- Sebastiano Merlino Sat, 29 Dec 2015 18:56:31 +0100 + libhttpserver (0.10.1) unstable; urgency=low * Removed POLL start configuration (THREAD now defaults to POLL or EPOLL on Linux) * Use TCP_FASTOPEN on linux >= 3.6 - -- Sebastiano Merlino Sat, 27 Jan 2016 19:39:01 +0100 + -- Sebastiano Merlino Sat, 27 Dec 2015 19:39:01 +0100 libhttpserver (0.10.0) unstable; urgency=low * Changed http_resource to use classic C++ polymorphism using virtual instead of CRTP - -- Sebastiano Merlino Sat, 26 Jan 2016 15:08:22 +0100 + -- Sebastiano Merlino Sat, 26 Dec 2015 15:08:22 +0100 libhttpserver (0.9.1) unstable; urgency=low * Eliminated build dependency on pkg-config diff --git a/examples/comet.cpp b/examples/comet.cpp index c5cec7d8..ae45d839 100755 --- a/examples/comet.cpp +++ b/examples/comet.cpp @@ -47,7 +47,7 @@ int main() { //it is possible to create a webserver passing a great number of parameters. //In this case we are just passing the port and the number of thread running. - webserver ws = create_webserver(8080).start_method(http::http_utils::INTERNAL_SELECT); + webserver ws = create_webserver(8080).comet(); comet_send_resource csr; comet_listen_resource clr; diff --git a/examples/hello_world.cpp b/examples/hello_world.cpp index 93a8f790..646bdb4d 100755 --- a/examples/hello_world.cpp +++ b/examples/hello_world.cpp @@ -49,7 +49,7 @@ int main() { //it is possible to create a webserver passing a great number of parameters. //In this case we are just passing the port and the number of thread running. - webserver ws = create_webserver(8080).max_threads(5); + webserver ws = create_webserver(8080).start_method(http::http_utils::INTERNAL_SELECT).max_threads(5); hello_world_resource hwr; //this way we are registering the hello_world_resource to answer for the endpoint @@ -62,5 +62,5 @@ int main() //a blocking call; if we want the call to be non-blocking we can just pass false to the //method. ws.start(true); - return 0; + return 0; } diff --git a/src/Makefile.am b/src/Makefile.am index 4e4b989f..2a57d322 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -21,7 +21,7 @@ METASOURCES = AUTO lib_LTLIBRARIES = libhttpserver.la libhttpserver_la_SOURCES = string_utilities.cpp webserver.cpp http_utils.cpp http_request.cpp http_response.cpp http_resource.cpp details/comet_manager.cpp details/http_endpoint.cpp noinst_HEADERS = httpserver/string_utilities.hpp httpserver/details/modded_request.hpp httpserver/details/http_response_ptr.hpp httpserver/details/cache_entry.hpp httpserver/details/comet_manager.hpp gettext.h -nobase_include_HEADERS = httpserver.hpp httpserver/create_webserver.hpp httpserver/webserver.hpp httpserver/http_utils.hpp httpserver/details/http_endpoint.hpp httpserver/http_request.hpp httpserver/http_response.hpp httpserver/http_resource.hpp httpserver/binders.hpp httpserver/event_supplier.hpp httpserver/details/event_tuple.hpp httpserver/http_response_builder.hpp +nobase_include_HEADERS = httpserver.hpp httpserver/create_webserver.hpp httpserver/webserver.hpp httpserver/http_utils.hpp httpserver/details/http_endpoint.hpp httpserver/http_request.hpp httpserver/http_response.hpp httpserver/http_resource.hpp httpserver/binders.hpp httpserver/http_response_builder.hpp AM_CXXFLAGS += -fPIC -Wall diff --git a/src/details/comet_manager.cpp b/src/details/comet_manager.cpp index a6eb8708..3443b618 100644 --- a/src/details/comet_manager.cpp +++ b/src/details/comet_manager.cpp @@ -33,264 +33,72 @@ namespace details comet_manager::comet_manager() { - pthread_rwlock_init(&comet_guard, NULL); - pthread_mutex_init(&cleanmux, NULL); - pthread_cond_init(&cleancond, NULL); } comet_manager::~comet_manager() { - pthread_rwlock_destroy(&comet_guard); - pthread_mutex_destroy(&cleanmux); - pthread_cond_destroy(&cleancond); } -void comet_manager::send_message_to_topic ( - const string& topic, - const string& message, - const httpserver::http::http_utils::start_method_T& start_method -) +void comet_manager::send_message_to_topic (const string& topic, const string& message) { - pthread_rwlock_wrlock(&comet_guard); - for(set::const_iterator it = q_waitings[topic].begin(); - it != q_waitings[topic].end(); - ++it - ) - { - q_messages[(*it)].push_back(message); - q_signal.insert((*it)); - if(start_method != http::http_utils::INTERNAL_SELECT) - { - pthread_mutex_lock(&q_blocks[(*it)].first); - pthread_cond_signal(&q_blocks[(*it)].second); - pthread_mutex_unlock(&q_blocks[(*it)].first); - } - map::const_iterator itt; - if((itt = q_keepalives.find(*it)) != q_keepalives.end()) - { - struct timeval curtime; - gettimeofday(&curtime, NULL); - q_keepalives[*it] = curtime.tv_sec; - } - } - pthread_rwlock_unlock(&comet_guard); - if(start_method != http::http_utils::INTERNAL_SELECT) - { - pthread_mutex_lock(&cleanmux); - pthread_cond_signal(&cleancond); - pthread_mutex_unlock(&cleanmux); - } -} + map >::const_iterator it = this->q_topics.find(topic); + if (it == this->q_topics.end()) return; -void comet_manager::register_to_topics ( - const vector& topics, - const http::httpserver_ska& connection_id, - int keepalive_secs, - string keepalive_msg, - const httpserver::http::http_utils::start_method_T& start_method -) -{ - pthread_rwlock_wrlock(&comet_guard); - for(vector::const_iterator it = topics.begin(); - it != topics.end(); ++it - ) - q_waitings[*it].insert(connection_id); - if(keepalive_secs != -1) - { - struct timeval curtime; - gettimeofday(&curtime, NULL); - q_keepalives[connection_id] = curtime.tv_sec; - q_keepalives_mem[connection_id] = make_pair( - keepalive_secs, keepalive_msg - ); - } - if(start_method != http::http_utils::INTERNAL_SELECT) + //copying value guarantees we iterate on a copy. Even if the original set is modified we are safe and so we stay lock free. + const set connections = it->second; + + for (set::const_iterator c_it = connections.begin(); c_it != connections.end(); ++c_it) { - pthread_mutex_t m; - pthread_cond_t c; - pthread_mutex_init(&m, NULL); - pthread_cond_init(&c, NULL); - q_blocks[connection_id] = - make_pair(m, c); - } - pthread_rwlock_unlock(&comet_guard); -} + map >::iterator message_queue_it = this->q_messages.find(*c_it); + if (message_queue_it == this->q_messages.end()) continue; -size_t comet_manager::read_message(const http::httpserver_ska& connection_id, - string& message -) -{ - pthread_rwlock_wrlock(&comet_guard); - deque& t_deq = q_messages[connection_id]; - message.assign(t_deq.front()); - t_deq.pop_front(); - pthread_rwlock_unlock(&comet_guard); - return message.size(); + message_queue_it->second.push_back(message); + + MHD_resume_connection(*c_it); + } } -size_t comet_manager::get_topic_consumers( - const string& topic, - set& consumers -) +void comet_manager::register_to_topics (const vector& topics, MHD_Connection* connection_id) { - pthread_rwlock_rdlock(&comet_guard); - - for(set::const_iterator it = q_waitings[topic].begin(); - it != q_waitings[topic].end(); ++it - ) + for(vector::const_iterator it = topics.begin(); it != topics.end(); ++it) { - consumers.insert((*it)); + this->q_topics[*it].insert(connection_id); // (1) Can this cause problems in concurrency with (2) ? } - std::set::size_type size = consumers.size(); - pthread_rwlock_unlock(&comet_guard); - return size; + this->q_subscriptions.insert(make_pair(connection_id, set(topics.begin(), topics.end()))); + this->q_messages.insert(make_pair(connection_id, deque())); } -bool comet_manager::pop_signaled(const http::httpserver_ska& consumer, - const httpserver::http::http_utils::start_method_T& start_method -) +size_t comet_manager::read_message(MHD_Connection* connection_id, string& message) { - if(start_method == http::http_utils::INTERNAL_SELECT) + if(this->q_messages[connection_id].empty()) { - pthread_rwlock_wrlock(&comet_guard); - set::iterator it = q_signal.find(consumer); - if(it != q_signal.end()) - { - if(q_messages[consumer].empty()) - { - q_signal.erase(it); - pthread_rwlock_unlock(&comet_guard); - return false; - } - pthread_rwlock_unlock(&comet_guard); - return true; - } - else - { - pthread_rwlock_unlock(&comet_guard); - return false; - } + MHD_suspend_connection(connection_id); + return 0; } - else - { - pthread_rwlock_rdlock(&comet_guard); - pthread_mutex_lock(&q_blocks[consumer].first); - struct timespec t; - struct timeval curtime; - - { - bool to_unlock = true; - while(q_signal.find(consumer) == q_signal.end()) - { - if(to_unlock) - { - pthread_rwlock_unlock(&comet_guard); - to_unlock = false; - } - gettimeofday(&curtime, NULL); - t.tv_sec = curtime.tv_sec + q_keepalives_mem[consumer].first; - t.tv_nsec = 0; - int rslt = pthread_cond_timedwait(&q_blocks[consumer].second, - &q_blocks[consumer].first, &t - ); - if(rslt == ETIMEDOUT) - { - pthread_rwlock_wrlock(&comet_guard); - send_message_to_consumer(consumer, - q_keepalives_mem[consumer].second, false, start_method - ); - pthread_rwlock_unlock(&comet_guard); - } - } - if(to_unlock) - pthread_rwlock_unlock(&comet_guard); - } - if(q_messages[consumer].size() == 0) - { - pthread_rwlock_wrlock(&comet_guard); - q_signal.erase(consumer); - pthread_mutex_unlock(&q_blocks[consumer].first); - pthread_rwlock_unlock(&comet_guard); - return false; - } - pthread_rwlock_rdlock(&comet_guard); - pthread_mutex_unlock(&q_blocks[consumer].first); - pthread_rwlock_unlock(&comet_guard); - return true; - } - return false; + deque& t_deq = this->q_messages[connection_id]; + message.assign(t_deq.front()); + t_deq.pop_front(); + return message.size(); } -void comet_manager::complete_request(const http::httpserver_ska& connection_id) +void comet_manager::complete_request(MHD_Connection* connection_id) { - pthread_rwlock_wrlock(&comet_guard); - q_messages.erase(connection_id); - q_blocks.erase(connection_id); - q_signal.erase(connection_id); - q_keepalives.erase(connection_id); + this->q_messages.erase(connection_id); - typedef map >::iterator conn_it; - for(conn_it it = q_waitings.begin(); it != q_waitings.end(); ++it) - { - it->second.erase(connection_id); - } - pthread_rwlock_unlock(&comet_guard); -} + map >::iterator topics_it = this->q_subscriptions.find(connection_id); + if (topics_it == q_subscriptions.end()) return; + set topics = topics_it->second; -void comet_manager::comet_select(unsigned long long* timeout_secs, - unsigned long long* timeout_microsecs, - const httpserver::http::http_utils::start_method_T& start_method -) -{ - pthread_rwlock_wrlock(&comet_guard); - for(map::iterator it = q_keepalives.begin(); it != q_keepalives.end(); ++it) + for(set::const_iterator it = topics.begin(); it != topics.end(); ++it) { - struct timeval curtime; - gettimeofday(&curtime, NULL); - int waited_time = curtime.tv_sec - (*it).second; - if(waited_time >= q_keepalives_mem[(*it).first].first) - { - send_message_to_consumer((*it).first, q_keepalives_mem[(*it).first].second, true, start_method); - } - else - { - unsigned long long to_wait_time = (q_keepalives_mem[(*it).first].first - waited_time); - if(to_wait_time < *timeout_secs) - { - *timeout_secs = to_wait_time; - *timeout_microsecs = 0; - } - } - } - pthread_rwlock_unlock(&comet_guard); -} + map >::iterator connections_it = this->q_topics.find(*it); + if (connections_it == this->q_topics.end()) continue; -void comet_manager::send_message_to_consumer( - const http::httpserver_ska& connection_id, - const std::string& message, - bool to_lock, - const httpserver::http::http_utils::start_method_T& start_method -) -{ - //This function need to be externally locked on write - q_messages[connection_id].push_back(message); - map::const_iterator it; - if((it = q_keepalives.find(connection_id)) != q_keepalives.end()) - { - struct timeval curtime; - gettimeofday(&curtime, NULL); - q_keepalives[connection_id] = curtime.tv_sec; - } - q_signal.insert(connection_id); - if(start_method != http::http_utils::INTERNAL_SELECT) - { - if(to_lock) - pthread_mutex_lock(&q_blocks[connection_id].first); - pthread_cond_signal(&q_blocks[connection_id].second); - if(to_lock) - pthread_mutex_unlock(&q_blocks[connection_id].first); + connections_it->second.erase(connection_id); + if (connections_it->second.size() == 0) this->q_topics.erase(*it); // (2) } + q_subscriptions.erase(connection_id); } } //details diff --git a/src/http_resource.cpp b/src/http_resource.cpp index 73840745..0142ec35 100644 --- a/src/http_resource.cpp +++ b/src/http_resource.cpp @@ -24,7 +24,6 @@ #include "http_utils.hpp" #include "http_request.hpp" #include "http_response.hpp" -#include "details/event_tuple.hpp" #include "webserver.hpp" #include "string_utilities.hpp" #include "http_response_builder.hpp" diff --git a/src/http_response.cpp b/src/http_response.cpp index 35558285..47cc1a36 100644 --- a/src/http_response.cpp +++ b/src/http_response.cpp @@ -25,7 +25,6 @@ #include #include #include "http_utils.hpp" -#include "details/event_tuple.hpp" #include "webserver.hpp" #include "http_response.hpp" #include "http_response_builder.hpp" @@ -245,19 +244,14 @@ void http_response::get_raw_response_lp_receive( ) { this->ws = ws; - this->connection_id = MHD_get_connection_info( - this->underlying_connection, - MHD_CONNECTION_INFO_CLIENT_ADDRESS - )->client_addr; + this->connection_id = this->underlying_connection; *response = MHD_create_response_from_callback(MHD_SIZE_UNKNOWN, 80, &http_response::data_generator, (void*) this, NULL); ws->register_to_topics( topics, - connection_id, - keepalive_secs, - keepalive_msg + connection_id ); } @@ -270,15 +264,10 @@ ssize_t http_response::data_generator( { http_response* _this = static_cast(cls); - if(_this->ws->pop_signaled(_this->connection_id)) - { - string message; - size_t size = _this->ws->read_message(_this->connection_id, message); - memcpy(buf, message.c_str(), size); - return size; - } - else - return 0; + string message; + size_t size = _this->ws->read_message(_this->connection_id, message); + memcpy(buf, message.c_str(), size); + return size; } void http_response::get_raw_response_lp_send( diff --git a/src/httpserver.hpp b/src/httpserver.hpp index 20f688b5..52b71443 100644 --- a/src/httpserver.hpp +++ b/src/httpserver.hpp @@ -29,8 +29,6 @@ #include "httpserver/http_response.hpp" #include "httpserver/http_response_builder.hpp" #include "httpserver/http_request.hpp" -#include "httpserver/event_supplier.hpp" -#include "httpserver/details/event_tuple.hpp" #include "httpserver/webserver.hpp" #endif diff --git a/src/httpserver/create_webserver.hpp b/src/httpserver/create_webserver.hpp index 02d2ae8a..78daa867 100644 --- a/src/httpserver/create_webserver.hpp +++ b/src/httpserver/create_webserver.hpp @@ -78,6 +78,7 @@ class create_webserver _regex_checking(true), _ban_system_enabled(true), _post_process_enabled(true), + _comet_enabled(false), _single_resource(0x0), _not_found_resource(0x0), _method_not_allowed_resource(0x0), @@ -118,6 +119,7 @@ class create_webserver _regex_checking(true), _ban_system_enabled(true), _post_process_enabled(true), + _comet_enabled(false), _single_resource(0x0), _not_found_resource(0x0), _method_not_allowed_resource(0x0), @@ -264,6 +266,14 @@ class create_webserver { _digest_auth_enabled = false; return *this; } + create_webserver& comet() + { + _comet_enabled = true; return *this; + } + create_webserver& no_comet() + { + _comet_enabled = false; return *this; + } create_webserver& regex_checking() { _regex_checking = true; return *this; @@ -349,6 +359,7 @@ class create_webserver bool _regex_checking; bool _ban_system_enabled; bool _post_process_enabled; + bool _comet_enabled; render_ptr _single_resource; render_ptr _not_found_resource; render_ptr _method_not_allowed_resource; diff --git a/src/httpserver/details/comet_manager.hpp b/src/httpserver/details/comet_manager.hpp index 936b5484..a1762276 100644 --- a/src/httpserver/details/comet_manager.hpp +++ b/src/httpserver/details/comet_manager.hpp @@ -38,11 +38,6 @@ namespace httpserver class webserver; -namespace http -{ -struct httpserver_ska; -}; - namespace details { @@ -53,50 +48,21 @@ class comet_manager ~comet_manager(); - void send_message_to_topic(const std::string& topic, - const std::string& message, const httpserver::http::http_utils::start_method_T& start_method - ); - - void send_message_to_consumer(const http::httpserver_ska& connection_id, - const std::string& message, bool to_lock, - const httpserver::http::http_utils::start_method_T& start_method - ); - - void register_to_topics(const std::vector& topics, - const http::httpserver_ska& connection_id, int keepalive_secs, - std::string keepalive_msg, const httpserver::http::http_utils::start_method_T& start_method - ); - - size_t read_message(const http::httpserver_ska& connection_id, - std::string& message - ); - - size_t get_topic_consumers(const std::string& topic, - std::set& consumers - ); + void send_message_to_topic(const std::string& topic, const std::string& message); - bool pop_signaled(const http::httpserver_ska& consumer, const httpserver::http::http_utils::start_method_T& start_method); + void register_to_topics(const std::vector& topics, MHD_Connection* connection_id); - void complete_request(const http::httpserver_ska& connection_id); + size_t read_message(MHD_Connection* connection_id, std::string& message); - void comet_select(unsigned long long* timeout_secs, - unsigned long long* timeout_microsecs, - const httpserver::http::http_utils::start_method_T& start_method - ); + void complete_request(MHD_Connection* connection_id); comet_manager(const comet_manager&) { } - std::map > q_messages; - std::map > q_waitings; - std::map > q_blocks; - std::set q_signal; - std::map q_keepalives; - std::map > q_keepalives_mem; - pthread_rwlock_t comet_guard; - pthread_mutex_t cleanmux; - pthread_cond_t cleancond; + std::map > q_messages; + std::map > q_topics; + std::map > q_subscriptions; friend class httpserver::webserver; }; diff --git a/src/httpserver/details/event_tuple.hpp b/src/httpserver/details/event_tuple.hpp deleted file mode 100644 index 71786b4b..00000000 --- a/src/httpserver/details/event_tuple.hpp +++ /dev/null @@ -1,71 +0,0 @@ -/* - This file is part of libhttpserver - Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 - USA -*/ - -#if !defined (_HTTPSERVER_HPP_INSIDE_) && !defined (HTTPSERVER_COMPILATION) -#error "Only or can be included directly." -#endif - -#ifndef _EVENT_TUPLE_HPP_ -#define _EVENT_TUPLE_HPP_ - -#include "httpserver/event_supplier.hpp" -#include "httpserver/binders.hpp" - -namespace httpserver { - -class webserver; - -namespace details -{ - class http_endpoint; - struct modded_request; - struct daemon_item; - - class event_tuple - { - private: - typedef void(*supply_events_ptr)( - fd_set*, - fd_set*, - fd_set*, - MHD_socket* - ); - - binders::functor_four supply_events; - binders::functor_zero get_timeout; - binders::functor_zero dispatch_events; - - event_tuple(); - - friend class ::httpserver::webserver; - public: - template - event_tuple(event_supplier* es): - supply_events(binders::functor_four(es, &T::supply_events)), - get_timeout(binders::functor_zero(es, &T::get_timeout)), - dispatch_events(binders::functor_zero(es, &T::dispatch_events)) - { - } - }; -} //details - -} //httpserver - -#endif //_EVENT_TUPLE_HPP_ diff --git a/src/httpserver/event_supplier.hpp b/src/httpserver/event_supplier.hpp deleted file mode 100644 index 64a0bba6..00000000 --- a/src/httpserver/event_supplier.hpp +++ /dev/null @@ -1,67 +0,0 @@ -/* - This file is part of libhttpserver - Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 - USA -*/ - -#if !defined (_HTTPSERVER_HPP_INSIDE_) && !defined (HTTPSERVER_COMPILATION) -#error "Only or can be included directly." -#endif - -#ifndef _EVENT_SUPPLIER_HPP_ -#define _EVENT_SUPPLIER_HPP_ - -namespace httpserver { - -template -class event_supplier -{ - public: - event_supplier() - { - } - - ~event_supplier() - { - } - - void supply_events( - fd_set* read_fdset, - fd_set* write_fdset, - fd_set* exc_fdset, - MHD_socket* max - ) const - { - static_cast(this)->supply_events( - read_fdset, write_fdset, exc_fdset, max - ); - } - - struct timeval get_timeout() const - { - return static_cast(this)->get_timeout(); - } - - void dispatch_events() const - { - static_cast(this)->dispatch_events(); - } -}; - -} //event_supplier - -#endif //_EVENT_SUPPLIER_HPP_ diff --git a/src/httpserver/http_response.hpp b/src/httpserver/http_response.hpp index 21ef6344..dbab9618 100644 --- a/src/httpserver/http_response.hpp +++ b/src/httpserver/http_response.hpp @@ -268,7 +268,7 @@ class http_response bool completed; webserver* ws; - struct http::httpserver_ska connection_id; + MHD_Connection* connection_id; void get_raw_response_str(MHD_Response** res, webserver* ws = 0x0); void get_raw_response_file(MHD_Response** res, webserver* ws = 0x0); diff --git a/src/httpserver/http_utils.hpp b/src/httpserver/http_utils.hpp index 7721173a..3559f0ea 100644 --- a/src/httpserver/http_utils.hpp +++ b/src/httpserver/http_utils.hpp @@ -77,9 +77,9 @@ class http_utils enum start_method_T { #if defined(__MINGW32__) || defined(__CYGWIN32__) - INTERNAL_SELECT = MHD_NO_FLAG, + INTERNAL_SELECT = MHD_USE_SELECT_INTERNALLY | MHD_USE_POLL, #else - INTERNAL_SELECT = MHD_NO_FLAG/* | MHD_USE_EPOLL_LINUX_ONLY | MHD_USE_EPOLL_TURBO*/, + INTERNAL_SELECT = MHD_USE_SELECT_INTERNALLY | MHD_USE_EPOLL_LINUX_ONLY | MHD_USE_EPOLL_TURBO, #endif THREAD_PER_CONNECTION = MHD_USE_THREAD_PER_CONNECTION | MHD_USE_POLL }; @@ -366,59 +366,6 @@ char* load_file (const char *filename); size_t load_file (const char* filename, char** content); -struct httpserver_ska -{ - httpserver_ska(struct sockaddr* addr): - addr(addr), - ip(get_ip_str_new(addr)), - port(get_port(addr)) - { - } - - httpserver_ska(): addr(0x0) { } - - httpserver_ska(const httpserver_ska& o): - addr(o.addr), - ip(o.ip), - port(o.port) - { - } - - bool operator<(const httpserver_ska& o) const - { - if(this->ip < o.ip) - return true; - else if(this->ip > o.ip) - return false; - else if(this->port < o.port) - return true; - else - return false; - } - - httpserver_ska& operator=(const httpserver_ska& o) - { - this->addr = o.addr; - this->ip = o.ip; - this->port = o.port; - return *this; - } - - httpserver_ska& operator=(struct sockaddr* addr) - { - this->addr = addr; - this->ip = get_ip_str_new(addr); - this->port = get_port(addr); - return *this; - } - - struct sockaddr* addr; - std::string ip; - int port; -}; - - - }; }; #endif diff --git a/src/httpserver/webserver.hpp b/src/httpserver/webserver.hpp index 8d4310ea..6e675697 100644 --- a/src/httpserver/webserver.hpp +++ b/src/httpserver/webserver.hpp @@ -49,7 +49,6 @@ namespace httpserver { class http_resource; class http_response; -template class event_supplier; class create_webserver; namespace http { @@ -58,7 +57,6 @@ struct httpserver_ska; }; namespace details { - class event_tuple; class http_endpoint; struct daemon_item; struct modded_request; @@ -124,20 +122,12 @@ class webserver void send_message_to_topic(const std::string& topic, const std::string& message ); - void send_message_to_consumer(const http::httpserver_ska& connection_id, - const std::string& message, bool to_lock = true - ); void register_to_topics(const std::vector& topics, - const http::httpserver_ska& connection_id, int keepalive_secs = -1, - std::string keepalive_msg = "" + MHD_Connection* connection_id ); - size_t read_message(const http::httpserver_ska& connection_id, + size_t read_message(MHD_Connection* connection_id, std::string& message ); - size_t get_topic_consumers(const std::string& topic, - std::set& consumers - ); - bool pop_signaled(const http::httpserver_ska& consumer); http_response* get_from_cache(const std::string& key, bool* valid, bool lock = false, bool write = false @@ -175,16 +165,6 @@ class webserver return this->unescaper; } - template - void register_event_supplier(const std::string& id, - event_supplier* ev_supplier - ) - { - register_event_supplier(id, details::event_tuple(ev_supplier)); - } - - void remove_event_supplier(const std::string& id); - /** * Method used to kill the webserver waiting for it to terminate **/ @@ -228,6 +208,7 @@ class webserver const bool regex_checking; const bool ban_system_enabled; const bool post_process_enabled; + const bool comet_enabled; bool single_resource; pthread_mutex_t mutexwait; pthread_rwlock_t runguard; @@ -248,8 +229,6 @@ class webserver std::vector daemons; std::vector threads; - std::map event_suppliers; - details::comet_manager* internal_comet_manager; static void* select(void* self); @@ -340,13 +319,6 @@ class webserver const char* version, const char* method ); - void register_event_supplier(const std::string& id, const details::event_tuple& evt); - - bool use_internal_select() - { - return this->start_method == http::http_utils::INTERNAL_SELECT; - } - friend int policy_callback (void *cls, const struct sockaddr* addr, socklen_t addrlen ); diff --git a/src/webserver.cpp b/src/webserver.cpp index 3ccee114..7005b234 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -50,7 +50,6 @@ #include "http_response_builder.hpp" #include "details/http_endpoint.hpp" #include "string_utilities.hpp" -#include "details/event_tuple.hpp" #include "create_webserver.hpp" #include "details/comet_manager.hpp" #include "webserver.hpp" @@ -169,6 +168,7 @@ webserver::webserver(const create_webserver& params): regex_checking(params._regex_checking), ban_system_enabled(params._ban_system_enabled), post_process_enabled(params._post_process_enabled), + comet_enabled(params._comet_enabled), single_resource(params._single_resource), not_found_resource(params._not_found_resource), method_not_allowed_resource(params._method_not_allowed_resource), @@ -242,94 +242,6 @@ bool webserver::register_resource(const std::string& resource, http_resource* hr return result.second; } -void* webserver::select(void* self) -{ - fd_set rs; - fd_set ws; - fd_set es; - struct timeval timeout_value; - details::daemon_item* di = static_cast(self); - MHD_socket max; - while (di->ws->is_running()) - { - max = 0; - FD_ZERO (&rs); - FD_ZERO (&ws); - FD_ZERO (&es); - if (MHD_YES != MHD_get_fdset (di->daemon, &rs, &ws, &es, &max)) - throw ::httpserver::webserver_exception(); /* fatal internal error */ - - unsigned long long timeout_microsecs = 0; - unsigned long long timeout_secs = 0; - - if (!(MHD_get_timeout (di->daemon, &timeout_microsecs) == MHD_YES)) - { - timeout_secs = 1; - timeout_microsecs = 0; - } - else - { - if(timeout_microsecs < 1000) - { - timeout_microsecs = timeout_microsecs * 1000; - timeout_secs = 0; - } - } - - // SUPPLIERS MANAGEMENT - { - std::map::const_iterator it; - pthread_rwlock_rdlock(&di->ws->runguard); - for(it = di->ws->event_suppliers.begin(); - it != di->ws->event_suppliers.end(); - ++it - ) - { - MHD_socket local_max; - (*it).second.supply_events(&rs, &ws, &es, &local_max); - - if(local_max > max) - max = local_max; - - struct timeval t = (*it).second.get_timeout(); - if((unsigned MHD_LONG_LONG) t.tv_sec < timeout_secs || - ((unsigned MHD_LONG_LONG) t.tv_sec == timeout_secs - && (unsigned MHD_LONG_LONG) t.tv_usec < timeout_microsecs - ) - ) - { - timeout_secs = t.tv_sec; - timeout_microsecs = t.tv_usec; - } - } - pthread_rwlock_unlock(&di->ws->runguard); - } - - // COMET CONNECTIONS MANAGEMENT - di->ws->internal_comet_manager->comet_select(&timeout_secs, &timeout_microsecs, di->ws->start_method); - - timeout_value.tv_sec = timeout_secs; - timeout_value.tv_usec = timeout_microsecs; - - /*On unix, MHD_socket will be an int anyway. - On windows, the cast is safe because winsock ignores first argument to select*/ - ::select ((int) max + 1, &rs, &ws, &es, &timeout_value); - MHD_run_from_select (di->daemon, &rs, &ws, &es); - - //EVENT SUPPLIERS DISPATCHING - { - std::map::const_iterator it; - pthread_rwlock_rdlock(&di->ws->runguard); - for(it = di->ws->event_suppliers.begin(); - it != di->ws->event_suppliers.end(); - ++it - ) - (*it).second.dispatch_events(); - } - } - return 0x0; -} - MHD_socket create_socket (int domain, int type, int protocol) { int sock_cloexec = SOCK_CLOEXEC; @@ -389,6 +301,8 @@ bool webserver::start(bool blocking) throw ::httpserver::webserver_exception(); } + if(max_threads != 0) + iov.push_back(gen(MHD_OPTION_THREAD_POOL_SIZE, max_threads)); if(max_connections != 0) iov.push_back(gen(MHD_OPTION_CONNECTION_LIMIT, max_connections)); if(memory_limit != 0) @@ -431,111 +345,6 @@ bool webserver::start(bool blocking) iov.push_back(gen(MHD_OPTION_HTTPS_CRED_TYPE, cred_type)); #endif //HAVE_GNUTLS - if(start_method == http_utils::INTERNAL_SELECT) - { - int on = 1; - bool bind_settled = true; - int result = 0; - if(!bind_socket) - { - bind_settled = false; - struct sockaddr_in servaddr4; -#if HAVE_INET6 - struct sockaddr_in6 servaddr6; -#endif - const struct sockaddr *servaddr = NULL; - socklen_t addrlen; -#if HAVE_INET6 - if (0 != (options & MHD_USE_IPv6)) - addrlen = sizeof (struct sockaddr_in6); - else -#endif - addrlen = sizeof (struct ::sockaddr_in); - -#if HAVE_INET6 - if (0 != (options & MHD_USE_IPv6)) - { - memset (&servaddr6, 0, sizeof (struct sockaddr_in6)); - servaddr6.sin6_family = AF_INET6; - servaddr6.sin6_port = htons (port); -#if HAVE_SOCKADDR_IN_SIN_LEN - servaddr6.sin6_len = sizeof (struct sockaddr_in6); -#endif - servaddr = (struct sockaddr *) &servaddr6; - } - else -#endif - { - memset (&servaddr4, 0, sizeof (struct ::sockaddr_in)); - servaddr4.sin_family = AF_INET; - servaddr4.sin_port = htons (port); -#if HAVE_SOCKADDR_IN_SIN_LEN - servaddr4.sin_len = sizeof (struct ::sockaddr_in); -#endif - servaddr = (struct sockaddr *) &servaddr4; - } - - if (use_ipv6) - bind_socket = create_socket (PF_INET6, SOCK_STREAM, 0); - else - bind_socket = create_socket (PF_INET, SOCK_STREAM, 0); - - if (bind_socket == -1) - { - cout << "Unable to create socket" << endl; - throw ::httpserver::webserver_exception(); - } - - result = setsockopt (bind_socket, - SOL_SOCKET, - SO_REUSEADDR, - (const char*) &on, sizeof (on)); - - if (result != 0) - { - cout << "Unable to set socket option SO_REUSEADDR" << endl; - throw ::httpserver::webserver_exception(); - } - - if(use_ipv6) - { -#ifdef IPPROTO_IPV6 -#ifdef IPV6_V6ONLY - setsockopt (bind_socket, - IPPROTO_IPV6, IPV6_V6ONLY, - (const char*) &on, sizeof (on) - ); -#endif -#endif - } - result = bind(bind_socket, servaddr, addrlen); - - if (result != 0) - { - cout << gettext("Unable to bind socket to port: ") << port << endl; - throw ::httpserver::webserver_exception(); - } - } -#ifdef _WINDOWS - unsigned long ioarg = 1; - ioctlsocket(bind_socket, FIONBIO, &ioarg); -#else - int flags = fcntl (bind_socket, F_GETFL); - flags |= O_NONBLOCK; - fcntl (bind_socket, F_SETFL, flags); -#endif - if(!bind_settled) - { - result = listen(bind_socket, 1); - if (result != 0) - { - cout << gettext("Unable to listen on port: ") << port << endl; - throw ::httpserver::webserver_exception(); - } - } - iov.push_back(gen(MHD_OPTION_LISTEN_SOCKET, bind_socket)); - } - iov.push_back(gen(MHD_OPTION_END, 0, NULL )); int start_conf = start_method; @@ -547,76 +356,30 @@ bool webserver::start(bool blocking) start_conf |= MHD_USE_DEBUG; if(pedantic) start_conf |= MHD_USE_PEDANTIC_CHECKS; + if(comet_enabled) + start_conf |= MHD_USE_SUSPEND_RESUME; #ifdef USE_FASTOPEN start_conf |= MHD_USE_TCP_FASTOPEN; #endif - int num_threads = 1; - if(max_threads > num_threads) - num_threads = max_threads; - this->running = true; - if(start_method == http_utils::INTERNAL_SELECT) - { - int i; - try { - for(i = 0; i < num_threads; i++) - { - struct MHD_Daemon* daemon = MHD_start_daemon - ( - start_conf, this->port, &policy_callback, this, - &answer_to_connection, this, MHD_OPTION_ARRAY, - &iov[0], MHD_OPTION_END - ); - if(NULL == daemon) - { - cout << gettext("Unable to connect daemon to port: ") << this->port << endl; - throw ::httpserver::webserver_exception(); - } - details::daemon_item* di = new details::daemon_item(this, daemon); - daemons.push_back(di); - - //RUN SELECT THREADS - pthread_t t; - threads.push_back(t); - - if(pthread_create( - &threads[i], - NULL, - &webserver::select, - static_cast(di) - )) - { - throw ::httpserver::webserver_exception(); - } - } - } catch (::httpserver::webserver_exception &e) { - this->running = false; - for (;i >= 0; --i) { - pthread_kill(threads[i], 9); - } - threads.clear(); - throw e; - } - } - else + + struct MHD_Daemon* daemon = MHD_start_daemon + ( + start_conf, this->port, &policy_callback, this, + &answer_to_connection, this, MHD_OPTION_ARRAY, + &iov[0], MHD_OPTION_END + ); + if(NULL == daemon) { - struct MHD_Daemon* daemon = MHD_start_daemon - ( - start_conf, this->port, &policy_callback, this, - &answer_to_connection, this, MHD_OPTION_ARRAY, - &iov[0], MHD_OPTION_END - ); - if(NULL == daemon) - { - cout << gettext("Unable to connect daemon to port: ") << - this->port << endl; - throw ::httpserver::webserver_exception(); - } - details::daemon_item* di = new details::daemon_item(this, daemon); - daemons.push_back(di); + cout << gettext("Unable to connect daemon to port: ") << + this->port << endl; + throw ::httpserver::webserver_exception(); } + details::daemon_item* di = new details::daemon_item(this, daemon); + daemons.push_back(di); + bool value_onclose = false; if(blocking) { @@ -651,12 +414,6 @@ bool webserver::stop() threads.clear(); typedef vector::const_iterator daemon_item_it; - if(start_method == http_utils::INTERNAL_SELECT) - { - for(daemon_item_it it = daemons.begin(); it != daemons.end(); ++it) - MHD_quiesce_daemon((*it)->daemon); - } - for(daemon_item_it it = daemons.begin(); it != daemons.end(); ++it) delete *it; daemons.clear(); @@ -1300,53 +1057,29 @@ int webserver::answer_to_connection(void* cls, MHD_Connection* connection, } } -void webserver::send_message_to_consumer( - const httpserver_ska& connection_id, - const std::string& message, - bool to_lock -) -{ - internal_comet_manager->send_message_to_consumer(connection_id, message, to_lock, start_method); -} - void webserver::send_message_to_topic( const std::string& topic, const std::string& message ) { - internal_comet_manager->send_message_to_topic(topic, message, start_method); + internal_comet_manager->send_message_to_topic(topic, message); } void webserver::register_to_topics( const std::vector& topics, - const httpserver_ska& connection_id, - int keepalive_secs, - string keepalive_msg + MHD_Connection* connection_id ) { - internal_comet_manager->register_to_topics(topics, connection_id, keepalive_secs, keepalive_msg, start_method); + internal_comet_manager->register_to_topics(topics, connection_id); } -size_t webserver::read_message(const httpserver_ska& connection_id, +size_t webserver::read_message(MHD_Connection* connection_id, std::string& message ) { return internal_comet_manager->read_message(connection_id, message); } -size_t webserver::get_topic_consumers( - const std::string& topic, - std::set& consumers -) -{ - return internal_comet_manager->get_topic_consumers(topic, consumers); -} - -bool webserver::pop_signaled(const httpserver_ska& consumer) -{ - return internal_comet_manager->pop_signaled(consumer, start_method); -} - http_response* webserver::get_from_cache( const std::string& key, bool* valid, @@ -1537,18 +1270,4 @@ void webserver::get_response(details::cache_entry* ce, http_response** res) *res = ce->response.ptr(); } -void webserver::remove_event_supplier(const std::string& id) -{ - pthread_rwlock_wrlock(&runguard); - event_suppliers.erase(id); - pthread_rwlock_unlock(&runguard); -} - -void webserver::register_event_supplier(const std::string& id, const details::event_tuple& evt) -{ - pthread_rwlock_wrlock(&runguard); - event_suppliers.insert(std::pair(id, evt)); - pthread_rwlock_unlock(&runguard); -} - }; diff --git a/test/integ/threaded.cpp b/test/integ/threaded.cpp index c9c8de63..6ca81afa 100644 --- a/test/integ/threaded.cpp +++ b/test/integ/threaded.cpp @@ -42,7 +42,7 @@ LT_BEGIN_SUITE(threaded_suite) void set_up() { - ws = new webserver(create_webserver(8080).max_threads(5)); + ws = new webserver(create_webserver(8080).start_method(http::http_utils::INTERNAL_SELECT).max_threads(5)); ws->start(false); } From 5d5b1dea73713e45d262398f8a997b9e72da9ea8 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Tue, 29 Dec 2015 19:49:45 +0100 Subject: [PATCH 182/623] Added benchmark example --- examples/benchmark.cpp | 68 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100755 examples/benchmark.cpp diff --git a/examples/benchmark.cpp b/examples/benchmark.cpp new file mode 100755 index 00000000..04209d77 --- /dev/null +++ b/examples/benchmark.cpp @@ -0,0 +1,68 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#include +#include + +#if defined(CPU_COUNT) && (CPU_COUNT+0) < 2 +#undef CPU_COUNT +#endif +#if !defined(CPU_COUNT) +#define CPU_COUNT 2 +#endif + +#define NUMBER_OF_THREADS CPU_COUNT + +#define PAGE "libmicrohttpd demolibhttpserver demo" + +using namespace httpserver; + +class hello_world_resource : public http_resource { + public: + void render(const http_request&, http_response**); +}; + +//using the render method you are able to catch each type of request you receive +void hello_world_resource::render(const http_request& req, http_response** res) +{ + //it is possible to send a response initializing an http_string_response + //that reads the content to send in response from a string. + *res = new http_response(http_response_builder(PAGE, 200).string_response()); +} + +int main() +{ + //it is possible to create a webserver passing a great number of parameters. + //In this case we are just passing the port and the number of thread running. + webserver ws = create_webserver(8080).start_method(http::http_utils::INTERNAL_SELECT).max_threads(NUMBER_OF_THREADS); + + hello_world_resource hwr; + //this way we are registering the hello_world_resource to answer for the endpoint + //"/hello". The requested method is called (if the request is a GET we call the render_GET + //method. In case that the specific render method is not implemented, the generic "render" + //method is called. + ws.register_resource("/", &hwr, true); + + //This way we are putting the created webserver in listen. We pass true in order to have + //a blocking call; if we want the call to be non-blocking we can just pass false to the + //method. + ws.start(true); + return 0; +} From 262819d7112dd771c16b884add0f956d51ef2485 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Wed, 30 Dec 2015 18:12:43 +0100 Subject: [PATCH 183/623] Removed unused code --- src/http_response.cpp | 2 -- src/httpserver/http_response.hpp | 4 ---- src/webserver.cpp | 3 +-- 3 files changed, 1 insertion(+), 8 deletions(-) diff --git a/src/http_response.cpp b/src/http_response.cpp index 47cc1a36..f80cd08a 100644 --- a/src/http_response.cpp +++ b/src/http_response.cpp @@ -53,8 +53,6 @@ http_response::http_response(const http_response_builder& builder): keepalive_msg(builder._keepalive_msg), send_topic(builder._send_topic), underlying_connection(0x0), - ca(0x0), - closure_data(0x0), ce(builder._ce), cycle_callback(builder._cycle_callback), get_raw_response(this, builder._get_raw_response), diff --git a/src/httpserver/http_response.hpp b/src/httpserver/http_response.hpp index dbab9618..cabc08f7 100644 --- a/src/httpserver/http_response.hpp +++ b/src/httpserver/http_response.hpp @@ -94,8 +94,6 @@ class http_response keepalive_msg(b.keepalive_msg), send_topic(b.send_topic), underlying_connection(b.underlying_connection), - ca(0x0), - closure_data(0x0), ce(b.ce), cycle_callback(b.cycle_callback), get_raw_response(b.get_raw_response), @@ -256,8 +254,6 @@ class http_response std::string keepalive_msg; std::string send_topic; struct MHD_Connection* underlying_connection; - void(*ca)(void*); - void* closure_data; details::cache_entry* ce; cycle_callback_ptr cycle_callback; diff --git a/src/webserver.cpp b/src/webserver.cpp index 7005b234..1d3ef49b 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -217,8 +217,7 @@ void webserver::request_completed ( { mr->ws->internal_comet_manager->complete_request(mr->dhrs->connection_id); } - if(mr->dhrs.res != 0x0 && mr->dhrs->ca != 0x0) - mr->dhrs->ca(mr->dhrs->closure_data); + delete mr; mr = 0x0; } From d3b30273b22955b31dd679b6bca1e55bc9a57903 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Wed, 30 Dec 2015 19:17:57 +0100 Subject: [PATCH 184/623] Linearized code to make it more readable --- src/webserver.cpp | 217 +++++++++++++++++++++------------------------- 1 file changed, 97 insertions(+), 120 deletions(-) diff --git a/src/webserver.cpp b/src/webserver.cpp index 1d3ef49b..cbdb7666 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -211,16 +211,12 @@ void webserver::request_completed ( ) { details::modded_request* mr = static_cast(*con_cls); - if (mr != 0x0) - { - if(mr->ws != 0x0) - { - mr->ws->internal_comet_manager->complete_request(mr->dhrs->connection_id); - } + if (mr == 0x0) return; - delete mr; - mr = 0x0; - } + if (mr->ws != 0x0) mr->ws->internal_comet_manager->complete_request(mr->dhrs->connection_id); + + delete mr; + mr = 0x0; } bool webserver::register_resource(const std::string& resource, http_resource* hrm, bool family) @@ -246,12 +242,12 @@ MHD_socket create_socket (int domain, int type, int protocol) int sock_cloexec = SOCK_CLOEXEC; int ctype = SOCK_STREAM | sock_cloexec; - /* use SOCK_STREAM rather than ai_socktype: some getaddrinfo + /* use SOCK_STREAM rather than ai_socktype: some getaddrinfo * implementations do not set ai_socktype, e.g. RHL6.2. */ MHD_socket fd = socket(domain, ctype, protocol); #ifdef _WINDOWS - if (fd == INVALID_SOCKET) + if (fd == INVALID_SOCKET) #else if ((fd == -1) && (errno == EINVAL || errno == EPROTONOSUPPORT) && (sock_cloexec != 0) @@ -398,33 +394,28 @@ bool webserver::is_running() bool webserver::stop() { - if(this->running) + if(!this->running) return false; + + pthread_mutex_lock(&mutexwait); + this->running = false; + pthread_cond_signal(&mutexcond); + pthread_mutex_unlock(&mutexwait); + for(unsigned int i = 0; i < threads.size(); ++i) { - pthread_mutex_lock(&mutexwait); - this->running = false; - pthread_cond_signal(&mutexcond); - pthread_mutex_unlock(&mutexwait); - for(unsigned int i = 0; i < threads.size(); ++i) - { - void* t_res; - pthread_join(threads[i], &t_res); - free(t_res); - } - threads.clear(); - typedef vector::const_iterator daemon_item_it; + void* t_res; + pthread_join(threads[i], &t_res); + free(t_res); + } + threads.clear(); + typedef vector::const_iterator daemon_item_it; - for(daemon_item_it it = daemons.begin(); it != daemons.end(); ++it) - delete *it; - daemons.clear(); + for(daemon_item_it it = daemons.begin(); it != daemons.end(); ++it) + delete *it; + daemons.clear(); - shutdown(bind_socket, 2); + shutdown(bind_socket, 2); - return true; - } - else - { - return false; - } + return true; } void webserver::unregister_resource(const string& resource) @@ -535,18 +526,20 @@ int webserver::build_request_args ( int policy_callback (void *cls, const struct sockaddr* addr, socklen_t addrlen) { - if((static_cast(cls))->ban_system_enabled) + if(!(static_cast(cls))->ban_system_enabled) return MHD_YES; + + if((((static_cast(cls))->default_policy == http_utils::ACCEPT) && + ((static_cast(cls))->bans.count(addr)) && + (!(static_cast(cls))->allowances.count(addr)) + ) || + (((static_cast(cls))->default_policy == http_utils::REJECT) + && ((!(static_cast(cls))->allowances.count(addr)) || + ((static_cast(cls))->bans.count(addr))) + )) { - if((((static_cast(cls))->default_policy == http_utils::ACCEPT) && - ((static_cast(cls))->bans.count(addr)) && - (!(static_cast(cls))->allowances.count(addr)) - ) || - (((static_cast(cls))->default_policy == http_utils::REJECT) - && ((!(static_cast(cls))->allowances.count(addr)) || - ((static_cast(cls))->bans.count(addr))) - )) - return MHD_NO; + return MHD_NO; } + return MHD_YES; } @@ -561,14 +554,12 @@ void* uri_log(void* cls, const char* uri) void error_log(void* cls, const char* fmt, va_list ap) { webserver* dws = static_cast(cls); - if(dws->log_error != 0x0) - dws->log_error(fmt); + if(dws->log_error != 0x0) dws->log_error(fmt); } void access_log(webserver* dws, string uri) { - if(dws->log_access != 0x0) - dws->log_access(uri); + if(dws->log_access != 0x0) dws->log_access(uri); } size_t unescaper_func(void * cls, struct MHD_Connection *c, char *s) @@ -590,10 +581,8 @@ size_t internal_unescaper(void* cls, char* s) dws->unescaper(s); return strlen(s); } - else - { - return http_unescape(s); - } + + return http_unescape(s); } int webserver::post_iterator (void *cls, enum MHD_ValueKind kind, @@ -704,21 +693,16 @@ int webserver::bodyfull_requests_answer_second_step( size_t* upload_data_size, struct details::modded_request* mr ) { - if ( 0 != *upload_data_size) - { + if (0 == *upload_data_size) return complete_request(connection, mr, version, method); + #ifdef DEBUG - cout << "Writing content: " << upload_data << endl; + cout << "Writing content: " << upload_data << endl; #endif //DEBUG - mr->dhr->grow_content(upload_data, *upload_data_size); - if (mr->pp != NULL) - { - MHD_post_process(mr->pp, upload_data, *upload_data_size); - } - *upload_data_size = 0; - return MHD_YES; - } + mr->dhr->grow_content(upload_data, *upload_data_size); - return complete_request(connection, mr, version, method); + if (mr->pp != NULL) MHD_post_process(mr->pp, upload_data, *upload_data_size); + *upload_data_size = 0; + return MHD_YES; } void webserver::end_request_construction( @@ -987,62 +971,7 @@ int webserver::answer_to_connection(void* cls, MHD_Connection* connection, struct details::modded_request* mr = static_cast(*con_cls); - if(mr->second == false) - { - mr->standardized_url = new string(); - internal_unescaper((void*) static_cast(cls), (char*) url); - http_utils::standardize_url(url, *mr->standardized_url); - - bool body = false; - - access_log( - static_cast(cls), - *(mr->complete_uri) + " METHOD: " + method - ); - - if( 0 == strcasecmp(method, http_utils::http_method_get.c_str())) - { - mr->callback = &http_resource::render_GET; - } - else if (0 == strcmp(method, http_utils::http_method_post.c_str())) - { - mr->callback = &http_resource::render_POST; - body = true; - } - else if (0 == strcasecmp(method, http_utils::http_method_put.c_str())) - { - mr->callback = &http_resource::render_PUT; - body = true; - } - else if (0 == strcasecmp(method,http_utils::http_method_delete.c_str())) - { - mr->callback = &http_resource::render_DELETE; - } - else if (0 == strcasecmp(method, http_utils::http_method_head.c_str())) - { - mr->callback = &http_resource::render_HEAD; - } - else if (0 ==strcasecmp(method,http_utils::http_method_connect.c_str())) - { - mr->callback = &http_resource::render_CONNECT; - } - else if (0 == strcasecmp(method, http_utils::http_method_trace.c_str())) - { - mr->callback = &http_resource::render_TRACE; - } - else if (0 ==strcasecmp(method,http_utils::http_method_options.c_str())) - { - mr->callback = &http_resource::render_OPTIONS; - } - - if(body) - return static_cast(cls)-> - bodyfull_requests_answer_first_step(connection, mr); - else - return static_cast(cls)-> - bodyless_requests_answer(connection, method, version, mr); - } - else + if(mr->second != false) { return static_cast(cls)-> bodyfull_requests_answer_second_step( @@ -1054,6 +983,54 @@ int webserver::answer_to_connection(void* cls, MHD_Connection* connection, mr ); } + + mr->standardized_url = new string(); + internal_unescaper((void*) static_cast(cls), (char*) url); + http_utils::standardize_url(url, *mr->standardized_url); + + bool body = false; + + access_log( + static_cast(cls), + *(mr->complete_uri) + " METHOD: " + method + ); + + if( 0 == strcasecmp(method, http_utils::http_method_get.c_str())) + { + mr->callback = &http_resource::render_GET; + } + else if (0 == strcmp(method, http_utils::http_method_post.c_str())) + { + mr->callback = &http_resource::render_POST; + body = true; + } + else if (0 == strcasecmp(method, http_utils::http_method_put.c_str())) + { + mr->callback = &http_resource::render_PUT; + body = true; + } + else if (0 == strcasecmp(method,http_utils::http_method_delete.c_str())) + { + mr->callback = &http_resource::render_DELETE; + } + else if (0 == strcasecmp(method, http_utils::http_method_head.c_str())) + { + mr->callback = &http_resource::render_HEAD; + } + else if (0 ==strcasecmp(method,http_utils::http_method_connect.c_str())) + { + mr->callback = &http_resource::render_CONNECT; + } + else if (0 == strcasecmp(method, http_utils::http_method_trace.c_str())) + { + mr->callback = &http_resource::render_TRACE; + } + else if (0 ==strcasecmp(method,http_utils::http_method_options.c_str())) + { + mr->callback = &http_resource::render_OPTIONS; + } + + return body ? static_cast(cls)->bodyfull_requests_answer_first_step(connection, mr) : static_cast(cls)->bodyless_requests_answer(connection, method, version, mr); } void webserver::send_message_to_topic( From cccacb6c91da57ac2934c4df916ff24f6e2ef793 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Wed, 30 Dec 2015 20:34:29 +0100 Subject: [PATCH 185/623] Linearized code to make it more readable --- src/details/http_endpoint.cpp | 146 ++++++++++------------------------ 1 file changed, 40 insertions(+), 106 deletions(-) diff --git a/src/details/http_endpoint.cpp b/src/details/http_endpoint.cpp index ef62d197..c3179f0c 100644 --- a/src/details/http_endpoint.cpp +++ b/src/details/http_endpoint.cpp @@ -50,10 +50,7 @@ http_endpoint::http_endpoint family_url(family), reg_compiled(false) { - if(use_regex) - this->url_modded = "^/"; - else - this->url_modded = "/"; + this->url_modded = use_regex ? "^/" : "/"; vector parts; #ifdef CASE_INSENSITIVE @@ -68,103 +65,49 @@ http_endpoint::http_endpoint http_utils::tokenize_url(url, parts); string buffered; bool first = true; - if(registration) + + for (unsigned int i = 0; i < parts.size(); i++) { - for(unsigned int i = 0; i< parts.size(); i++) + if(!registration) { - if((parts[i] != "") && (parts[i][0] != '{')) - { - if(first) - { - if(parts[i][0] == '^') - { - this->url_modded = parts[i]; - } - else - { - this->url_modded += parts[i]; - } - first = false; - } - else - { - this->url_modded += "/" + parts[i]; - } - } - else - { - if( - (parts[i].size() >= 3) && - (parts[i][0] == '{') && - (parts[i][parts[i].size() - 1] == '}') - ) - { - std::string::size_type bar = parts[i].find_first_of('|'); - if(bar != string::npos) - { - this->url_pars.push_back(parts[i].substr(1, bar - 1)); - if(first) - { - this->url_modded += parts[i].substr( - bar + 1, parts[i].size() - bar - 2 - ); - first = false; - } - else - { - this->url_modded += "/"+parts[i].substr( - bar + 1, parts[i].size() - bar - 2 - ); - } - } - else - { - this->url_pars.push_back( - parts[i].substr(1,parts[i].size() - 2) - ); - if(first) - { - this->url_modded += "([^\\/]+)"; - first = false; - } - else - { - this->url_modded += "/([^\\/]+)"; - } - } - this->chunk_positions.push_back(i); - } - else - { - throw bad_http_endpoint(); - } - } + this->url_modded += (first ? "" : "/") + parts[i]; + first = false; + this->url_pieces.push_back(parts[i]); + + continue; } - } - else - { - for(unsigned int i = 0; i< parts.size(); i++) + + if((parts[i] != "") && (parts[i][0] != '{')) { - if(first) - { - this->url_modded += parts[i]; - first = false; - } - else - { - this->url_modded += "/" + parts[i]; - } + this->url_modded = (first ? "" : "/") + (parts[i][0] == '^' ? "" : this->url_modded) + parts[i]; + first = false; this->url_pieces.push_back(parts[i]); + + continue; } + + if((parts[i].size() < 3) || (parts[i][0] != '{') || (parts[i][parts[i].size() - 1] != '}')) + throw bad_http_endpoint(); + + std::string::size_type bar = parts[i].find_first_of('|'); + this->url_pars.push_back(parts[i].substr(1, bar != string::npos ? bar - 1 : parts[i].size() - bar - 2)); + this->url_modded += (first ? "" : "/") + (bar != string::npos ? parts[i].substr(bar + 1, parts[i].size() - bar - 2) : "([^\\/]+)"); + + first = false; + + this->chunk_positions.push_back(i); + + this->url_pieces.push_back(parts[i]); } + if(use_regex) { this->url_modded += "$"; regcomp(&(this->re_url_modded), url_modded.c_str(), REG_EXTENDED|REG_ICASE|REG_NOSUB ); - reg_compiled = true; + this->reg_compiled = true; } } @@ -206,27 +149,18 @@ bool http_endpoint::operator <(const http_endpoint& b) const bool http_endpoint::match(const http_endpoint& url) const { - if(this->family_url && (url.url_pieces.size() >= this->url_pieces.size())) + + if(!this->family_url || url.url_pieces.size() < this->url_pieces.size()) + return regexec(&(this->re_url_modded), url.url_complete.c_str(), 0, NULL, 0) == 0; + + string nn = "/"; + bool first = true; + for(unsigned int i = 0; i < this->url_pieces.size(); i++) { - string nn = "/"; - bool first = true; - for(unsigned int i = 0; i < this->url_pieces.size(); i++) - { - if(first) - { - nn += url.url_pieces[i]; - first = false; - } - else - { - nn += "/" + url.url_pieces[i]; - } - } - return regexec(&(this->re_url_modded), nn.c_str(), 0, NULL, 0) == 0; + nn += (first ? "" : "/") + url.url_pieces[i]; + first = false; } - else - return regexec(&(this->re_url_modded), - url.url_complete.c_str(), 0, NULL, 0) == 0; + return regexec(&(this->re_url_modded), nn.c_str(), 0, NULL, 0) == 0; } }; From 0c2c52d6fcccaa9fa32c818443685dbfe1238ddf Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Mon, 4 Jan 2016 00:29:58 +0100 Subject: [PATCH 186/623] http_response_ptr is now thread safe using atomic --- src/httpserver/details/http_response_ptr.hpp | 97 ++++++++++---------- 1 file changed, 47 insertions(+), 50 deletions(-) diff --git a/src/httpserver/details/http_response_ptr.hpp b/src/httpserver/details/http_response_ptr.hpp index 860e0575..31f8ff93 100644 --- a/src/httpserver/details/http_response_ptr.hpp +++ b/src/httpserver/details/http_response_ptr.hpp @@ -25,6 +25,26 @@ #ifndef _HTTP_RESPONSE_PTR_HPP_ #define _HTTP_RESPONSE_PTR_HPP_ +#include "http_response.hpp" + +#if defined(__CLANG_ATOMICS) + +#define atomic_increment(object) \ + __c11_atomicadd_fetch(object, 1, __ATOMIC_RELAXED) + +#define atomic_decrement(object) \ + __c11_atomic_sub_fetch(object, 1, __ATOMIC_ACQ_REL) + +#else + +#define atomic_increment(object) \ + __atomic_add_fetch(object, 1, __ATOMIC_RELAXED) + +#define atomic_decrement(object) \ + __atomic_sub_fetch(object, 1, __ATOMIC_ACQ_REL) + +#endif + namespace httpserver { @@ -36,78 +56,55 @@ namespace details struct http_response_ptr { public: - http_response_ptr(): - res(0x0), - num_references(0x0) - { - num_references = new int(0); - } - http_response_ptr(http_response* res): - res(res), - num_references(0x0) + http_response_ptr(http_response* res = 0x0): + res(res) { - num_references = new int(0); + num_references = new int(1); } + http_response_ptr(const http_response_ptr& b): res(b.res), num_references(b.num_references) { - (*num_references)++; + atomic_increment(b.num_references); } + ~http_response_ptr() { - if(num_references) - { - if((*num_references) == 0) - { - if(res && res->is_autodelete()) - { - delete res; - res = 0x0; - } - delete num_references; - } - else - (*num_references)--; - } + if (atomic_decrement(num_references) != 0 || res == 0x0) return; + + delete res; + delete num_references; + + res = 0x0; + num_references = 0x0; + } + + http_response_ptr& operator=(http_response_ptr b) + { + using std::swap; + + swap(this->num_references, b.num_references); + swap(this->res, b.res); + + return *this; } + http_response& operator* () { return *res; } + http_response* operator-> () { return res; } + http_response* ptr() { return res; } - http_response_ptr& operator= (const http_response_ptr& b) - { - if( this != &b) - { - if(num_references) - { - if((*num_references) == 0) - { - if(res && res->autodelete) - { - delete res; - res = 0x0; - } - delete num_references; - } - else - (*num_references)--; - } - - res = b.res; - num_references = b.num_references; - (*num_references)++; - } - return *this; - } + private: http_response* res; int* num_references; From 195d2daedd9ca1e92e77d24420f758f7b34c3829 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Thu, 7 Jan 2016 02:27:03 +0100 Subject: [PATCH 187/623] Add support for gcc <= 4.6 --- src/httpserver/details/http_response_ptr.hpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/httpserver/details/http_response_ptr.hpp b/src/httpserver/details/http_response_ptr.hpp index 31f8ff93..9529ba83 100644 --- a/src/httpserver/details/http_response_ptr.hpp +++ b/src/httpserver/details/http_response_ptr.hpp @@ -35,7 +35,7 @@ #define atomic_decrement(object) \ __c11_atomic_sub_fetch(object, 1, __ATOMIC_ACQ_REL) -#else +#elif defined(__GNUC_ATOMICS) #define atomic_increment(object) \ __atomic_add_fetch(object, 1, __ATOMIC_RELAXED) @@ -43,6 +43,14 @@ #define atomic_decrement(object) \ __atomic_sub_fetch(object, 1, __ATOMIC_ACQ_REL) +#else + +#define atomic_increment(object) \ + __sync_add_and_fetch(object, 1) + +#define atomic_decrement(object) \ + __sync_sub_and_fetch(object, 1) + #endif namespace httpserver From 7353e9f7662720d0194f6333d1b0255d0cdb844a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Sch=C3=A4tzlein?= Date: Mon, 14 Mar 2016 14:30:41 +0100 Subject: [PATCH 188/623] FIX: logic error in refactoring with commit cccacb6c91da57ac2934c4df916ff24f6e2ef793 the logic was broken at two points: For the case "(parts[i] != "") && (parts[i][0] != '{') and ! first" url_modded was set to "/" + url_modded + parts[i] instead of url_modded + "/" + parts[i] The second error was subtracting variable 'bar' when bar == string::npos --- src/details/http_endpoint.cpp | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/details/http_endpoint.cpp b/src/details/http_endpoint.cpp index c3179f0c..84f073cf 100644 --- a/src/details/http_endpoint.cpp +++ b/src/details/http_endpoint.cpp @@ -80,8 +80,15 @@ http_endpoint::http_endpoint if((parts[i] != "") && (parts[i][0] != '{')) { - this->url_modded = (first ? "" : "/") + (parts[i][0] == '^' ? "" : this->url_modded) + parts[i]; - first = false; + if(first) + { + this->url_modded = (parts[i][0] == '^' ? "" : this->url_modded) + parts[i]; + first = false; + } + else + { + this->url_modded += "/" + parts[i]; + } this->url_pieces.push_back(parts[i]); continue; @@ -91,7 +98,7 @@ http_endpoint::http_endpoint throw bad_http_endpoint(); std::string::size_type bar = parts[i].find_first_of('|'); - this->url_pars.push_back(parts[i].substr(1, bar != string::npos ? bar - 1 : parts[i].size() - bar - 2)); + this->url_pars.push_back(parts[i].substr(1, bar != string::npos ? bar - 1 : parts[i].size() - 2)); this->url_modded += (first ? "" : "/") + (bar != string::npos ? parts[i].substr(bar + 1, parts[i].size() - bar - 2) : "([^\\/]+)"); first = false; From c2c9656cd47aeb4114735866853be46d19df18ec Mon Sep 17 00:00:00 2001 From: Jagat Date: Thu, 24 Mar 2016 16:46:57 -0400 Subject: [PATCH 189/623] For a POST parameter with a large value, libmicrohttpd will call webserver::post_iterator multiple times with chunnks of post data and successibe offsets. Currently, set_arg will overwrites the value. So after all the successive post_iterator calls, the value will be set to the last chunk. This change fixes the set_arg() function(s).. by checking for existing value and appending to it than overwriting it. --- src/httpserver/http_request.hpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/httpserver/http_request.hpp b/src/httpserver/http_request.hpp index a217356d..acc51e4d 100644 --- a/src/httpserver/http_request.hpp +++ b/src/httpserver/http_request.hpp @@ -442,7 +442,11 @@ class http_request **/ void set_arg(const std::string& key, const std::string& value) { - this->args[key] = value; + if (this->args[key].empty()) { + this->args[key] = value; + } else { + this->args[key].append(value); + } } /** * Method used to set an argument value by key. @@ -452,7 +456,11 @@ class http_request **/ void set_arg(const char* key, const char* value, size_t size) { - this->args[key] = std::string(value, size); + if (this->args[key].empty()) { + this->args[key] = std::string(value, size); + } else { + this->args[key].append(value, size); + } } /** * Method used to set the content of the request From 61995c4b43075feb6f9b45d534db487d831a8aae Mon Sep 17 00:00:00 2001 From: Walter Landry Date: Fri, 15 Apr 2016 17:09:56 -0700 Subject: [PATCH 190/623] Fix warnings about const results of functions --- src/httpserver/http_response.hpp | 14 +++++++------- src/httpserver/http_utils.hpp | 2 +- src/httpserver/webserver.hpp | 8 ++++---- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/httpserver/http_response.hpp b/src/httpserver/http_response.hpp index cabc08f7..3cbdc4f4 100644 --- a/src/httpserver/http_response.hpp +++ b/src/httpserver/http_response.hpp @@ -110,7 +110,7 @@ class http_response * Method used to get the content from the response. * @return the content in string form **/ - const std::string get_content() + std::string get_content() { return this->content; } @@ -125,7 +125,7 @@ class http_response * @param key The header identification * @return a string representing the value assumed by the header **/ - const std::string get_header(const std::string& key) + std::string get_header(const std::string& key) { return this->headers[key]; } @@ -140,7 +140,7 @@ class http_response * @param key The footer identification * @return a string representing the value assumed by the footer **/ - const std::string get_footer(const std::string& key) + std::string get_footer(const std::string& key) { return this->footers[key]; } @@ -150,7 +150,7 @@ class http_response result = this->footers[key]; } - const std::string get_cookie(const std::string& key) + std::string get_cookie(const std::string& key) { return this->cookies[key]; } @@ -189,7 +189,7 @@ class http_response return this->response_code; } - const std::string get_realm() const + std::string get_realm() const { return this->realm; } @@ -199,7 +199,7 @@ class http_response result = this->realm; } - const std::string get_opaque() const + std::string get_opaque() const { return this->opaque; } @@ -209,7 +209,7 @@ class http_response result = this->opaque; } - const bool need_nonce_reload() const + bool need_nonce_reload() const { return this->reload_nonce; } diff --git a/src/httpserver/http_utils.hpp b/src/httpserver/http_utils.hpp index 3559f0ea..a6fabf56 100644 --- a/src/httpserver/http_utils.hpp +++ b/src/httpserver/http_utils.hpp @@ -303,7 +303,7 @@ struct ip_representation ip_representation(const struct sockaddr* ip); bool operator <(const ip_representation& b) const; - const int weight() const + int weight() const { //variable-precision SWAR algorithm unsigned int x = mask; diff --git a/src/httpserver/webserver.hpp b/src/httpserver/webserver.hpp index 6e675697..a6769614 100644 --- a/src/httpserver/webserver.hpp +++ b/src/httpserver/webserver.hpp @@ -145,22 +145,22 @@ class webserver bool is_valid(const std::string& key); void clean_cache(); - const log_access_ptr get_access_logger() const + log_access_ptr get_access_logger() const { return this->log_access; } - const log_error_ptr get_error_logger() const + log_error_ptr get_error_logger() const { return this->log_error; } - const validator_ptr get_request_validator() const + validator_ptr get_request_validator() const { return this->validator; } - const unescaper_ptr get_unescaper() const + unescaper_ptr get_unescaper() const { return this->unescaper; } From 868f711f3483cc9ea8836e4fd7148790cc492cc7 Mon Sep 17 00:00:00 2001 From: Walter Landry Date: Sun, 17 Apr 2016 18:25:35 -0700 Subject: [PATCH 191/623] Parse POST's with format of formdata as well as urlencoded. --- src/webserver.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/webserver.cpp b/src/webserver.cpp index cbdb7666..14dd69a0 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -669,7 +669,12 @@ int webserver::bodyfull_requests_answer_first_step( encoding, strlen (MHD_HTTP_POST_ENCODING_FORM_URLENCODED) ) - )) + ) + || (0 == strncasecmp ( + MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA, + encoding, + strlen (MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA) + ))) ) ) { From 85848b95bb1fc61700b051b584e85931639713e2 Mon Sep 17 00:00:00 2001 From: Walter Landry Date: Sun, 17 Apr 2016 19:32:55 -0700 Subject: [PATCH 192/623] Handle POST's larger than 1024 bytes correctly. --- src/webserver.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/webserver.cpp b/src/webserver.cpp index cbdb7666..c54f2733 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -594,7 +594,7 @@ int webserver::post_iterator (void *cls, enum MHD_ValueKind kind, ) { struct details::modded_request* mr = (struct details::modded_request*) cls; - mr->dhr->set_arg(key, data, size); + mr->dhr->set_arg(key, mr->dhr->get_arg(key) + std::string(data, size)); return MHD_YES; } From 369707bd195b7831b34d51ae5da8510914d1a054 Mon Sep 17 00:00:00 2001 From: Walter Landry Date: Sun, 17 Apr 2016 22:07:00 -0700 Subject: [PATCH 193/623] Add a way to limit size of uploads --- src/httpserver/create_webserver.hpp | 7 +++++++ src/httpserver/http_request.hpp | 7 ++++++- src/httpserver/webserver.hpp | 1 + src/webserver.cpp | 3 ++- 4 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/httpserver/create_webserver.hpp b/src/httpserver/create_webserver.hpp index 78daa867..6fc7e447 100644 --- a/src/httpserver/create_webserver.hpp +++ b/src/httpserver/create_webserver.hpp @@ -52,6 +52,7 @@ class create_webserver _max_threads(0), _max_connections(0), _memory_limit(0), + _content_size_limit(0), _connection_timeout(DEFAULT_WS_TIMEOUT), _per_IP_connection_limit(0), _log_access(0x0), @@ -93,6 +94,7 @@ class create_webserver _max_threads(0), _max_connections(0), _memory_limit(0), + _content_size_limit(0), _connection_timeout(DEFAULT_WS_TIMEOUT), _per_IP_connection_limit(0), _log_access(0x0), @@ -147,6 +149,10 @@ class create_webserver { _memory_limit = memory_limit; return *this; } + create_webserver& content_size_limit(size_t content_size_limit) + { + _content_size_limit = content_size_limit; return *this; + } create_webserver& connection_timeout(int connection_timeout) { _connection_timeout = connection_timeout; return *this; @@ -333,6 +339,7 @@ class create_webserver int _max_threads; int _max_connections; int _memory_limit; + size_t _content_size_limit; int _connection_timeout; int _per_IP_connection_limit; log_access_ptr _log_access; diff --git a/src/httpserver/http_request.hpp b/src/httpserver/http_request.hpp index a217356d..a44c3ffa 100644 --- a/src/httpserver/http_request.hpp +++ b/src/httpserver/http_request.hpp @@ -467,9 +467,14 @@ class http_request * @param content The content to append. * @param size The size of the data to append. **/ - void grow_content(const char* content, size_t size) + void grow_content(const char* content, size_t size, + size_t content_size_limit) { this->content.append(content, size); + if (this->content.size() > content_size_limit) + { + this->content.resize (content_size_limit); + } } /** * Method used to set the path requested. diff --git a/src/httpserver/webserver.hpp b/src/httpserver/webserver.hpp index 6e675697..0875f5e8 100644 --- a/src/httpserver/webserver.hpp +++ b/src/httpserver/webserver.hpp @@ -179,6 +179,7 @@ class webserver const int max_threads; const int max_connections; const int memory_limit; + const size_t content_size_limit; const int connection_timeout; const int per_IP_connection_limit; log_access_ptr log_access; diff --git a/src/webserver.cpp b/src/webserver.cpp index c54f2733..57260287 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -141,6 +141,7 @@ webserver::webserver(const create_webserver& params): max_threads(params._max_threads), max_connections(params._max_connections), memory_limit(params._memory_limit), + content_size_limit(params._content_size_limit), connection_timeout(params._connection_timeout), per_IP_connection_limit(params._per_IP_connection_limit), log_access(params._log_access), @@ -698,7 +699,7 @@ int webserver::bodyfull_requests_answer_second_step( #ifdef DEBUG cout << "Writing content: " << upload_data << endl; #endif //DEBUG - mr->dhr->grow_content(upload_data, *upload_data_size); + mr->dhr->grow_content(upload_data, *upload_data_size, content_size_limit); if (mr->pp != NULL) MHD_post_process(mr->pp, upload_data, *upload_data_size); *upload_data_size = 0; From 2bdbda9b4a6444ffdbd6655b559e0f072399bfa6 Mon Sep 17 00:00:00 2001 From: Walter Landry Date: Mon, 18 Apr 2016 11:32:25 -0700 Subject: [PATCH 194/623] Properly limit the maximum size of the content and any processed posts or arguments. The default is unlimited. --- src/httpserver/create_webserver.hpp | 4 ++-- src/httpserver/http_request.hpp | 28 +++++++++++++++++++--------- src/webserver.cpp | 3 ++- 3 files changed, 23 insertions(+), 12 deletions(-) diff --git a/src/httpserver/create_webserver.hpp b/src/httpserver/create_webserver.hpp index 6fc7e447..191047d4 100644 --- a/src/httpserver/create_webserver.hpp +++ b/src/httpserver/create_webserver.hpp @@ -52,7 +52,7 @@ class create_webserver _max_threads(0), _max_connections(0), _memory_limit(0), - _content_size_limit(0), + _content_size_limit(static_cast(-1)), _connection_timeout(DEFAULT_WS_TIMEOUT), _per_IP_connection_limit(0), _log_access(0x0), @@ -94,7 +94,7 @@ class create_webserver _max_threads(0), _max_connections(0), _memory_limit(0), - _content_size_limit(0), + _content_size_limit(static_cast(-1)), _connection_timeout(DEFAULT_WS_TIMEOUT), _per_IP_connection_limit(0), _log_access(0x0), diff --git a/src/httpserver/http_request.hpp b/src/httpserver/http_request.hpp index a44c3ffa..6a135aa7 100644 --- a/src/httpserver/http_request.hpp +++ b/src/httpserver/http_request.hpp @@ -361,7 +361,7 @@ class http_request * Default constructor of the class. It is a specific responsibility of apis to initialize this type of objects. **/ http_request(): - content("") + content(""), content_size_limit(static_cast(-1)) { } /** @@ -381,6 +381,7 @@ class http_request args(b.args), querystring(b.querystring), content(b.content), + content_size_limit(b.content_size_limit), version(b.version), requestor(b.requestor), underlying_connection(b.underlying_connection) @@ -398,6 +399,7 @@ class http_request std::map args; std::string querystring; std::string content; + size_t content_size_limit; std::string version; std::string requestor; @@ -442,7 +444,7 @@ class http_request **/ void set_arg(const std::string& key, const std::string& value) { - this->args[key] = value; + this->args[key] = value.substr(0,content_size_limit); } /** * Method used to set an argument value by key. @@ -452,7 +454,8 @@ class http_request **/ void set_arg(const char* key, const char* value, size_t size) { - this->args[key] = std::string(value, size); + this->args[key] = std::string(value, + std::min(size, content_size_limit)); } /** * Method used to set the content of the request @@ -460,21 +463,28 @@ class http_request **/ void set_content(const std::string& content) { - this->content = content; + this->content = content.substr(0,content_size_limit); + } + /** + * Method used to set the maximum size of the content + * @param content_size_limit The limit on the maximum size of the content and arg's. + **/ + void set_content_size_limit(size_t content_size_limit) + { + this->content_size_limit = content_size_limit; } /** * Method used to append content to the request preserving the previous inserted content * @param content The content to append. * @param size The size of the data to append. **/ - void grow_content(const char* content, size_t size, - size_t content_size_limit) + void grow_content(const char* content, size_t size) { this->content.append(content, size); if (this->content.size() > content_size_limit) - { + { this->content.resize (content_size_limit); - } + } } /** * Method used to set the path requested. @@ -565,7 +575,7 @@ class http_request { std::map::const_iterator it; for(it = args.begin(); it != args.end(); ++it) - this->args[it->first] = it->second; + this->args[it->first] = it->second.substr(0,content_size_limit); } /** * Method used to set the username of the request. diff --git a/src/webserver.cpp b/src/webserver.cpp index 57260287..7e5b22b6 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -655,6 +655,7 @@ int webserver::bodyfull_requests_answer_first_step( { mr->second = true; mr->dhr = new http_request(); + mr->dhr->set_content_size_limit(content_size_limit); const char *encoding = MHD_lookup_connection_value ( connection, MHD_HEADER_KIND, @@ -699,7 +700,7 @@ int webserver::bodyfull_requests_answer_second_step( #ifdef DEBUG cout << "Writing content: " << upload_data << endl; #endif //DEBUG - mr->dhr->grow_content(upload_data, *upload_data_size, content_size_limit); + mr->dhr->grow_content(upload_data, *upload_data_size); if (mr->pp != NULL) MHD_post_process(mr->pp, upload_data, *upload_data_size); *upload_data_size = 0; From 311c208d6aacf31a51f6e21898b80865f10e505b Mon Sep 17 00:00:00 2001 From: Walter Landry Date: Mon, 18 Apr 2016 12:14:53 -0700 Subject: [PATCH 195/623] Increase memory limit for POST processing to 32kb (same as #MHD_POOL_SIZE_DEFAULT) --- src/webserver.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/webserver.cpp b/src/webserver.cpp index cbdb7666..95b4b0ad 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -673,9 +673,10 @@ int webserver::bodyfull_requests_answer_first_step( ) ) { + const size_t post_memory_limit (32*1024); // Same as #MHD_POOL_SIZE_DEFAULT mr->pp = MHD_create_post_processor ( connection, - 1024, + post_memory_limit, &post_iterator, mr ); From d727939cb3ef0afd1c6893e4bd1477dc8a1afaf9 Mon Sep 17 00:00:00 2001 From: Walter Landry Date: Tue, 19 Apr 2016 16:34:46 -0700 Subject: [PATCH 196/623] Add a function to check whether the content of a request was too large. --- src/httpserver/http_request.hpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/httpserver/http_request.hpp b/src/httpserver/http_request.hpp index 6a135aa7..e145f5cd 100644 --- a/src/httpserver/http_request.hpp +++ b/src/httpserver/http_request.hpp @@ -310,6 +310,18 @@ class http_request { result = this->content; } + /** + * Method to check whether the size of the content reached or exceeded content_size_limit. + * @return boolean + **/ + bool content_too_large() const + { + return content.size()>=content_size_limit; + } + /** + * Method used to get the content of the query string.. + * @return the query string in string representation + **/ const std::string get_querystring() const { return this->querystring; From 686d58e9650c86c43429a6008d845bd970f34b29 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Wed, 13 Jul 2016 03:22:54 +0200 Subject: [PATCH 197/623] Fixed memory leak in http_response_ptr --- src/httpserver/details/http_response_ptr.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/httpserver/details/http_response_ptr.hpp b/src/httpserver/details/http_response_ptr.hpp index 9529ba83..b5fa0c43 100644 --- a/src/httpserver/details/http_response_ptr.hpp +++ b/src/httpserver/details/http_response_ptr.hpp @@ -79,10 +79,10 @@ struct http_response_ptr ~http_response_ptr() { - if (atomic_decrement(num_references) != 0 || res == 0x0) return; + if (atomic_decrement(num_references) != 0) return; - delete res; - delete num_references; + if (res != 0x0) delete res; + if (num_references != 0x0) delete num_references; res = 0x0; num_references = 0x0; From 02d08679a9848b427de599a6ebc94860378c5d72 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Wed, 13 Jul 2016 03:26:58 +0200 Subject: [PATCH 198/623] Incremented version --- ChangeLog | 4 ++++ configure.ac | 2 +- debian/changelog.in | 6 ++++++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index 042a2b81..f4bb0e92 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +Wed Jul 13 02:23:11 2016 +0100 + Fixed problems with large payloads + Fixed memory leak in http_response_ptr + Tue Dec 29 18:56:31 2015 +0100 Removed support for event supplier (badly defined, complicated and almost useless) Eliminated custom selection logic (simplified overall code in webserver.cpp) diff --git a/configure.ac b/configure.ac index 821ff3e0..87a52096 100644 --- a/configure.ac +++ b/configure.ac @@ -22,7 +22,7 @@ AC_PREREQ(2.57) m4_define([libhttpserver_MAJOR_VERSION],[0])dnl m4_define([libhttpserver_MINOR_VERSION],[11])dnl -m4_define([libhttpserver_REVISION],[0])dnl +m4_define([libhttpserver_REVISION],[1])dnl m4_define([libhttpserver_PKG_VERSION],[libhttpserver_MAJOR_VERSION.libhttpserver_MINOR_VERSION.libhttpserver_REVISION])dnl m4_define([libhttpserver_LDF_VERSION],[libhttpserver_MAJOR_VERSION:libhttpserver_MINOR_VERSION:libhttpserver_REVISION])dnl AC_INIT([libhttpserver], libhttpserver_PKG_VERSION, [electrictwister2000@gmail.com]) diff --git a/debian/changelog.in b/debian/changelog.in index 642e52c4..1e4b2480 100644 --- a/debian/changelog.in +++ b/debian/changelog.in @@ -1,3 +1,9 @@ +libhttpserver (0.11.1) unstable; urgency=low + * Fixed problems with large payloads + * Fixed memory leak in http_response_ptr + + -- Sebastiano Merlino Wed, 13 Jul 2016 02:23:11 +0100 + libhttpserver (0.11.0) unstable; urgency=low * Removed support for event supplier (badly defined, complicated and almost useless) * Eliminated custom selection logic (simplified overall code in webserver.cpp) From bdf2329c41518dbbd07a3a5720f009ccba78211b Mon Sep 17 00:00:00 2001 From: Walter Landry Date: Thu, 14 Jul 2016 17:36:21 -0700 Subject: [PATCH 199/623] Update debian/ install files to handle the latest multiarch configuration --- debian/libhttpserver-dev.install.in | 6 +++--- debian/libhttpserver.install.in | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/debian/libhttpserver-dev.install.in b/debian/libhttpserver-dev.install.in index 934451bf..5af7a46f 100644 --- a/debian/libhttpserver-dev.install.in +++ b/debian/libhttpserver-dev.install.in @@ -1,4 +1,4 @@ usr/include -usr/lib/*.a -usr/lib/*.so -usr/lib/pkgconfig \ No newline at end of file +usr/lib/*/*.a +usr/lib/*/*.so +usr/lib/*/pkgconfig \ No newline at end of file diff --git a/debian/libhttpserver.install.in b/debian/libhttpserver.install.in index 49513a14..d20df8ab 100644 --- a/debian/libhttpserver.install.in +++ b/debian/libhttpserver.install.in @@ -1 +1 @@ -usr/lib/*.so.* +usr/lib/*/*.so.* From c5405fca66be5dba08f8b0b73bd5935fcea202a0 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Fri, 22 Jul 2016 02:13:27 +0200 Subject: [PATCH 200/623] Avoid memory leak due to user, pass and digested_user initialization --- src/httpserver/webserver.hpp | 2 +- src/webserver.cpp | 27 ++++++++++++--------------- 2 files changed, 13 insertions(+), 16 deletions(-) diff --git a/src/httpserver/webserver.hpp b/src/httpserver/webserver.hpp index ee86e542..ddb4fe0d 100644 --- a/src/httpserver/webserver.hpp +++ b/src/httpserver/webserver.hpp @@ -308,7 +308,7 @@ class webserver void end_request_construction(MHD_Connection* connection, struct details::modded_request* mr, const char* version, - const char* method, char* user, char* pass, char* digested_user + const char* method, char** user, char** pass, char** digested_user ); int finalize_answer(MHD_Connection* connection, diff --git a/src/webserver.cpp b/src/webserver.cpp index 8c370130..15399620 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -718,9 +718,9 @@ void webserver::end_request_construction( struct details::modded_request* mr, const char* version, const char* method, - char* user, - char* pass, - char* digested_user + char** user, + char** pass, + char** digested_user ) { mr->ws = this; @@ -752,12 +752,9 @@ void webserver::end_request_construction( mr->dhr->set_path(mr->standardized_url->c_str()); mr->dhr->set_method(method); - if(basic_auth_enabled) - { - user = MHD_basic_auth_get_username_password(connection, &pass); - } - if(digest_auth_enabled) - digested_user = MHD_digest_auth_get_username(connection); + if(basic_auth_enabled) *user = MHD_basic_auth_get_username_password(connection, pass); + if(digest_auth_enabled) *digested_user = MHD_digest_auth_get_username(connection); + mr->dhr->set_version(version); const MHD_ConnectionInfo * conninfo = MHD_get_connection_info( connection, @@ -769,12 +766,12 @@ void webserver::end_request_construction( mr->dhr->set_requestor_port(get_port(conninfo->client_addr)); if(pass != 0x0) { - mr->dhr->set_pass(pass); - mr->dhr->set_user(user); + mr->dhr->set_pass(*pass); + mr->dhr->set_user(*user); } if(digested_user != 0x0) { - mr->dhr->set_digested_user(digested_user); + mr->dhr->set_digested_user(*digested_user); } } @@ -953,9 +950,9 @@ int webserver::complete_request( mr, version, method, - pass, - user, - digested_user + &pass, + &user, + &digested_user ); int to_ret = finalize_answer(connection, mr, method); From ebc5acf3255994d96139922006e02dd14e108cf2 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Fri, 22 Jul 2016 02:17:26 +0200 Subject: [PATCH 201/623] Revert "Avoid memory leak due to user, pass and digested_user initialization" This reverts commit c5405fca66be5dba08f8b0b73bd5935fcea202a0. --- src/httpserver/webserver.hpp | 2 +- src/webserver.cpp | 27 +++++++++++++++------------ 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/src/httpserver/webserver.hpp b/src/httpserver/webserver.hpp index ddb4fe0d..ee86e542 100644 --- a/src/httpserver/webserver.hpp +++ b/src/httpserver/webserver.hpp @@ -308,7 +308,7 @@ class webserver void end_request_construction(MHD_Connection* connection, struct details::modded_request* mr, const char* version, - const char* method, char** user, char** pass, char** digested_user + const char* method, char* user, char* pass, char* digested_user ); int finalize_answer(MHD_Connection* connection, diff --git a/src/webserver.cpp b/src/webserver.cpp index 15399620..8c370130 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -718,9 +718,9 @@ void webserver::end_request_construction( struct details::modded_request* mr, const char* version, const char* method, - char** user, - char** pass, - char** digested_user + char* user, + char* pass, + char* digested_user ) { mr->ws = this; @@ -752,9 +752,12 @@ void webserver::end_request_construction( mr->dhr->set_path(mr->standardized_url->c_str()); mr->dhr->set_method(method); - if(basic_auth_enabled) *user = MHD_basic_auth_get_username_password(connection, pass); - if(digest_auth_enabled) *digested_user = MHD_digest_auth_get_username(connection); - + if(basic_auth_enabled) + { + user = MHD_basic_auth_get_username_password(connection, &pass); + } + if(digest_auth_enabled) + digested_user = MHD_digest_auth_get_username(connection); mr->dhr->set_version(version); const MHD_ConnectionInfo * conninfo = MHD_get_connection_info( connection, @@ -766,12 +769,12 @@ void webserver::end_request_construction( mr->dhr->set_requestor_port(get_port(conninfo->client_addr)); if(pass != 0x0) { - mr->dhr->set_pass(*pass); - mr->dhr->set_user(*user); + mr->dhr->set_pass(pass); + mr->dhr->set_user(user); } if(digested_user != 0x0) { - mr->dhr->set_digested_user(*digested_user); + mr->dhr->set_digested_user(digested_user); } } @@ -950,9 +953,9 @@ int webserver::complete_request( mr, version, method, - &pass, - &user, - &digested_user + pass, + user, + digested_user ); int to_ret = finalize_answer(connection, mr, method); From c8d270e30bc89f881c5ac1bd39c332f7a358f182 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 18 Sep 2016 22:25:49 +0200 Subject: [PATCH 202/623] marking ws as started only after mhd is started --- src/webserver.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/webserver.cpp b/src/webserver.cpp index 8c370130..cc74be76 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -359,8 +359,6 @@ bool webserver::start(bool blocking) start_conf |= MHD_USE_TCP_FASTOPEN; #endif - this->running = true; - struct MHD_Daemon* daemon = MHD_start_daemon ( start_conf, this->port, &policy_callback, this, @@ -377,6 +375,9 @@ bool webserver::start(bool blocking) daemons.push_back(di); bool value_onclose = false; + + this->running = true; + if(blocking) { pthread_mutex_lock(&mutexwait); From 4c18ce706ce18d5bf3a8871c9086c8995ad991cb Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Mon, 19 Sep 2016 03:33:24 +0200 Subject: [PATCH 203/623] Experimenting on improving readability of http_response interface --- examples/benchmark.cpp | 6 +- examples/comet.cpp | 8 +- examples/hello_world.cpp | 6 +- examples/service.cpp | 121 ++++++++++++---------- src/http_resource.cpp | 4 +- src/httpserver/create_webserver.hpp | 4 +- src/httpserver/details/modded_request.hpp | 2 +- src/httpserver/http_resource.hpp | 41 ++++---- src/httpserver/http_response.hpp | 13 ++- src/httpserver/webserver.hpp | 12 +-- src/webserver.cpp | 107 ++++++++----------- test/integ/basic.cpp | 53 +++++----- test/integ/threaded.cpp | 4 +- 13 files changed, 185 insertions(+), 196 deletions(-) diff --git a/examples/benchmark.cpp b/examples/benchmark.cpp index 04209d77..dab45332 100755 --- a/examples/benchmark.cpp +++ b/examples/benchmark.cpp @@ -36,15 +36,15 @@ using namespace httpserver; class hello_world_resource : public http_resource { public: - void render(const http_request&, http_response**); + const http_response render(const http_request&); }; //using the render method you are able to catch each type of request you receive -void hello_world_resource::render(const http_request& req, http_response** res) +const http_response hello_world_resource::render(const http_request& req) { //it is possible to send a response initializing an http_string_response //that reads the content to send in response from a string. - *res = new http_response(http_response_builder(PAGE, 200).string_response()); + return http_response(http_response_builder(PAGE, 200).string_response()); } int main() diff --git a/examples/comet.cpp b/examples/comet.cpp index ae45d839..2830fa9f 100755 --- a/examples/comet.cpp +++ b/examples/comet.cpp @@ -29,17 +29,17 @@ std::vector topics(topics_array, topics_array + sizeof(topics_array class comet_send_resource : public http_resource { public: - void render(const http_request& req, http_response** res) + const http_response render(const http_request& req) { - *res = new http_response(http_response_builder("Hi", 200).long_polling_send_response(topics_array[0])); + return http_response(http_response_builder("Hi", 200).long_polling_send_response(topics_array[0])); } }; class comet_listen_resource : public http_resource { public: - void render(const http_request& req, http_response** res) + const http_response render(const http_request& req) { - *res = new http_response(http_response_builder("OK", 200).long_polling_receive_response(topics)); + return http_response(http_response_builder("OK", 200).long_polling_receive_response(topics)); } }; diff --git a/examples/hello_world.cpp b/examples/hello_world.cpp index 646bdb4d..2742d47f 100755 --- a/examples/hello_world.cpp +++ b/examples/hello_world.cpp @@ -25,13 +25,13 @@ using namespace httpserver; class hello_world_resource : public http_resource { public: - void render(const http_request&, http_response**); + const http_response render(const http_request&); void set_some_data(const std::string &s) {data = s;} std::string data; }; //using the render method you are able to catch each type of request you receive -void hello_world_resource::render(const http_request& req, http_response** res) +const http_response hello_world_resource::render(const http_request& req) { //it is possible to store data inside the resource object that can be altered //through the requests @@ -42,7 +42,7 @@ void hello_world_resource::render(const http_request& req, http_response** res) //it is possible to send a response initializing an http_string_response //that reads the content to send in response from a string. - *res = new http_response(http_response_builder("Hello World!!!", 200).string_response()); + return http_response(http_response_builder("Hello World!!!", 200).string_response()); } int main() diff --git a/examples/service.cpp b/examples/service.cpp index b374c0de..48ea39f9 100644 --- a/examples/service.cpp +++ b/examples/service.cpp @@ -33,21 +33,14 @@ class service_resource: public http_resource { ~service_resource(); - void render_GET(const http_request &req, http_response** res); - - void render_PUT(const http_request &req, http_response** res); - - void render_POST(const http_request &req, http_response** res); - - void render(const http_request &req, http_response** res); - - void render_HEAD(const http_request &req, http_response** res); - - void render_OPTIONS(const http_request &req, http_response** res); - - void render_CONNECT(const http_request &req, http_response** res); - - void render_DELETE(const http_request &req, http_response** res); + const http_response render_GET(const http_request &req); + const http_response render_PUT(const http_request &req); + const http_response render_POST(const http_request &req); + const http_response render(const http_request &req); + const http_response render_HEAD(const http_request &req); + const http_response render_OPTIONS(const http_request &req); + const http_response render_CONNECT(const http_request &req); + const http_response render_DELETE(const http_request &req); private: @@ -60,102 +53,120 @@ service_resource::service_resource() service_resource::~service_resource() {} -void -service_resource::render_GET(const http_request &req, http_response** res) +const http_response +service_resource::render_GET(const http_request &req) { - std::cout << "service_resource::render_GET()" << std::endl; + std::cout << "service_resource::render_GET()" << std::endl; if (verbose) std::cout << req; - *res = new http_response(http_response_builder("GET response", 200).string_response()); + http_response res(http_response_builder("GET response", 200).string_response()); + + if (verbose) std::cout << res; + + return res; - if (verbose) std::cout << **res; } -void -service_resource::render_PUT(const http_request &req, http_response** res) +const http_response +service_resource::render_PUT(const http_request &req) { - std::cout << "service_resource::render_PUT()" << std::endl; + std::cout << "service_resource::render_PUT()" << std::endl; if (verbose) std::cout << req; - *res = new http_response(http_response_builder("PUT response", 200).string_response()); + http_response res(http_response_builder("PUT response", 200).string_response()); - if (verbose) std::cout << **res; + if (verbose) std::cout << res; + + return res; } -void -service_resource::render_POST(const http_request &req, http_response** res) +const http_response +service_resource::render_POST(const http_request &req) { - std::cout << "service_resource::render_POST()" << std::endl; + std::cout << "service_resource::render_POST()" << std::endl; if (verbose) std::cout << req; - *res = new http_response(http_response_builder("POST response", 200).string_response()); + http_response res(http_response_builder("POST response", 200).string_response()); + + if (verbose) std::cout << res; - if (verbose) std::cout << **res; + return res; } -void -service_resource::render(const http_request &req, http_response** res) + +const http_response +service_resource::render(const http_request &req) { - std::cout << "service_resource::render()" << std::endl; + std::cout << "service_resource::render()" << std::endl; if (verbose) std::cout << req; - *res = new http_response(http_response_builder("generic response", 200).string_response()); + http_response res(http_response_builder("generic response", 200).string_response()); + + if (verbose) std::cout << res; - if (verbose) std::cout << **res; + return res; } -void -service_resource::render_HEAD(const http_request &req, http_response** res) +const http_response +service_resource::render_HEAD(const http_request &req) { - std::cout << "service_resource::render_HEAD()" << std::endl; + std::cout << "service_resource::render_HEAD()" << std::endl; if (verbose) std::cout << req; - *res = new http_response(http_response_builder("HEAD response", 200).string_response()); + http_response res(http_response_builder("HEAD response", 200).string_response()); - if (verbose) std::cout << **res; + if (verbose) std::cout << res; + + return res; } -void -service_resource::render_OPTIONS(const http_request &req, http_response** res) +const http_response +service_resource::render_OPTIONS(const http_request &req) { - std::cout << "service_resource::render_OPTIONS()" << std::endl; + std::cout << "service_resource::render_OPTIONS()" << std::endl; if (verbose) std::cout << req; - *res = new http_response(http_response_builder("OPTIONS response", 200).string_response()); + http_response res(http_response_builder("OPTIONS response", 200).string_response()); + + if (verbose) std::cout << res; - if (verbose) std::cout << **res; + return res; } -void -service_resource::render_CONNECT(const http_request &req, http_response** res) +const http_response +service_resource::render_CONNECT(const http_request &req) { - std::cout << "service_resource::render_CONNECT()" << std::endl; + std::cout << "service_resource::render_CONNECT()" << std::endl; if (verbose) std::cout << req; - *res = new http_response(http_response_builder("CONNECT response", 200).string_response()); + http_response res(http_response_builder("CONNECT response", 200).string_response()); - if (verbose) std::cout << **res; + if (verbose) std::cout << res; + + return res; } -void -service_resource::render_DELETE(const http_request &req, http_response** res) +const http_response +service_resource::render_DELETE(const http_request &req) { - std::cout << "service_resource::render_DELETE()" << std::endl; + std::cout << "service_resource::render_DELETE()" << std::endl; if (verbose) std::cout << req; - *res = new http_response(http_response_builder("DELETE response", 200).string_response()); + http_response res(http_response_builder("DELETE response", 200).string_response()); + + if (verbose) std::cout << res; - if (verbose) std::cout << **res; + return res; } void usage() diff --git a/src/http_resource.cpp b/src/http_resource.cpp index 0142ec35..93e481e3 100644 --- a/src/http_resource.cpp +++ b/src/http_resource.cpp @@ -48,9 +48,9 @@ void resource_init(map& allowed_methods) namespace details { -void empty_render(const http_request& r, http_response** res) +http_response empty_render(const http_request& r) { - *res = new http_response(http_response_builder("", 200).string_response()); + return http_response_builder("", 200).string_response(); } }; diff --git a/src/httpserver/create_webserver.hpp b/src/httpserver/create_webserver.hpp index 191047d4..9cdf81a7 100644 --- a/src/httpserver/create_webserver.hpp +++ b/src/httpserver/create_webserver.hpp @@ -27,6 +27,7 @@ #include #include "httpserver/http_utils.hpp" +#include "httpserver/http_response.hpp" #define DEFAULT_WS_TIMEOUT 180 #define DEFAULT_WS_PORT 9898 @@ -35,9 +36,8 @@ namespace httpserver { class webserver; class http_request; -class http_response; -typedef void(*render_ptr)(const http_request&, http_response**); +typedef http_response(*render_ptr)(const http_request&); typedef bool(*validator_ptr)(const std::string&); typedef void(*unescaper_ptr)(char*); typedef void(*log_access_ptr)(const std::string&); diff --git a/src/httpserver/details/modded_request.hpp b/src/httpserver/details/modded_request.hpp index 42819a03..f47f9a5d 100644 --- a/src/httpserver/details/modded_request.hpp +++ b/src/httpserver/details/modded_request.hpp @@ -41,7 +41,7 @@ struct modded_request std::string* standardized_url; webserver* ws; - void (httpserver::http_resource::*callback)(const httpserver::http_request&, httpserver::http_response**); + const http_response (httpserver::http_resource::*callback)(const httpserver::http_request&); http_request* dhr; http_response_ptr dhrs; diff --git a/src/httpserver/http_resource.hpp b/src/httpserver/http_resource.hpp index 8b89684e..6465f5c3 100644 --- a/src/httpserver/http_resource.hpp +++ b/src/httpserver/http_resource.hpp @@ -31,17 +31,18 @@ #include #endif +#include "httpserver/http_response.hpp" + namespace httpserver { class webserver; class http_request; -class http_response; namespace details { -void empty_render(const http_request& r, http_response** res); +http_response empty_render(const http_request& r); }; @@ -66,81 +67,81 @@ class http_resource * @param req Request passed through http * @return A http_response object **/ - virtual void render(const http_request& r, http_response** res) + virtual const http_response render(const http_request& req) { - details::empty_render(r, res); + return details::empty_render(req); } /** * Method used to answer to a GET request * @param req Request passed through http * @return A http_response object **/ - virtual void render_GET(const http_request& req, http_response** res) + virtual const http_response render_GET(const http_request& req) { - render(req, res); + return render(req); } /** * Method used to answer to a POST request * @param req Request passed through http * @return A http_response object **/ - virtual void render_POST(const http_request& req, http_response** res) + virtual const http_response render_POST(const http_request& req) { - render(req, res); + return render(req); } /** * Method used to answer to a PUT request * @param req Request passed through http * @return A http_response object **/ - virtual void render_PUT(const http_request& req, http_response** res) + virtual const http_response render_PUT(const http_request& req) { - render(req, res); + return render(req); } /** * Method used to answer to a HEAD request * @param req Request passed through http * @return A http_response object **/ - virtual void render_HEAD(const http_request& req, http_response** res) + virtual const http_response render_HEAD(const http_request& req) { - render(req, res); + return render(req); } /** * Method used to answer to a DELETE request * @param req Request passed through http * @return A http_response object **/ - virtual void render_DELETE(const http_request& req, http_response** res) + virtual const http_response render_DELETE(const http_request& req) { - render(req, res); + return render(req); } /** * Method used to answer to a TRACE request * @param req Request passed through http * @return A http_response object **/ - virtual void render_TRACE(const http_request& req, http_response** res) + virtual const http_response render_TRACE(const http_request& req) { - render(req, res); + return render(req); } /** * Method used to answer to a OPTIONS request * @param req Request passed through http * @return A http_response object **/ - virtual void render_OPTIONS(const http_request& req, http_response** res) + virtual const http_response render_OPTIONS(const http_request& req) { - render(req, res); + return render(req); } /** * Method used to answer to a CONNECT request * @param req Request passed through http * @return A http_response object **/ - virtual void render_CONNECT(const http_request& req, http_response** res) + virtual const http_response render_CONNECT(const http_request& req) { - render(req, res); + return render(req); } /** * Method used to set if a specific method is allowed or not on this request diff --git a/src/httpserver/http_response.hpp b/src/httpserver/http_response.hpp index 3cbdc4f4..6fa68693 100644 --- a/src/httpserver/http_response.hpp +++ b/src/httpserver/http_response.hpp @@ -32,8 +32,10 @@ #include #include "httpserver/binders.hpp" +#include "httpserver/http_utils.hpp" struct MHD_Connection; +struct MHD_Response; namespace httpserver { @@ -41,12 +43,6 @@ namespace httpserver class webserver; class http_response_builder; -namespace http -{ - class header_comparator; - class arg_comparator; -}; - namespace details { struct http_response_ptr; @@ -70,7 +66,6 @@ typedef ssize_t(*cycle_callback_ptr)(char*, size_t); class http_response { public: - http_response(const http_response_builder& builder); /** @@ -105,6 +100,10 @@ class http_response { } + http_response(): response_code(-1) + { + } + ~http_response(); /** * Method used to get the content from the response. diff --git a/src/httpserver/webserver.hpp b/src/httpserver/webserver.hpp index ee86e542..312b154f 100644 --- a/src/httpserver/webserver.hpp +++ b/src/httpserver/webserver.hpp @@ -44,11 +44,11 @@ #include #include "httpserver/create_webserver.hpp" +#include "httpserver/http_response.hpp" namespace httpserver { class http_resource; -class http_response; class create_webserver; namespace http { @@ -235,13 +235,9 @@ class webserver static void* select(void* self); static void* cleaner(void* self); - void method_not_allowed_page(http_response** dhrs, - details::modded_request* mr - ); - void internal_error_page(http_response** dhrs, - details::modded_request* mr, bool force_our = false - ); - void not_found_page(http_response** dhrs, details::modded_request* mr); + const http_response method_not_allowed_page(details::modded_request* mr) const; + const http_response internal_error_page(details::modded_request* mr, bool force_our = false) const; + const http_response not_found_page(details::modded_request* mr) const; static int method_not_acceptable_page ( diff --git a/src/webserver.cpp b/src/webserver.cpp index cc74be76..bacf9298 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -62,6 +62,8 @@ #define SOCK_CLOEXEC 02000000 #endif +#define NEW_OR_MOVE(TYPE, VALUE) new TYPE(VALUE) + using namespace std; namespace httpserver @@ -605,38 +607,36 @@ void webserver::upgrade_handler (void *cls, struct MHD_Connection* connection, { } -void webserver::not_found_page( - http_response** dhrs, - details::modded_request* mr -) +const http_response webserver::not_found_page(details::modded_request* mr) const { if(not_found_resource != 0x0) - not_found_resource(*mr->dhr, dhrs); + { + return not_found_resource(*mr->dhr); + } else - *dhrs = new http_response(http_response_builder(NOT_FOUND_ERROR, http_utils::http_not_found).string_response()); + { + return http_response(http_response_builder(NOT_FOUND_ERROR, http_utils::http_not_found).string_response()); + } } -void webserver::method_not_allowed_page( - http_response** dhrs, - details::modded_request* mr -) +const http_response webserver::method_not_allowed_page(details::modded_request* mr) const { if(method_not_acceptable_resource != 0x0) - method_not_allowed_resource(*mr->dhr, dhrs); + { + return method_not_allowed_resource(*mr->dhr); + } else - *dhrs = new http_response(http_response_builder(METHOD_ERROR, http_utils::http_method_not_allowed).string_response()); + { + return http_response(http_response_builder(METHOD_ERROR, http_utils::http_method_not_allowed).string_response()); + } } -void webserver::internal_error_page( - http_response** dhrs, - details::modded_request* mr, - bool force_our -) +const http_response webserver::internal_error_page(details::modded_request* mr, bool force_our) const { if(internal_error_resource != 0x0 && !force_our) - internal_error_resource(*mr->dhr, dhrs); + return internal_error_resource(*mr->dhr); else - *dhrs = new http_response(http_response_builder(GENERIC_ERROR, http_utils::http_internal_server_error).string_response()); + return http_response(http_response_builder(GENERIC_ERROR, http_utils::http_internal_server_error).string_response()); } int webserver::bodyless_requests_answer( @@ -786,7 +786,6 @@ int webserver::finalize_answer( ) { int to_ret = MHD_NO; - http_response* dhrs = 0x0; map::iterator fe; @@ -803,36 +802,19 @@ int webserver::finalize_answer( if(regex_checking) { - map< - details::http_endpoint, http_resource* - >::iterator found_endpoint; + map::iterator found_endpoint; - details::http_endpoint endpoint( - st_url, false, false, regex_checking - ); + details::http_endpoint endpoint(st_url, false, false, regex_checking); - map< - details::http_endpoint, - http_resource* - >::iterator it; + map::iterator it; size_t len = 0; size_t tot_len = 0; - for( - it=registered_resources.begin(); - it!=registered_resources.end(); - ++it - ) + for(it=registered_resources.begin(); it!=registered_resources.end(); ++it) { size_t endpoint_pieces_len = (*it).first.get_url_pieces_num(); size_t endpoint_tot_len = (*it).first.get_url_complete_size(); - if(!found || - endpoint_pieces_len > len || - ( - endpoint_pieces_len == len && - endpoint_tot_len > tot_len - ) - ) + if(!found || endpoint_pieces_len > len || (endpoint_pieces_len == len && endpoint_tot_len > tot_len)) { if((*it).first.match(endpoint)) { @@ -847,8 +829,7 @@ int webserver::finalize_answer( { vector url_pars; - size_t pars_size = - found_endpoint->first.get_url_pars(url_pars); + size_t pars_size = found_endpoint->first.get_url_pars(url_pars); vector url_pieces; endpoint.get_url_pieces(url_pieces); @@ -874,6 +855,7 @@ int webserver::finalize_answer( hrm = registered_resources.begin()->second; found = true; } + mr->dhr->set_underlying_connection(connection); if(found) @@ -882,58 +864,59 @@ int webserver::finalize_answer( { if(hrm->is_allowed(method)) { - ((hrm)->*(mr->callback))(*mr->dhr, &dhrs); - if (dhrs == 0x0) internal_error_page(&dhrs, mr); + mr->dhrs = NEW_OR_MOVE(http_response, ((hrm)->*(mr->callback))(*mr->dhr)); //copy in memory (move in case) + if (mr->dhrs->get_response_code() == -1) mr->dhrs = NEW_OR_MOVE(http_response, internal_error_page(mr)); } else { - method_not_allowed_page(&dhrs, mr); + mr->dhrs = NEW_OR_MOVE(http_response, method_not_allowed_page(mr)); } } catch(const std::exception& e) { - internal_error_page(&dhrs, mr); + mr->dhrs = NEW_OR_MOVE(http_response, internal_error_page(mr)); } catch(...) { - internal_error_page(&dhrs, mr); + mr->dhrs = NEW_OR_MOVE(http_response, internal_error_page(mr)); } } else { - not_found_page(&dhrs, mr); + mr->dhrs = NEW_OR_MOVE(http_response, not_found_page(mr)); } - mr->dhrs = dhrs; + mr->dhrs->underlying_connection = connection; + try { try { - dhrs->get_raw_response(&raw_response, this); + mr->dhrs->get_raw_response(&raw_response, this); } catch(const file_access_exception& fae) { - not_found_page(&dhrs, mr); - dhrs->get_raw_response(&raw_response, this); + mr->dhrs = NEW_OR_MOVE(http_response, not_found_page(mr)); + mr->dhrs->get_raw_response(&raw_response, this); } catch(const std::exception& e) { - internal_error_page(&dhrs, mr); - dhrs->get_raw_response(&raw_response, this); + mr->dhrs = NEW_OR_MOVE(http_response, internal_error_page(mr)); + mr->dhrs->get_raw_response(&raw_response, this); } catch(...) { - internal_error_page(&dhrs, mr); - dhrs->get_raw_response(&raw_response, this); + mr->dhrs = NEW_OR_MOVE(http_response, internal_error_page(mr)); + mr->dhrs->get_raw_response(&raw_response, this); } } catch(...) { - internal_error_page(&dhrs, mr, true); - dhrs->get_raw_response(&raw_response, this); + mr->dhrs = NEW_OR_MOVE(http_response, internal_error_page(mr, true)); + mr->dhrs->get_raw_response(&raw_response, this); } - dhrs->decorate_response(raw_response); - to_ret = dhrs->enqueue_response(connection, raw_response); + mr->dhrs->decorate_response(raw_response); + to_ret = mr->dhrs->enqueue_response(connection, raw_response); MHD_destroy_response (raw_response); return to_ret; } diff --git a/test/integ/basic.cpp b/test/integ/basic.cpp index 69530c24..0d7d20e9 100644 --- a/test/integ/basic.cpp +++ b/test/integ/basic.cpp @@ -48,95 +48,94 @@ size_t headerfunc(void *ptr, size_t size, size_t nmemb, map* ss) class simple_resource : public http_resource { public: - void render_GET(const http_request& req, http_response** res) + const http_response render_GET(const http_request& req) { - *res = new http_response(http_response_builder("OK", 200, "text/plain").string_response()); + return http_response(http_response_builder("OK", 200, "text/plain").string_response()); } - void render_POST(const http_request& req, http_response** res) + const http_response render_POST(const http_request& req) { - *res = new http_response( - http_response_builder(req.get_arg("arg1")+req.get_arg("arg2"), 200, "text/plain").string_response() - ); + return http_response(http_response_builder(req.get_arg("arg1")+req.get_arg("arg2"), 200, "text/plain").string_response()); } }; class long_content_resource : public http_resource { public: - void render_GET(const http_request& req, http_response** res) + const http_response render_GET(const http_request& req) { - *res = new http_response(http_response_builder(lorem_ipsum, 200, "text/plain").string_response()); + return http_response(http_response_builder(lorem_ipsum, 200, "text/plain").string_response()); } }; class header_test_resource : public http_resource { public: - void render_GET(const http_request& req, http_response** res) + const http_response render_GET(const http_request& req) { http_response_builder hrb("OK", 200, "text/plain"); hrb.with_header("KEY", "VALUE"); - *res = new http_response(hrb.string_response()); + return http_response(hrb.string_response()); } }; class complete_test_resource : public http_resource { public: - void render_GET(const http_request& req, http_response** res) + const http_response render_GET(const http_request& req) { - *res = new http_response(http_response_builder("OK", 200, "text/plain").string_response()); + return http_response(http_response_builder("OK", 200, "text/plain").string_response()); } - void render_POST(const http_request& req, http_response** res) + const http_response render_POST(const http_request& req) { - *res = new http_response(http_response_builder("OK", 200, "text/plain").string_response()); + return http_response(http_response_builder("OK", 200, "text/plain").string_response()); } - void render_PUT(const http_request& req, http_response** res) + const http_response render_PUT(const http_request& req) { - *res = new http_response(http_response_builder("OK", 200, "text/plain").string_response()); + return http_response(http_response_builder("OK", 200, "text/plain").string_response()); } - void render_DELETE(const http_request& req, http_response** res) + const http_response render_DELETE(const http_request& req) { - *res = new http_response(http_response_builder("OK", 200, "text/plain").string_response()); + return http_response(http_response_builder("OK", 200, "text/plain").string_response()); } - void render_CONNECT(const http_request& req, http_response** res) + const http_response render_CONNECT(const http_request& req) { - *res = new http_response(http_response_builder("OK", 200, "text/plain").string_response()); + return http_response(http_response_builder("OK", 200, "text/plain").string_response()); } }; class only_render_resource : public http_resource { public: - void render(const http_request& req, http_response** res) + const http_response render(const http_request& req) { - *res = new http_response(http_response_builder("OK", 200, "text/plain").string_response()); + return http_response(http_response_builder("OK", 200, "text/plain").string_response()); } }; class ok_resource : public http_resource { public: - void render_GET(const http_request& req, http_response** res) + const http_response render_GET(const http_request& req) { - *res = new http_response(http_response_builder("OK", 200, "text/plain").string_response()); + return http_response(http_response_builder("OK", 200, "text/plain").string_response()); } }; class nok_resource : public http_resource { public: - void render_GET(const http_request& req, http_response** res) + const http_response render_GET(const http_request& req) { - *res = new http_response(http_response_builder("NOK", 200, "text/plain").string_response()); + return http_response(http_response_builder("NOK", 200, "text/plain").string_response()); } }; class no_response_resource : public http_resource { public: - void render_GET(const http_request& req, http_response** res) + const http_response render_GET(const http_request& req) { + return http_response(); } }; diff --git a/test/integ/threaded.cpp b/test/integ/threaded.cpp index 6ca81afa..ed7f4a70 100644 --- a/test/integ/threaded.cpp +++ b/test/integ/threaded.cpp @@ -30,9 +30,9 @@ using namespace std; class ok_resource : public http_resource { public: - void render_GET(const http_request& req, http_response** res) + const http_response render_GET(const http_request& req) { - *res = new http_response(http_response_builder("OK", 200, "text/plain").string_response()); + return http_response(http_response_builder("OK", 200, "text/plain").string_response()); } }; From b0445308917da73e23c61d3d38cdac11fb15ee7d Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Mon, 19 Sep 2016 04:12:23 +0200 Subject: [PATCH 204/623] Avoid double-call to copy constructor --- examples/benchmark.cpp | 2 +- examples/comet.cpp | 4 ++-- examples/hello_world.cpp | 2 +- src/webserver.cpp | 6 +++--- test/integ/basic.cpp | 24 ++++++++++++------------ test/integ/threaded.cpp | 2 +- 6 files changed, 20 insertions(+), 20 deletions(-) diff --git a/examples/benchmark.cpp b/examples/benchmark.cpp index dab45332..94e4bc26 100755 --- a/examples/benchmark.cpp +++ b/examples/benchmark.cpp @@ -44,7 +44,7 @@ const http_response hello_world_resource::render(const http_request& req) { //it is possible to send a response initializing an http_string_response //that reads the content to send in response from a string. - return http_response(http_response_builder(PAGE, 200).string_response()); + return http_response_builder(PAGE, 200).string_response(); } int main() diff --git a/examples/comet.cpp b/examples/comet.cpp index 2830fa9f..33eb3d7c 100755 --- a/examples/comet.cpp +++ b/examples/comet.cpp @@ -31,7 +31,7 @@ class comet_send_resource : public http_resource { public: const http_response render(const http_request& req) { - return http_response(http_response_builder("Hi", 200).long_polling_send_response(topics_array[0])); + return http_response_builder("Hi", 200).long_polling_send_response(topics_array[0]); } }; @@ -39,7 +39,7 @@ class comet_listen_resource : public http_resource { public: const http_response render(const http_request& req) { - return http_response(http_response_builder("OK", 200).long_polling_receive_response(topics)); + return http_response_builder("OK", 200).long_polling_receive_response(topics); } }; diff --git a/examples/hello_world.cpp b/examples/hello_world.cpp index 2742d47f..9a0a4e4c 100755 --- a/examples/hello_world.cpp +++ b/examples/hello_world.cpp @@ -42,7 +42,7 @@ const http_response hello_world_resource::render(const http_request& req) //it is possible to send a response initializing an http_string_response //that reads the content to send in response from a string. - return http_response(http_response_builder("Hello World!!!", 200).string_response()); + return http_response_builder("Hello World!!!", 200).string_response(); } int main() diff --git a/src/webserver.cpp b/src/webserver.cpp index bacf9298..f7cc1ee5 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -615,7 +615,7 @@ const http_response webserver::not_found_page(details::modded_request* mr) const } else { - return http_response(http_response_builder(NOT_FOUND_ERROR, http_utils::http_not_found).string_response()); + return http_response_builder(NOT_FOUND_ERROR, http_utils::http_not_found).string_response(); } } @@ -627,7 +627,7 @@ const http_response webserver::method_not_allowed_page(details::modded_request* } else { - return http_response(http_response_builder(METHOD_ERROR, http_utils::http_method_not_allowed).string_response()); + return http_response_builder(METHOD_ERROR, http_utils::http_method_not_allowed).string_response(); } } @@ -636,7 +636,7 @@ const http_response webserver::internal_error_page(details::modded_request* mr, if(internal_error_resource != 0x0 && !force_our) return internal_error_resource(*mr->dhr); else - return http_response(http_response_builder(GENERIC_ERROR, http_utils::http_internal_server_error).string_response()); + return http_response_builder(GENERIC_ERROR, http_utils::http_internal_server_error).string_response(); } int webserver::bodyless_requests_answer( diff --git a/test/integ/basic.cpp b/test/integ/basic.cpp index 0d7d20e9..69b6657f 100644 --- a/test/integ/basic.cpp +++ b/test/integ/basic.cpp @@ -50,11 +50,11 @@ class simple_resource : public http_resource public: const http_response render_GET(const http_request& req) { - return http_response(http_response_builder("OK", 200, "text/plain").string_response()); + return http_response_builder("OK", 200, "text/plain").string_response(); } const http_response render_POST(const http_request& req) { - return http_response(http_response_builder(req.get_arg("arg1")+req.get_arg("arg2"), 200, "text/plain").string_response()); + return http_response_builder(req.get_arg("arg1")+req.get_arg("arg2"), 200, "text/plain").string_response(); } }; @@ -63,7 +63,7 @@ class long_content_resource : public http_resource public: const http_response render_GET(const http_request& req) { - return http_response(http_response_builder(lorem_ipsum, 200, "text/plain").string_response()); + return http_response_builder(lorem_ipsum, 200, "text/plain").string_response(); } }; @@ -74,7 +74,7 @@ class header_test_resource : public http_resource { http_response_builder hrb("OK", 200, "text/plain"); hrb.with_header("KEY", "VALUE"); - return http_response(hrb.string_response()); + return http_response(hrb.string_response(); } }; @@ -83,23 +83,23 @@ class complete_test_resource : public http_resource public: const http_response render_GET(const http_request& req) { - return http_response(http_response_builder("OK", 200, "text/plain").string_response()); + return http_response_builder("OK", 200, "text/plain").string_response(); } const http_response render_POST(const http_request& req) { - return http_response(http_response_builder("OK", 200, "text/plain").string_response()); + return http_response_builder("OK", 200, "text/plain").string_response(); } const http_response render_PUT(const http_request& req) { - return http_response(http_response_builder("OK", 200, "text/plain").string_response()); + return http_response_builder("OK", 200, "text/plain").string_response(); } const http_response render_DELETE(const http_request& req) { - return http_response(http_response_builder("OK", 200, "text/plain").string_response()); + return http_response_builder("OK", 200, "text/plain").string_response(); } const http_response render_CONNECT(const http_request& req) { - return http_response(http_response_builder("OK", 200, "text/plain").string_response()); + return http_response_builder("OK", 200, "text/plain").string_response(); } }; @@ -108,7 +108,7 @@ class only_render_resource : public http_resource public: const http_response render(const http_request& req) { - return http_response(http_response_builder("OK", 200, "text/plain").string_response()); + return http_response_builder("OK", 200, "text/plain").string_response(); } }; @@ -117,7 +117,7 @@ class ok_resource : public http_resource public: const http_response render_GET(const http_request& req) { - return http_response(http_response_builder("OK", 200, "text/plain").string_response()); + return http_response_builder("OK", 200, "text/plain").string_response(); } }; @@ -126,7 +126,7 @@ class nok_resource : public http_resource public: const http_response render_GET(const http_request& req) { - return http_response(http_response_builder("NOK", 200, "text/plain").string_response()); + return http_response_builder("NOK", 200, "text/plain").string_response(); } }; diff --git a/test/integ/threaded.cpp b/test/integ/threaded.cpp index ed7f4a70..575e5ab5 100644 --- a/test/integ/threaded.cpp +++ b/test/integ/threaded.cpp @@ -32,7 +32,7 @@ class ok_resource : public http_resource public: const http_response render_GET(const http_request& req) { - return http_response(http_response_builder("OK", 200, "text/plain").string_response()); + return http_response_builder("OK", 200, "text/plain").string_response(); } }; From ac643dbaa2133525c6a4eae8a3e21a9d6184f780 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Mon, 19 Sep 2016 04:19:33 +0200 Subject: [PATCH 205/623] Fix failing test --- test/integ/basic.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/integ/basic.cpp b/test/integ/basic.cpp index 69b6657f..09d4d5bd 100644 --- a/test/integ/basic.cpp +++ b/test/integ/basic.cpp @@ -74,7 +74,7 @@ class header_test_resource : public http_resource { http_response_builder hrb("OK", 200, "text/plain"); hrb.with_header("KEY", "VALUE"); - return http_response(hrb.string_response(); + return hrb.string_response(); } }; From e53d066628af6c6200fe4b792dbddacbaa84862f Mon Sep 17 00:00:00 2001 From: rdiazmartin Date: Mon, 7 Nov 2016 15:50:39 +0100 Subject: [PATCH 206/623] fixed a problem that causes a memory leak. We are passing user, pass, and digested_user as char pointers by value which causes the original pointers be always null when they are going to be free in complete_request, now we pass them by reference and everything works fine --- src/httpserver/webserver.hpp | 2 +- src/webserver.cpp | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/httpserver/webserver.hpp b/src/httpserver/webserver.hpp index 9a8e172f..0ad61f7a 100644 --- a/src/httpserver/webserver.hpp +++ b/src/httpserver/webserver.hpp @@ -328,7 +328,7 @@ class webserver void end_request_construction(MHD_Connection* connection, struct details::modded_request* mr, const char* version, - const char* method, char* user, char* pass, char* digested_user + const char* method, char* &user, char* &pass, char* &digested_user ); int finalize_answer(MHD_Connection* connection, diff --git a/src/webserver.cpp b/src/webserver.cpp index d1a3c930..282f6db3 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -964,9 +964,9 @@ void webserver::end_request_construction( struct details::modded_request* mr, const char* version, const char* method, - char* user, - char* pass, - char* digested_user + char* &user, + char* &pass, + char* &digested_user ) { mr->ws = this; From 85de047ca91f73aadeea59176bbed20714a5f008 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Thu, 17 Nov 2016 23:57:33 +0000 Subject: [PATCH 207/623] Removed debian and redhat scripts. Not able to support it here anymore. Better to be supported outside of the lib --- debian/changelog.in | 149 ---------------------------- debian/control.in | 43 -------- debian/copyright.in | 33 ------ debian/libhttpserver-dev.install.in | 4 - debian/libhttpserver.install.in | 1 - debian/rules.in | 22 ---- redhat/libhttpserver.SPEC.in | 56 ----------- 7 files changed, 308 deletions(-) delete mode 100644 debian/changelog.in delete mode 100644 debian/control.in delete mode 100644 debian/copyright.in delete mode 100644 debian/libhttpserver-dev.install.in delete mode 100644 debian/libhttpserver.install.in delete mode 100755 debian/rules.in delete mode 100644 redhat/libhttpserver.SPEC.in diff --git a/debian/changelog.in b/debian/changelog.in deleted file mode 100644 index 1e4b2480..00000000 --- a/debian/changelog.in +++ /dev/null @@ -1,149 +0,0 @@ -libhttpserver (0.11.1) unstable; urgency=low - * Fixed problems with large payloads - * Fixed memory leak in http_response_ptr - - -- Sebastiano Merlino Wed, 13 Jul 2016 02:23:11 +0100 - -libhttpserver (0.11.0) unstable; urgency=low - * Removed support for event supplier (badly defined, complicated and almost useless) - * Eliminated custom selection logic (simplified overall code in webserver.cpp) - * Changed comet to use a lock-free implementation - - -- Sebastiano Merlino Sat, 29 Dec 2015 18:56:31 +0100 - -libhttpserver (0.10.1) unstable; urgency=low - * Removed POLL start configuration (THREAD now defaults to POLL or EPOLL on Linux) - * Use TCP_FASTOPEN on linux >= 3.6 - - -- Sebastiano Merlino Sat, 27 Dec 2015 19:39:01 +0100 - -libhttpserver (0.10.0) unstable; urgency=low - * Changed http_resource to use classic C++ polymorphism using virtual instead of CRTP - - -- Sebastiano Merlino Sat, 26 Dec 2015 15:08:22 +0100 - -libhttpserver (0.9.1) unstable; urgency=low - * Eliminated build dependency on pkg-config - - -- Sebastiano Merlino Fri, 17 Jul 2015 21:38:54 +0000 - -libhttpserver (0.9.0) unstable; urgency=low - * Support build on MacOsX - * Improved support for CI on travis - * Solved bug on event_supplier registering - * Solved bug on standardize_url to avoid removing root - * Change cycle_callback_ptr so that buffer can be modified - * Moved to version 0.9.0 - - -- Sebastiano Merlino Wed, 15 Apr 2015 01:40:11 +0000 - -libhttpserver (0.8.0) unstable; urgency=low - * Support for building on MinGW/Cygwin systems - * min libmicrohttpd version moved to 0.9.37 - * Moved to version 0.8.0 - - -- Sebastiano Merlino Sat, 23 Jul 2014 02:46:20 +0100 - -libhttpserver (0.7.2) unstable; urgency=low - * Documentation updates - * Reduced responsibilities of webserver class - - -- Sebastiano Merlino Sat, 23 Mar 2014 15:23:40 +0100 - -libhttpserver (0.7.1) unstable; urgency=low - * Improved methods constness - - -- Sebastiano Merlino Sat, 22 Feb 2014 10:58:02 +0100 - -libhttpserver (0.7.0) unstable; urgency=low - * Cleaned-up webserver.cpp code to extract secondary classes - * Enforced immutability of webserver class - * Enabled library to compile on g++ 4.1.2 - - -- Sebastiano Merlino Sat, 25 Jan 2014 16:31:03 +0100 - -libhttpserver (0.6.3) unstable; urgency=low - * Fixed some bugs in exception management - - -- Sebastiano Merlino Wed, 28 Nov 2012 10:17:03 +0100 - -libhttpserver (0.6.2) unstable; urgency=low - * Cookie management in http_response.hpp; - - -- Sebastiano Merlino Fri, 16 Nov 2012 13:01:23 +0100 - -libhttpserver (0.6.1) unstable; urgency=low - * Solved problems in cache management; - - -- Sebastiano Merlino Wed, 08 Nov 2012 17:25:55 +0100 - -libhttpserver (0.6.0) unstable; urgency=low - * Added support for caching; - - -- Sebastiano Merlino Wed, 06 Nov 2012 17:05:23 +0100 - -libhttpserver (0.5.5) unstable; urgency=low - * Added a parameter to avoid http_response deletion by WS; - - -- Sebastiano Merlino Wed, 31 Oct 2012 18:02:40 +0100 - -libhttpserver (0.5.4) unstable; urgency=low - * Solved some problems with debian package building - - -- Sebastiano Merlino Tue, 30 Oct 2012 16:12:00 +0100 - -libhttpserver (0.5.3) unstable; urgency=low - * Added grow method to http_response - - -- Sebastiano Merlino Tue, 30 Oct 2012 12:49:42 +0100 - -libhttpserver (0.5.2) unstable; urgency=low - * Changed default log behaviour to print nothing - * Added getters and setters for webserver external components - - -- Sebastiano Merlino Tue, 23 Oct 2012 12:45:31 +0200 - -libhttpserver (0.5.1) unstable; urgency=low - * Stabilized comet support - * Added features to manage requests easily - * Added response constructor with byte initializer - - -- Sebastiano Merlino Tue, 10 Oct 2012 17:18:42 +0200 - -libhttpserver (0.4.0) unstable; urgency=low - * Added support to Comet - - -- Sebastiano Merlino Tue, 25 Sep 2012 11:42:23 +0200 - -libhttpserver (0.3.0) unstable; urgency=low - * Improved performaces - * Added capability to register default error pages - - -- Sebastiano Merlino Sun, 26 Aug 2012 19:00:42 +0200 - -libhttpserver (0.2.0) unstable; urgency=low - - * Deb target now seems to work really - * Rpm target added - - -- Sebastiano Merlino Wed, 25 Jul 2012 17:05:11 +0200 - -libhttpserver (0.1.2) unstable; urgency=low - - * Added deb target to makefile. - * Added cmakemodule in order to help people who want to include the library - in a cmake installation - - -- Sebastiano Merlino Sat, 21 Jul 2012 00:40:05 +0200 - -libhttpserver (0.1.2) unstable; urgency=low - - * First debian release. - - -- Andrea Nicotra Thu, 19 Jul 2012 13:37:44 +0200 - -libhttpserver (0.1.1) unstable; urgency=low - - * First tag. - - -- Sebastiano Merlino Sun, 15 Jul 2012 11:15:44 +0200 diff --git a/debian/control.in b/debian/control.in deleted file mode 100644 index 110d0cc5..00000000 --- a/debian/control.in +++ /dev/null @@ -1,43 +0,0 @@ -Source: libhttpserver -Section: libs -Priority: optional -Maintainer: Sebastiano Merlino -Build-Depends: cdbs -Depends: libmicrohttpd10(>= 0.9.7), libstdc++6 -Standards-Version: @VERSION@ -Vcs-Git: git://github.com/etr/libhttpserver.git -Vcs-browser: https://github.com/etr/libhttpserver.git -Homepage: https://github.com/etr/libhttpserver - -Package: libhttpserver -Architecture: any -Installed-Size: -Depends: ${misc:Depends}, ${shlibs:Depends} -Description: - library embedding RESTful HTTP server functionality - libhttpserver is a small C++ library for embedding RESTful HTTP server functionality into - applications. - . - This package includes libraries. - -Package: libhttpserver-dbg -Section: debug -Architecture: any -Installed-Size: -Depends: ${misc:Depends}, ${shlibs:Depends}, libmicrohttpd-dbg(>= 0.9.7), libstdc++6 -Description: - library embedding RESTful HTTP server functionality (debug) - libhttpserver is a small C++ library for embedding RESTful HTTP server functionality into - applications. - . - This package contains the debugging symbols. - -Package: libhttpserver-dev -Section: libdevel -Architecture: any -Installed-Size: -Depends: ${misc:Depends}, ${shlibs:Depends}, libmicrohttpd-dev(>= 0.9.7), libstdc++6 -Description: - The libhttpserver library . - . - This package contains the development files. diff --git a/debian/copyright.in b/debian/copyright.in deleted file mode 100644 index 89a1f7a3..00000000 --- a/debian/copyright.in +++ /dev/null @@ -1,33 +0,0 @@ -Files: * -Copyright: (C) 2011-2015 Sebastiano Merlino -License: LGPL-2.1+ - -Files: doc/* -Copyright: (C) 2011-2015 Sebastiano Merlino - -License: GFDL-1.3+ - Permission is granted to copy, distribute and/or modify this document - under the terms of the GNU Free Documentation License, Version 1.3 - or any later version published by the Free Software Foundation; - with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. - . - The complete text of the GNU Free Documentation License - can be found in /usr/share/common-licenses/GFDL-1.3 file. - -License: LGPL-2.1+ - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 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 - Lesser General Public License for more details. - . - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - . - The complete text of the GNU Library General Public License - can be found in /usr/share/common-licenses/LGPL-2.1 file. diff --git a/debian/libhttpserver-dev.install.in b/debian/libhttpserver-dev.install.in deleted file mode 100644 index 5af7a46f..00000000 --- a/debian/libhttpserver-dev.install.in +++ /dev/null @@ -1,4 +0,0 @@ -usr/include -usr/lib/*/*.a -usr/lib/*/*.so -usr/lib/*/pkgconfig \ No newline at end of file diff --git a/debian/libhttpserver.install.in b/debian/libhttpserver.install.in deleted file mode 100644 index d20df8ab..00000000 --- a/debian/libhttpserver.install.in +++ /dev/null @@ -1 +0,0 @@ -usr/lib/*/*.so.* diff --git a/debian/rules.in b/debian/rules.in deleted file mode 100755 index 78981fe3..00000000 --- a/debian/rules.in +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/make -f - -# Uncomment this to turn on verbose mode. -#export DH_VERBOSE=1 - -include /usr/share/cdbs/1/rules/debhelper.mk - -DEB_CONFIGURE_EXTRA_FLAGS += --libdir=\$${prefix}/lib/$(DEB_HOST_MULTIARCH) -CURDIR := -DEB_SRCDIR := @abs_top_srcdir@ -DEB_DESTDIR := @abs_top_builddir@/debian/tmp -DEB_DH_INSTALL_SOURCEDIR := @abs_top_builddir@/debian/tmp -DEB_BUILDDIR := @abs_top_builddir@/deb_build - -# compile jobs -MAKE += -j1 - -include /usr/share/cdbs/1/class/autotools.mk - -#makebuilddir/libhttpserver:: -# autoreconf --install - diff --git a/redhat/libhttpserver.SPEC.in b/redhat/libhttpserver.SPEC.in deleted file mode 100644 index e06c5139..00000000 --- a/redhat/libhttpserver.SPEC.in +++ /dev/null @@ -1,56 +0,0 @@ -%define _topdir @abs_builddir@ -%define name libhttpserver -%define version @VERSION@ -%define buildroot %{_topdir}/%{name}-%{version}-root - -Name: %{name} -Release: 1 -Version: %{version} -Summary: library embedding RESTful HTTP server functionality -Group: Development/Libraries -License: LGPL 2.1 -URL: https://github.com/etr/libhttpserver -Source: @abs_builddir@/%{name}-%{version}.tar.gz -Vendor: Zencoders -Prefix: /usr -BuildRoot: %{buildroot} -Packager: Sebastiano Merlino -BuildRequires: libmicrohttpd >= 0.9.7 -Requires: libmicrohttpd >= 0.9.7 -%description -libhttpserver is a small C++ library for embedding RESTful HTTP server functionality into applications. - -%package devel -Summary: Development files -BuildRequires: libmicrohttpd >= 0.9.7, libmicrohttpd-devel >= 0.9.7 -Group: Development/Libraries -Requires: libmicrohttpd >= 0.9.7, libmicrohttpd-devel >= 0.9.7 -%description devel -libhttpserver is a small C++ library for embedding RESTful HTTP server functionality into applications. -This package contains development files and headers. - - -%prep -%setup - -%build -mkdir build -cd build -../configure --prefix=$RPM_BUILD_ROOT/usr -make - -%install -cd build -make install -rm $RPM_BUILD_ROOT/usr/lib/libhttpserver.la - -%files -%defattr(-,root,root) -/usr/lib/*.so.* - -%files devel -%defattr(-,root,root) -/usr/include -/usr/lib/*.a -/usr/lib/*.so -/usr/lib/pkgconfig From cd2752a1852cbb4014fb724d0334a8396f947bbb Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Fri, 18 Nov 2016 00:05:32 +0000 Subject: [PATCH 208/623] Removed debian and redhat scripts. Not able to support it here anymore. Better to be supported outside of the lib --- Makefile.am | 21 +++------------------ configure.ac | 7 ------- 2 files changed, 3 insertions(+), 25 deletions(-) diff --git a/Makefile.am b/Makefile.am index 9968f6ea..4deb3bf8 100644 --- a/Makefile.am +++ b/Makefile.am @@ -26,10 +26,10 @@ ACLOCAL_AMFLAGS = -I m4 SUBDIRS = src test examples DIST_SUBDIRS = src test examples -EXTRA_DIST = libhttpserver.pc.in debian/changelog.in debian/control.in debian/copyright.in debian/rules.in debian/libhttpserver-dev.install.in debian/libhttpserver.install.in redhat/libhttpserver.SPEC.in $(DX_CONFIG) +EXTRA_DIST = libhttpserver.pc.in $(DX_CONFIG) -MOSTLYCLEANFILES = $(DX_CLEANFILES) redhat/SOURCES/* *.gcda *.gcno *.gcov -DISTCLEANFILES = redhat/SOURCES/* redhat/SPEC/* redhat/RPMS/* redhat/SRPMS/* redhat/* debian/* DIST_REVISION +MOSTLYCLEANFILES = $(DX_CLEANFILES) *.gcda *.gcno *.gcov +DISTCLEANFILES = DIST_REVISION pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = libhttpserver.pc @@ -47,18 +47,3 @@ dist-hook: date >DIST_REVISION git branch -vv >>DIST_REVISION cp DIST_REVISION $(distdir)/ - -deb: - debuild -i -us -uc -b - mv ../libhttpserver_@VERSION@_* . - mv ../libhttpserver-dev_@VERSION@_* . - mv ../libhttpserver-dbg_@VERSION@_* . - -rpm: dist - mkdir -p redhat/SOURCES - mkdir -p redhat/BUILD - mkdir -p redhat/RPMS - mkdir -p redhat/SRPMS - cp libhttpserver-@VERSION@.tar.gz redhat/SOURCES - rpmbuild -v -bb redhat/libhttpserver.SPEC - rpmbuild -v -ba redhat/libhttpserver.SPEC diff --git a/configure.ac b/configure.ac index 87a52096..6d671df3 100644 --- a/configure.ac +++ b/configure.ac @@ -214,13 +214,6 @@ AC_OUTPUT( src/Makefile test/Makefile examples/Makefile - debian/changelog - debian/copyright - debian/control - debian/libhttpserver-dev.install - debian/libhttpserver.install - debian/rules - redhat/libhttpserver.SPEC ) AC_MSG_NOTICE([Configuration Summary: From aa65438525d2557506eb497000f4b8f50f1c44a4 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 11 Dec 2016 00:53:50 +0000 Subject: [PATCH 209/623] Force address in a separate option --- src/webserver.cpp | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/src/webserver.cpp b/src/webserver.cpp index 51b969ec..76023160 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -289,8 +289,8 @@ bool webserver::start(bool blocking) this) ); iov.push_back(gen(MHD_OPTION_CONNECTION_TIMEOUT, connection_timeout)); - if(bind_address != 0x0) - iov.push_back(gen(MHD_OPTION_SOCK_ADDR, (intptr_t) bind_address)); + //if(bind_address != 0x0) + // iov.push_back(gen(MHD_OPTION_SOCK_ADDR, (intptr_t) bind_address)); if(bind_socket != 0) iov.push_back(gen(MHD_OPTION_LISTEN_SOCKET, bind_socket)); if(start_method == http_utils::THREAD_PER_CONNECTION && max_threads != 0) @@ -361,12 +361,23 @@ bool webserver::start(bool blocking) start_conf |= MHD_USE_TCP_FASTOPEN; #endif - struct MHD_Daemon* daemon = MHD_start_daemon - ( - start_conf, this->port, &policy_callback, this, - &answer_to_connection, this, MHD_OPTION_ARRAY, - &iov[0], MHD_OPTION_END - ); + struct MHD_Daemon* daemon = NULL; + if(bind_address == 0x0) { + daemon = MHD_start_daemon + ( + start_conf, this->port, &policy_callback, this, + &answer_to_connection, this, MHD_OPTION_ARRAY, + &iov[0], MHD_OPTION_END + ); + } else { + daemon = MHD_start_daemon + ( + start_conf, 1, &policy_callback, this, + &answer_to_connection, this, MHD_OPTION_ARRAY, + &iov[0], MHD_OPTION_SOCK_ADDR, bind_address, MHD_OPTION_END + ); + } + if(NULL == daemon) { cout << gettext("Unable to connect daemon to port: ") << From bd087724076e7575218a6f7fc32eabd8f858c02e Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Thu, 29 Dec 2016 01:27:19 +0000 Subject: [PATCH 210/623] Add option for static linking --- .travis.yml | 5 ++++- configure.ac | 13 +++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 5d88ec6b..82f8cee9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,6 +5,7 @@ compiler: env: - DEBUG="debug" - DEBUG="nodebug" + - LINKING="static" before_install: - sudo apt-get install info install-info - sudo pip install cpp-coveralls @@ -20,7 +21,7 @@ script: - ./bootstrap - mkdir build - cd build - - if [ $DEBUG = "debug" ]; then ../configure --enable-debug --disable-shared --disable-fastopen; else ../configure --disable-fastopen; fi + - if [ $LINKING = "static" ]; then ../configure --enable-static --disable-fastopen; elif [ $DEBUG = "debug" ]; then ../configure --enable-debug --disable-shared --disable-fastopen; else ../configure --disable-fastopen; fi - make - make check after_success: @@ -29,3 +30,5 @@ matrix: exclude: - compiler: clang env: DEBUG="debug" + - compiler: clang + env: LINKING='static' diff --git a/configure.ac b/configure.ac index 6d671df3..9a3da0ea 100644 --- a/configure.ac +++ b/configure.ac @@ -133,6 +133,18 @@ if test x"$fastopen" = x"yes"; then fi fi +AC_MSG_CHECKING([whether to link statically]) +AC_ARG_ENABLE([static], + [AS_HELP_STRING([--enable-static], + [enable use static linking (def=yes)])], + [static="$enableval"], + [static=yes]) +AC_MSG_RESULT([$static]) + +if test x"$static" = x"$yes"; then + LDFLAGS="-static $LDFLAGS"; +fi + m4_pattern_allow([AC_TYPE_SIZE_T]) m4_pattern_allow([AC_TYPE_UINT16_T]) m4_pattern_allow([AC_TYPE_UINT32_T]) @@ -223,4 +235,5 @@ AC_MSG_NOTICE([Configuration Summary: Debug : ${debugit} TLS Enabled : ${have_gnutls} TCP_FASTOPEN : ${is_fastopen_supported} + Static : ${static} ]) From b6d0976694c41b5fa3d10587509e8e3ec4f00080 Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Sun, 12 Feb 2017 14:15:20 +0000 Subject: [PATCH 211/623] Update to libmicrohttpd 0.9.52 --- ChangeLog | 3 +++ configure.ac | 12 ++++++------ src/http_utils.cpp | 3 +-- src/httpserver/http_utils.hpp | 2 +- 4 files changed, 11 insertions(+), 9 deletions(-) diff --git a/ChangeLog b/ChangeLog index f4bb0e92..42e7a040 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,6 @@ +Wed Feb 12 13:14:01 2017 +0000 + Updated to libmicrohttpd 0.9.52 + Wed Jul 13 02:23:11 2016 +0100 Fixed problems with large payloads Fixed memory leak in http_response_ptr diff --git a/configure.ac b/configure.ac index 9a3da0ea..5e59348d 100644 --- a/configure.ac +++ b/configure.ac @@ -21,8 +21,8 @@ AC_PREREQ(2.57) m4_define([libhttpserver_MAJOR_VERSION],[0])dnl -m4_define([libhttpserver_MINOR_VERSION],[11])dnl -m4_define([libhttpserver_REVISION],[1])dnl +m4_define([libhttpserver_MINOR_VERSION],[12])dnl +m4_define([libhttpserver_REVISION],[0])dnl m4_define([libhttpserver_PKG_VERSION],[libhttpserver_MAJOR_VERSION.libhttpserver_MINOR_VERSION.libhttpserver_REVISION])dnl m4_define([libhttpserver_LDF_VERSION],[libhttpserver_MAJOR_VERSION:libhttpserver_MINOR_VERSION:libhttpserver_REVISION])dnl AC_INIT([libhttpserver], libhttpserver_PKG_VERSION, [electrictwister2000@gmail.com]) @@ -92,17 +92,17 @@ AC_CHECK_HEADER([gnutls/gnutls.h],[have_gnutls="yes"],[AC_MSG_WARN("gnutls/gnutl # Checks for libmicrohttpd AC_CHECK_HEADER([microhttpd.h], AC_CHECK_LIB([microhttpd], [MHD_get_fdset2], - [AC_MSG_CHECKING([for libmicrohttpd >= 0.9.37]) + [AC_MSG_CHECKING([for libmicrohttpd >= 0.9.52]) AC_COMPILE_IFELSE( [AC_LANG_SOURCE([ #include - #if (MHD_VERSION < 0x0093700) - #error needs at least version 0.9.37 + #if (MHD_VERSION < 0x00095102) + #error needs at least version 0.9.52 #endif int main () { return 0; } ])], [], - [AC_MSG_ERROR("libmicrohttpd is too old - install libmicrohttpd >= 0.9.37")] + [AC_MSG_ERROR("libmicrohttpd is too old - install libmicrohttpd >= 0.9.52")] ) ], [AC_MSG_ERROR(["libmicrohttpd not found"])] diff --git a/src/http_utils.cpp b/src/http_utils.cpp index fec42643..d5cd8ef9 100644 --- a/src/http_utils.cpp +++ b/src/http_utils.cpp @@ -79,8 +79,7 @@ const int http_utils::http_payment_required = MHD_HTTP_PAYMENT_REQUIRED; const int http_utils::http_forbidden = MHD_HTTP_FORBIDDEN; const int http_utils::http_not_found = MHD_HTTP_NOT_FOUND; const int http_utils::http_method_not_allowed = MHD_HTTP_METHOD_NOT_ALLOWED; -const int http_utils::http_method_not_acceptable = - MHD_HTTP_METHOD_NOT_ACCEPTABLE; +const int http_utils::http_method_not_acceptable = MHD_HTTP_NOT_ACCEPTABLE; const int http_utils::http_proxy_authentication_required = MHD_HTTP_PROXY_AUTHENTICATION_REQUIRED; const int http_utils::http_request_timeout = MHD_HTTP_REQUEST_TIMEOUT; diff --git a/src/httpserver/http_utils.hpp b/src/httpserver/http_utils.hpp index a6fabf56..f8a6b8c5 100644 --- a/src/httpserver/http_utils.hpp +++ b/src/httpserver/http_utils.hpp @@ -79,7 +79,7 @@ class http_utils #if defined(__MINGW32__) || defined(__CYGWIN32__) INTERNAL_SELECT = MHD_USE_SELECT_INTERNALLY | MHD_USE_POLL, #else - INTERNAL_SELECT = MHD_USE_SELECT_INTERNALLY | MHD_USE_EPOLL_LINUX_ONLY | MHD_USE_EPOLL_TURBO, + INTERNAL_SELECT = MHD_USE_SELECT_INTERNALLY | MHD_USE_EPOLL | MHD_USE_EPOLL_TURBO, #endif THREAD_PER_CONNECTION = MHD_USE_THREAD_PER_CONNECTION | MHD_USE_POLL }; From c5405d2e24be87cad178953210fd5c0b6dd58369 Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Sun, 12 Feb 2017 14:16:26 +0000 Subject: [PATCH 212/623] Update travis configuration file to match code requirements --- .travis.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 82f8cee9..4020403d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,9 +10,9 @@ before_install: - sudo apt-get install info install-info - sudo pip install cpp-coveralls - sudo pip install gcovr - - wget ftp://ftp.gnu.org/gnu/libmicrohttpd/libmicrohttpd-0.9.37.tar.gz - - tar -xvzf libmicrohttpd-0.9.37.tar.gz - - cd libmicrohttpd-0.9.37 + - wget ftp://ftp.gnu.org/gnu/libmicrohttpd/libmicrohttpd-0.9.52.tar.gz + - tar -xvzf libmicrohttpd-0.9.52.tar.gz + - cd libmicrohttpd-0.9.52 - ./configure --prefix=/usr - make - sudo make install From 4a632f140bb6d95b694b0121b06fc5c483a181d3 Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Sun, 26 Feb 2017 21:29:16 +0000 Subject: [PATCH 213/623] Fixed problem with segfault on copy of http_response object --- src/http_response.cpp | 5 ++++- src/httpserver/http_response.hpp | 17 +++++++++++++---- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/http_response.cpp b/src/http_response.cpp index f80cd08a..6bae7995 100644 --- a/src/http_response.cpp +++ b/src/http_response.cpp @@ -60,7 +60,10 @@ http_response::http_response(const http_response_builder& builder): enqueue_response(this, builder._enqueue_response), completed(false), ws(0x0), - connection_id(0x0) + connection_id(0x0), + _get_raw_response(builder._get_raw_response), + _decorate_response(builder._decorate_response), + _enqueue_response(builder._enqueue_response) { } diff --git a/src/httpserver/http_response.hpp b/src/httpserver/http_response.hpp index 6fa68693..c3807a7c 100644 --- a/src/httpserver/http_response.hpp +++ b/src/httpserver/http_response.hpp @@ -30,6 +30,7 @@ #include #include #include +#include #include "httpserver/binders.hpp" #include "httpserver/http_utils.hpp" @@ -91,12 +92,15 @@ class http_response underlying_connection(b.underlying_connection), ce(b.ce), cycle_callback(b.cycle_callback), - get_raw_response(b.get_raw_response), - decorate_response(b.decorate_response), - enqueue_response(b.enqueue_response), + get_raw_response(this, b._get_raw_response), + decorate_response(this, b._decorate_response), + enqueue_response(this, b._enqueue_response), completed(b.completed), ws(b.ws), - connection_id(b.connection_id) + connection_id(b.connection_id), + _get_raw_response(b._get_raw_response), + _decorate_response(b._decorate_response), + _enqueue_response(b._enqueue_response) { } @@ -298,6 +302,11 @@ class http_response http_response& operator=(const http_response& b); static ssize_t data_generator (void* cls, uint64_t pos, char* buf, size_t max); + + void (http_response::*_get_raw_response)(MHD_Response**, webserver*); + void (http_response::*_decorate_response)(MHD_Response*); + int (http_response::*_enqueue_response)(MHD_Connection*, MHD_Response*); + }; std::ostream &operator<< (std::ostream &os, const http_response &r); From 4895f43ed29195af70beb47bcfd1ef3ab4555665 Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Sun, 26 Feb 2017 21:31:29 +0000 Subject: [PATCH 214/623] Upgraded version --- ChangeLog | 3 +++ configure.ac | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index 42e7a040..be5666bc 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,6 @@ +Wed Feb 26 21:31:00 2017 +0000 + Fixed problem with segfault when copying http_response object + Wed Feb 12 13:14:01 2017 +0000 Updated to libmicrohttpd 0.9.52 diff --git a/configure.ac b/configure.ac index 5e59348d..0f916544 100644 --- a/configure.ac +++ b/configure.ac @@ -21,7 +21,7 @@ AC_PREREQ(2.57) m4_define([libhttpserver_MAJOR_VERSION],[0])dnl -m4_define([libhttpserver_MINOR_VERSION],[12])dnl +m4_define([libhttpserver_MINOR_VERSION],[13])dnl m4_define([libhttpserver_REVISION],[0])dnl m4_define([libhttpserver_PKG_VERSION],[libhttpserver_MAJOR_VERSION.libhttpserver_MINOR_VERSION.libhttpserver_REVISION])dnl m4_define([libhttpserver_LDF_VERSION],[libhttpserver_MAJOR_VERSION:libhttpserver_MINOR_VERSION:libhttpserver_REVISION])dnl From c535126a20876a95866ad580ee024f26403c4073 Mon Sep 17 00:00:00 2001 From: "G. Mercat" Date: Tue, 7 Nov 2017 18:37:02 +0100 Subject: [PATCH 215/623] Manage ipv6 in get_ip_str function sockaddr_in (IPv4) is the same size as sockaddr, which is why getnameinfo() is working for IPv4. But sockaddr_in6 (IPv6) is larger than sockaddr, which is why getnameinfo() fails. --- src/http_utils.cpp | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/http_utils.cpp b/src/http_utils.cpp index d5cd8ef9..9bbbe602 100644 --- a/src/http_utils.cpp +++ b/src/http_utils.cpp @@ -244,9 +244,21 @@ void get_ip_str( { if(sa) { + int addrlen = sizeof(sockaddr_in); + if (AF_INET6 == sa->sa_family) + { + addrlen = sizeof(sockaddr_in6); + } + char to_ret[NI_MAXHOST]; - getnameinfo(sa, sizeof (struct sockaddr), to_ret, NI_MAXHOST, NULL, 0, NI_NUMERICHOST); - result = to_ret; + if (0 == getnameinfo(sa, addrlen, to_ret, NI_MAXHOST, NULL, 0, NI_NUMERICHOST)) + { + result = to_ret; + } + else + { + result.clear(); + } } } From bd39cb40940c115ccd571e7c467fec5f133d6e51 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Tue, 7 Nov 2017 22:36:52 +0000 Subject: [PATCH 216/623] Avoid tests getting stack on CONNECT requests (ongoing investigation on this issue) --- test/integ/basic.cpp | 41 ++++++++++++++++++++++++++--------------- 1 file changed, 26 insertions(+), 15 deletions(-) diff --git a/test/integ/basic.cpp b/test/integ/basic.cpp index 09d4d5bd..61307281 100644 --- a/test/integ/basic.cpp +++ b/test/integ/basic.cpp @@ -191,7 +191,6 @@ LT_BEGIN_AUTO_TEST(basic_suite, two_endpoints) LT_CHECK_EQ(t, "NOK"); curl_easy_cleanup(curl); } - LT_END_AUTO_TEST(two_endpoints) LT_BEGIN_AUTO_TEST(basic_suite, read_body) @@ -253,46 +252,55 @@ LT_BEGIN_AUTO_TEST(basic_suite, complete) complete_test_resource* resource = new complete_test_resource(); ws->register_resource("base", resource); curl_global_init(CURL_GLOBAL_ALL); - std::string s; - CURL* curl; - CURLcode res; - curl = curl_easy_init(); + { + CURL* curl = curl_easy_init(); curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); - res = curl_easy_perform(curl); + CURLcode res = curl_easy_perform(curl); LT_ASSERT_EQ(res, 0); curl_easy_cleanup(curl); + } - curl = curl_easy_init(); + { + CURL* curl = curl_easy_init(); curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "DELETE"); - res = curl_easy_perform(curl); + CURLcode res = curl_easy_perform(curl); LT_ASSERT_EQ(res, 0); curl_easy_cleanup(curl); + } - curl = curl_easy_init(); + { + CURL* curl = curl_easy_init(); curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PUT"); - res = curl_easy_perform(curl); + CURLcode res = curl_easy_perform(curl); LT_ASSERT_EQ(res, 0); curl_easy_cleanup(curl); + } - curl = curl_easy_init(); +/* + { + CURL* curl = curl_easy_init(); curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "CONNECT"); - res = curl_easy_perform(curl); + CURLcode res = curl_easy_perform(curl); LT_ASSERT_EQ(res, 0); curl_easy_cleanup(curl); + } +*/ - curl = curl_easy_init(); + { + CURL* curl = curl_easy_init(); curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); curl_easy_setopt(curl, CURLOPT_POST, 1L); curl_easy_setopt(curl, CURLOPT_POSTFIELDS, NULL); curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, 0); - res = curl_easy_perform(curl); + CURLcode res = curl_easy_perform(curl); LT_ASSERT_EQ(res, 0); curl_easy_cleanup(curl); + } LT_END_AUTO_TEST(complete) LT_BEGIN_AUTO_TEST(basic_suite, only_render) @@ -336,6 +344,7 @@ LT_BEGIN_AUTO_TEST(basic_suite, only_render) LT_CHECK_EQ(s, "OK"); curl_easy_cleanup(curl); +/* s = ""; curl = curl_easy_init(); curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); @@ -346,6 +355,7 @@ LT_BEGIN_AUTO_TEST(basic_suite, only_render) LT_ASSERT_EQ(res, 0); LT_CHECK_EQ(s, "OK"); curl_easy_cleanup(curl); +*/ s = ""; curl = curl_easy_init(); @@ -370,7 +380,6 @@ LT_BEGIN_AUTO_TEST(basic_suite, only_render) LT_ASSERT_EQ(res, 0); LT_CHECK_EQ(s, "OK"); curl_easy_cleanup(curl); - LT_END_AUTO_TEST(only_render) LT_BEGIN_AUTO_TEST(basic_suite, postprocessor) @@ -404,6 +413,7 @@ LT_BEGIN_AUTO_TEST(basic_suite, empty_arg) curl_easy_cleanup(curl); LT_END_AUTO_TEST(empty_arg) +/* LT_BEGIN_AUTO_TEST(basic_suite, no_response) no_response_resource* resource = new no_response_resource(); ws->register_resource("base", resource); @@ -422,6 +432,7 @@ LT_BEGIN_AUTO_TEST(basic_suite, no_response) LT_ASSERT_EQ(http_code, 500); curl_easy_cleanup(curl); LT_END_AUTO_TEST(no_response) +*/ LT_BEGIN_AUTO_TEST_ENV() AUTORUN_TESTS() From 5281246df41558cfcaffeae8d9142d5b16a464b8 Mon Sep 17 00:00:00 2001 From: "G. Mercat" Date: Thu, 9 Nov 2017 11:28:55 +0100 Subject: [PATCH 217/623] Remove clear on returned value --- src/http_utils.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/http_utils.cpp b/src/http_utils.cpp index 9bbbe602..25509e89 100644 --- a/src/http_utils.cpp +++ b/src/http_utils.cpp @@ -255,10 +255,6 @@ void get_ip_str( { result = to_ret; } - else - { - result.clear(); - } } } From 9c5bbdb037856903cf3fae439711e16db4f8934f Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 4 Mar 2018 17:56:51 +0000 Subject: [PATCH 218/623] Changing requestor_port to be represented with an unsigned short rather than a short --- src/http_utils.cpp | 2 +- src/httpserver/http_request.hpp | 6 +++--- src/httpserver/http_utils.hpp | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/http_utils.cpp b/src/http_utils.cpp index 25509e89..1eb29ed1 100644 --- a/src/http_utils.cpp +++ b/src/http_utils.cpp @@ -268,7 +268,7 @@ std::string get_ip_str_new( return to_ret; } -short get_port(const struct sockaddr* sa) +unsigned short get_port(const struct sockaddr* sa) { if(sa) { diff --git a/src/httpserver/http_request.hpp b/src/httpserver/http_request.hpp index e145f5cd..83ae85aa 100644 --- a/src/httpserver/http_request.hpp +++ b/src/httpserver/http_request.hpp @@ -358,7 +358,7 @@ class http_request * Method used to get the requestor port used. * @return the requestor port **/ - short get_requestor_port() const + unsigned short get_requestor_port() const { return this->requestor_port; } @@ -415,7 +415,7 @@ class http_request std::string version; std::string requestor; - short requestor_port; + unsigned short requestor_port; struct MHD_Connection* underlying_connection; void set_underlying_connection(struct MHD_Connection* conn) @@ -537,7 +537,7 @@ class http_request * Method used to set the requestor port * @param requestor The requestor port to set **/ - void set_requestor_port(short requestor_port) + void set_requestor_port(unsigned short requestor_port) { this->requestor_port = requestor_port; } diff --git a/src/httpserver/http_utils.hpp b/src/httpserver/http_utils.hpp index f8a6b8c5..a1a22068 100644 --- a/src/httpserver/http_utils.hpp +++ b/src/httpserver/http_utils.hpp @@ -79,7 +79,7 @@ class http_utils #if defined(__MINGW32__) || defined(__CYGWIN32__) INTERNAL_SELECT = MHD_USE_SELECT_INTERNALLY | MHD_USE_POLL, #else - INTERNAL_SELECT = MHD_USE_SELECT_INTERNALLY | MHD_USE_EPOLL | MHD_USE_EPOLL_TURBO, + INTERNAL_SELECT = MHD_USE_SELECT_INTERNALLY | MHD_USE_EPOLL | MHD_USE_EPOLL_TURBO, #endif THREAD_PER_CONNECTION = MHD_USE_THREAD_PER_CONNECTION | MHD_USE_POLL }; @@ -331,7 +331,7 @@ std::string get_ip_str_new(const struct sockaddr* sa, * @param sa The sockaddr object to find the port from * @return short representing the port **/ -short get_port(const struct sockaddr* sa); +unsigned short get_port(const struct sockaddr* sa); /** * Method to output the contents of a headers map to a std::ostream From 562a0eb3f67dce8e1236ed5a425831a01fa2fa09 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 12 Aug 2018 18:50:28 -0700 Subject: [PATCH 219/623] Add ko-fi button --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 1bd46b18..12c5cdfb 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ The libhttpserver (0.8.0) reference manual ========================================== [![Build Status](https://travis-ci.org/etr/libhttpserver.png?branch=master)](https://travis-ci.org/etr/libhttpserver) +[![ko-fi](https://www.ko-fi.com/img/donate_sm.png)](https://ko-fi.com/F1F5HY8B) This library has been originally developed under the zencoders flags and this community has always supported me all along this work so I am happy to put the logo on this readme. From b5f5ef9760d7d06ae6d416086c697fbbb711b2a8 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Mon, 5 Nov 2018 03:41:58 +0000 Subject: [PATCH 220/623] Changed http_endpoint to be an inner class of webserver. This avoids the class being publicly accessible in a cleaner way because it avoids using friends as a mechanism to allow instantiation within stl structures --- ChangeLog | 3 ++ configure.ac | 2 +- src/Makefile.am | 4 +- src/{details => }/http_endpoint.cpp | 20 ++++------ src/httpserver.hpp | 2 +- .../{details => }/http_endpoint.hpp | 40 ++++++++----------- src/httpserver/webserver.hpp | 5 ++- src/webserver.cpp | 16 ++++---- 8 files changed, 43 insertions(+), 49 deletions(-) rename src/{details => }/http_endpoint.cpp (89%) rename src/httpserver/{details => }/http_endpoint.hpp (93%) diff --git a/ChangeLog b/ChangeLog index be5666bc..86e8cb2a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,6 @@ +Sun Nov 04 19:28:00 2018 -0800 + Moved http_endpoint as a sub-class of webserver. This avoids usage of friends. + Wed Feb 26 21:31:00 2017 +0000 Fixed problem with segfault when copying http_response object diff --git a/configure.ac b/configure.ac index 0f916544..91ffeed2 100644 --- a/configure.ac +++ b/configure.ac @@ -21,7 +21,7 @@ AC_PREREQ(2.57) m4_define([libhttpserver_MAJOR_VERSION],[0])dnl -m4_define([libhttpserver_MINOR_VERSION],[13])dnl +m4_define([libhttpserver_MINOR_VERSION],[14])dnl m4_define([libhttpserver_REVISION],[0])dnl m4_define([libhttpserver_PKG_VERSION],[libhttpserver_MAJOR_VERSION.libhttpserver_MINOR_VERSION.libhttpserver_REVISION])dnl m4_define([libhttpserver_LDF_VERSION],[libhttpserver_MAJOR_VERSION:libhttpserver_MINOR_VERSION:libhttpserver_REVISION])dnl diff --git a/src/Makefile.am b/src/Makefile.am index 2a57d322..ce693016 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -19,9 +19,9 @@ AM_CPPFLAGS = -I../ -I$(srcdir)/httpserver/ METASOURCES = AUTO lib_LTLIBRARIES = libhttpserver.la -libhttpserver_la_SOURCES = string_utilities.cpp webserver.cpp http_utils.cpp http_request.cpp http_response.cpp http_resource.cpp details/comet_manager.cpp details/http_endpoint.cpp +libhttpserver_la_SOURCES = string_utilities.cpp webserver.cpp http_utils.cpp http_request.cpp http_response.cpp http_resource.cpp details/comet_manager.cpp http_endpoint.cpp noinst_HEADERS = httpserver/string_utilities.hpp httpserver/details/modded_request.hpp httpserver/details/http_response_ptr.hpp httpserver/details/cache_entry.hpp httpserver/details/comet_manager.hpp gettext.h -nobase_include_HEADERS = httpserver.hpp httpserver/create_webserver.hpp httpserver/webserver.hpp httpserver/http_utils.hpp httpserver/details/http_endpoint.hpp httpserver/http_request.hpp httpserver/http_response.hpp httpserver/http_resource.hpp httpserver/binders.hpp httpserver/http_response_builder.hpp +nobase_include_HEADERS = httpserver.hpp httpserver/create_webserver.hpp httpserver/webserver.hpp httpserver/http_utils.hpp httpserver/http_endpoint.hpp httpserver/http_request.hpp httpserver/http_response.hpp httpserver/http_resource.hpp httpserver/binders.hpp httpserver/http_response_builder.hpp AM_CXXFLAGS += -fPIC -Wall diff --git a/src/details/http_endpoint.cpp b/src/http_endpoint.cpp similarity index 89% rename from src/details/http_endpoint.cpp rename to src/http_endpoint.cpp index 84f073cf..f1be0414 100644 --- a/src/details/http_endpoint.cpp +++ b/src/http_endpoint.cpp @@ -18,7 +18,7 @@ USA */ -#include "details/http_endpoint.hpp" +#include "http_endpoint.hpp" #include "http_utils.hpp" #include "string_utilities.hpp" @@ -29,10 +29,7 @@ namespace httpserver using namespace http; -namespace details -{ - -http_endpoint::~http_endpoint() +webserver::http_endpoint::~http_endpoint() { if(reg_compiled) { @@ -40,7 +37,7 @@ http_endpoint::~http_endpoint() } } -http_endpoint::http_endpoint +webserver::http_endpoint::http_endpoint ( const string& url, bool family, @@ -95,7 +92,7 @@ http_endpoint::http_endpoint } if((parts[i].size() < 3) || (parts[i][0] != '{') || (parts[i][parts[i].size() - 1] != '}')) - throw bad_http_endpoint(); + throw webserver::http_endpoint::bad_http_endpoint(); std::string::size_type bar = parts[i].find_first_of('|'); this->url_pars.push_back(parts[i].substr(1, bar != string::npos ? bar - 1 : parts[i].size() - 2)); @@ -118,7 +115,7 @@ http_endpoint::http_endpoint } } -http_endpoint::http_endpoint(const http_endpoint& h): +webserver::http_endpoint::http_endpoint(const webserver::http_endpoint& h): url_complete(h.url_complete), url_modded(h.url_modded), url_pars(h.url_pars), @@ -133,7 +130,7 @@ http_endpoint::http_endpoint(const http_endpoint& h): ); } -http_endpoint& http_endpoint::operator =(const http_endpoint& h) +webserver::http_endpoint& webserver::http_endpoint::operator =(const webserver::http_endpoint& h) { this->url_complete = h.url_complete; this->url_modded = h.url_modded; @@ -149,12 +146,12 @@ http_endpoint& http_endpoint::operator =(const http_endpoint& h) return *this; } -bool http_endpoint::operator <(const http_endpoint& b) const +bool webserver::http_endpoint::operator <(const webserver::http_endpoint& b) const { COMPARATOR(this->url_modded, b.url_modded, std::toupper); } -bool http_endpoint::match(const http_endpoint& url) const +bool webserver::http_endpoint::match(const webserver::http_endpoint& url) const { if(!this->family_url || url.url_pieces.size() < this->url_pieces.size()) @@ -172,4 +169,3 @@ bool http_endpoint::match(const http_endpoint& url) const }; -}; diff --git a/src/httpserver.hpp b/src/httpserver.hpp index 52b71443..7028f68a 100644 --- a/src/httpserver.hpp +++ b/src/httpserver.hpp @@ -24,7 +24,7 @@ #define _HTTPSERVER_HPP_INSIDE_ #include "httpserver/http_utils.hpp" -#include "httpserver/details/http_endpoint.hpp" +#include "httpserver/http_endpoint.hpp" #include "httpserver/http_resource.hpp" #include "httpserver/http_response.hpp" #include "httpserver/http_response_builder.hpp" diff --git a/src/httpserver/details/http_endpoint.hpp b/src/httpserver/http_endpoint.hpp similarity index 93% rename from src/httpserver/details/http_endpoint.hpp rename to src/httpserver/http_endpoint.hpp index f366f7dc..35d13464 100644 --- a/src/httpserver/details/http_endpoint.hpp +++ b/src/httpserver/http_endpoint.hpp @@ -30,6 +30,8 @@ #include #include +#include "webserver.hpp" + namespace httpserver { @@ -42,24 +44,27 @@ namespace details /** * Exception class throwed when a bad formatted http url is used **/ -class bad_http_endpoint : public std::exception -{ - /** - * Method used to see error details - * @return a const char* containing the error message - **/ - virtual const char* what() const throw() - { - return "Bad url format!"; - } + }; /** * Class representing an Http Endpoint. It is an abstraction used by the APIs. **/ -class http_endpoint +class webserver::http_endpoint { - private: + public: + class bad_http_endpoint : public std::exception + { + /** + * Method used to see error details + * @return a const char* containing the error message + **/ + virtual const char* what() const throw() + { + return "Bad url format!"; + } + }; + /** * Copy constructor. It is useful expecially to copy regex_t structure that contains dinamically allocated data. * @param h The http_endpoint to copy @@ -232,17 +237,6 @@ class http_endpoint * Boolean indicating if the regex is compiled **/ bool reg_compiled; - friend class httpserver::webserver; - friend void _register_resource( - webserver*, - const std::string&, - const http_resource&, - bool - ); - template friend struct std::pair; - template friend struct std::less; -}; - }; }; diff --git a/src/httpserver/webserver.hpp b/src/httpserver/webserver.hpp index 1e751fb1..f5b9acf0 100644 --- a/src/httpserver/webserver.hpp +++ b/src/httpserver/webserver.hpp @@ -57,7 +57,6 @@ struct httpserver_ska; }; namespace details { - class http_endpoint; struct daemon_item; struct modded_request; struct cache_entry; @@ -174,6 +173,8 @@ class webserver webserver& operator=(const webserver& other); private: + class http_endpoint; + const uint16_t port; http::http_utils::start_method_T start_method; const int max_threads; @@ -218,7 +219,7 @@ class webserver render_ptr method_not_allowed_resource; render_ptr method_not_acceptable_resource; render_ptr internal_error_resource; - std::map registered_resources; + std::map registered_resources; std::map registered_resources_str; std::map response_cache; diff --git a/src/webserver.cpp b/src/webserver.cpp index 76023160..4603b11a 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -48,7 +48,7 @@ #include "http_response.hpp" #include "http_request.hpp" #include "http_response_builder.hpp" -#include "details/http_endpoint.hpp" +#include "http_endpoint.hpp" #include "string_utilities.hpp" #include "create_webserver.hpp" #include "details/comet_manager.hpp" @@ -224,10 +224,10 @@ void webserver::request_completed ( bool webserver::register_resource(const std::string& resource, http_resource* hrm, bool family) { - details::http_endpoint idx(resource, family, true, regex_checking); + http_endpoint idx(resource, family, true, regex_checking); - pair::iterator, bool> result = registered_resources.insert( - map::value_type(idx, hrm) + pair::iterator, bool> result = registered_resources.insert( + map::value_type(idx, hrm) ); if(result.second) @@ -435,7 +435,7 @@ bool webserver::stop() void webserver::unregister_resource(const string& resource) { - details::http_endpoint he(resource); + http_endpoint he(resource); this->registered_resources.erase(he); this->registered_resources.erase(he.url_complete); } @@ -813,11 +813,11 @@ int webserver::finalize_answer( if(regex_checking) { - map::iterator found_endpoint; + map::iterator found_endpoint; - details::http_endpoint endpoint(st_url, false, false, regex_checking); + http_endpoint endpoint(st_url, false, false, regex_checking); - map::iterator it; + map::iterator it; size_t len = 0; size_t tot_len = 0; From 6ba444665f3ffdb9668a63b4bdd14cbdd1e7fdfc Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 4 Nov 2018 20:02:39 -0800 Subject: [PATCH 221/623] Added options for travis to build on gcc 5,6 and 7 Trying out travis build on gcc 5, 6 and 7. Experimental, possibly needs revert. --- .travis.yml | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/.travis.yml b/.travis.yml index 4020403d..07eadd3a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,6 +17,7 @@ before_install: - make - sudo make install - cd .. + - eval "${MATRIX_EVAL}" script: - ./bootstrap - mkdir build @@ -32,3 +33,31 @@ matrix: env: DEBUG="debug" - compiler: clang env: LINKING='static' + include: + - os: linux + addons: + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - g++-5 + env: + - MATRIX_EVAL="CC=gcc-5 && CXX=g++-5" + - os: linux + addons: + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - g++-6 + env: + - MATRIX_EVAL="CC=gcc-6 && CXX=g++-6" + - os: linux + addons: + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - g++-7 + env: + - MATRIX_EVAL="CC=gcc-7 && CXX=g++-7" From 82617f910b4158f699d476ce381423ad2f062394 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 4 Nov 2018 20:15:21 -0800 Subject: [PATCH 222/623] pulling libmicrohttpd tgz from a private s3 This to avoid spurious failures due to remotely owned ftp --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 07eadd3a..d4fd2594 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,7 +10,7 @@ before_install: - sudo apt-get install info install-info - sudo pip install cpp-coveralls - sudo pip install gcovr - - wget ftp://ftp.gnu.org/gnu/libmicrohttpd/libmicrohttpd-0.9.52.tar.gz + - wget https://s3.amazonaws.com/libhttpserver/libmicrohttpd_releases/libmicrohttpd-0.9.52.tar.gz - tar -xvzf libmicrohttpd-0.9.52.tar.gz - cd libmicrohttpd-0.9.52 - ./configure --prefix=/usr From 4ad9346ebbac0372dc4c2e3b557a727755d0d9bd Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Thu, 22 Nov 2018 16:59:07 -0800 Subject: [PATCH 223/623] Try experimenting with travis tests on OSX --- .travis.yml | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index d4fd2594..0a24593d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,7 @@ language: cpp +os: + - linux + - osx compiler: - gcc - clang @@ -7,10 +10,10 @@ env: - DEBUG="nodebug" - LINKING="static" before_install: - - sudo apt-get install info install-info - - sudo pip install cpp-coveralls - - sudo pip install gcovr - - wget https://s3.amazonaws.com/libhttpserver/libmicrohttpd_releases/libmicrohttpd-0.9.52.tar.gz + - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; sudo apt-get install info install-info; fi + - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; sudo pip install cpp-coveralls; fi + - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; sudo pip install gcovr; fi + - curl https://s3.amazonaws.com/libhttpserver/libmicrohttpd_releases/libmicrohttpd-0.9.52.tar.gz -o libmicrohttpd-0.9.52.tar.gz - tar -xvzf libmicrohttpd-0.9.52.tar.gz - cd libmicrohttpd-0.9.52 - ./configure --prefix=/usr From 9b894163800ddd96b3786db0592089e546431cb2 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Thu, 22 Nov 2018 17:03:07 -0800 Subject: [PATCH 224/623] Fixing script to build on travis-ci --- .travis.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 0a24593d..c3d768cd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,9 +10,9 @@ env: - DEBUG="nodebug" - LINKING="static" before_install: - - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; sudo apt-get install info install-info; fi - - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; sudo pip install cpp-coveralls; fi - - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; sudo pip install gcovr; fi + - if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo apt-get install info install-info; fi + - if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo pip install cpp-coveralls; fi + - if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo pip install gcovr; fi - curl https://s3.amazonaws.com/libhttpserver/libmicrohttpd_releases/libmicrohttpd-0.9.52.tar.gz -o libmicrohttpd-0.9.52.tar.gz - tar -xvzf libmicrohttpd-0.9.52.tar.gz - cd libmicrohttpd-0.9.52 @@ -29,7 +29,7 @@ script: - make - make check after_success: - - if [ $DEBUG = "debug" ]; then ../ci-report-coverage; fi + - if [ $DEBUG = "debug" ] && [ "$TRAVIS_OS_NAME" = "linux" ]; then ../ci-report-coverage; fi matrix: exclude: - compiler: clang From 9c4274c9a956dfcef611646515f69ddb74121b78 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Thu, 22 Nov 2018 17:13:35 -0800 Subject: [PATCH 225/623] Enable compatibility for OSX builds --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index c3d768cd..e7d9c750 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,6 +13,7 @@ before_install: - if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo apt-get install info install-info; fi - if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo pip install cpp-coveralls; fi - if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo pip install gcovr; fi + - if [ "$TRAVIS_OS_NAME" = "osx" ]; then export CFLAGS='-march=pentium4 -mtune=generic -O2'; fi - curl https://s3.amazonaws.com/libhttpserver/libmicrohttpd_releases/libmicrohttpd-0.9.52.tar.gz -o libmicrohttpd-0.9.52.tar.gz - tar -xvzf libmicrohttpd-0.9.52.tar.gz - cd libmicrohttpd-0.9.52 From 13f67b5ec0792d82ca2498f6bc2b0f2c1599e5f9 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Thu, 22 Nov 2018 17:18:58 -0800 Subject: [PATCH 226/623] reduced the set of options for osx --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index e7d9c750..8d2e684e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,7 +13,7 @@ before_install: - if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo apt-get install info install-info; fi - if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo pip install cpp-coveralls; fi - if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo pip install gcovr; fi - - if [ "$TRAVIS_OS_NAME" = "osx" ]; then export CFLAGS='-march=pentium4 -mtune=generic -O2'; fi + - if [ "$TRAVIS_OS_NAME" = "osx" ]; then export CFLAGS='-mtune=generic'; fi - curl https://s3.amazonaws.com/libhttpserver/libmicrohttpd_releases/libmicrohttpd-0.9.52.tar.gz -o libmicrohttpd-0.9.52.tar.gz - tar -xvzf libmicrohttpd-0.9.52.tar.gz - cd libmicrohttpd-0.9.52 From a08d90ccf284dc568af9fe0f2ec121ea30ec3617 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Thu, 22 Nov 2018 17:25:20 -0800 Subject: [PATCH 227/623] Disable building examples in libmicrohttpd --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 8d2e684e..0207d7bc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,7 +17,7 @@ before_install: - curl https://s3.amazonaws.com/libhttpserver/libmicrohttpd_releases/libmicrohttpd-0.9.52.tar.gz -o libmicrohttpd-0.9.52.tar.gz - tar -xvzf libmicrohttpd-0.9.52.tar.gz - cd libmicrohttpd-0.9.52 - - ./configure --prefix=/usr + - ./configure --disable-examples --prefix=/usr - make - sudo make install - cd .. From 1a6316c203ea75b78eaea5d9b076f66e72aa505a Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Thu, 22 Nov 2018 17:38:09 -0800 Subject: [PATCH 228/623] Move to libmicrohttpd 0.9.60 --- .travis.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 0207d7bc..7bb36872 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,9 +14,9 @@ before_install: - if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo pip install cpp-coveralls; fi - if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo pip install gcovr; fi - if [ "$TRAVIS_OS_NAME" = "osx" ]; then export CFLAGS='-mtune=generic'; fi - - curl https://s3.amazonaws.com/libhttpserver/libmicrohttpd_releases/libmicrohttpd-0.9.52.tar.gz -o libmicrohttpd-0.9.52.tar.gz - - tar -xvzf libmicrohttpd-0.9.52.tar.gz - - cd libmicrohttpd-0.9.52 + - curl https://s3.amazonaws.com/libhttpserver/libmicrohttpd_releases/libmicrohttpd-0.9.60.tar.gz -o libmicrohttpd-0.9.60.tar.gz + - tar -xvzf libmicrohttpd-0.9.60.tar.gz + - cd libmicrohttpd-0.9.60 - ./configure --disable-examples --prefix=/usr - make - sudo make install From daffcf260dbebe30cb072d93c8cd57168aa63119 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Thu, 22 Nov 2018 17:40:54 -0800 Subject: [PATCH 229/623] Use libmicrohttpd 0.9.59 --- .travis.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 7bb36872..902d4a5d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,9 +14,9 @@ before_install: - if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo pip install cpp-coveralls; fi - if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo pip install gcovr; fi - if [ "$TRAVIS_OS_NAME" = "osx" ]; then export CFLAGS='-mtune=generic'; fi - - curl https://s3.amazonaws.com/libhttpserver/libmicrohttpd_releases/libmicrohttpd-0.9.60.tar.gz -o libmicrohttpd-0.9.60.tar.gz - - tar -xvzf libmicrohttpd-0.9.60.tar.gz - - cd libmicrohttpd-0.9.60 + - curl https://s3.amazonaws.com/libhttpserver/libmicrohttpd_releases/libmicrohttpd-0.9.59.tar.gz -o libmicrohttpd-0.9.59.tar.gz + - tar -xvzf libmicrohttpd-0.9.59.tar.gz + - cd libmicrohttpd-0.9.59 - ./configure --disable-examples --prefix=/usr - make - sudo make install From 1a67477b42fd0c193219dfe8c10b2b520e59fb9d Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Thu, 22 Nov 2018 17:57:42 -0800 Subject: [PATCH 230/623] Build off /usr/lib --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 902d4a5d..2ee788eb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,7 +17,7 @@ before_install: - curl https://s3.amazonaws.com/libhttpserver/libmicrohttpd_releases/libmicrohttpd-0.9.59.tar.gz -o libmicrohttpd-0.9.59.tar.gz - tar -xvzf libmicrohttpd-0.9.59.tar.gz - cd libmicrohttpd-0.9.59 - - ./configure --disable-examples --prefix=/usr + - ./configure --disable-examples - make - sudo make install - cd .. From bcd3fd153dc1540a9b83ffdc810426dd0b567d73 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Thu, 22 Nov 2018 18:12:48 -0800 Subject: [PATCH 231/623] Logging output of the test suite at completion --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 2ee788eb..73919afc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -29,6 +29,7 @@ script: - if [ $LINKING = "static" ]; then ../configure --enable-static --disable-fastopen; elif [ $DEBUG = "debug" ]; then ../configure --enable-debug --disable-shared --disable-fastopen; else ../configure --disable-fastopen; fi - make - make check + - cat test/test-suite.log after_success: - if [ $DEBUG = "debug" ] && [ "$TRAVIS_OS_NAME" = "linux" ]; then ../ci-report-coverage; fi matrix: From 3fe90d3bf2b2dd4cadee6a9079705ce5b3a33817 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Thu, 22 Nov 2018 18:33:45 -0800 Subject: [PATCH 232/623] specify ldflags when building on travis --- .travis.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 73919afc..cf4df604 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,9 +6,9 @@ compiler: - gcc - clang env: - - DEBUG="debug" - - DEBUG="nodebug" - - LINKING="static" + - DEBUG="debug" LDFLAGS="$LDFLAGS -L/usr/local/lib -L/usr/lib" + - DEBUG="nodebug" LDFLAGS="$LDFLAGS -L/usr/local/lib -L/usr/lib" + - LINKING="static" LDFLAGS="$LDFLAGS -L/usr/local/lib -L/usr/lib" before_install: - if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo apt-get install info install-info; fi - if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo pip install cpp-coveralls; fi From fd2bb10dbc2f1830e1f61d237e0b5cb3f15deabf Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Thu, 22 Nov 2018 18:37:12 -0800 Subject: [PATCH 233/623] fix ldflags in configure.ac --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 91ffeed2..dc543e73 100644 --- a/configure.ac +++ b/configure.ac @@ -111,7 +111,7 @@ AC_CHECK_HEADER([microhttpd.h], ) CXXFLAGS="-DHTTPSERVER_COMPILATION -D_REENTRANT $LIBMICROHTTPD_CFLAGS $CXXFLAGS" -LDFLAGS="$LIBMICROHTTPD_LIBS $REGEX_LIBS $LD_FLAGS" +LDFLAGS="$LIBMICROHTTPD_LIBS $REGEX_LIBS $LDFLAGS" AC_MSG_CHECKING([whether to build with TCP_FASTOPEN support]) AC_ARG_ENABLE([fastopen], From 70b3f95087bd60c91b0d301c2b329ec132653a1f Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Thu, 22 Nov 2018 18:39:05 -0800 Subject: [PATCH 234/623] travis print content of lib dirs --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index cf4df604..9a0e4e4e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -30,6 +30,8 @@ script: - make - make check - cat test/test-suite.log + - ls -l /usr/local/lib/ + - ls -l /usr/lib/ after_success: - if [ $DEBUG = "debug" ] && [ "$TRAVIS_OS_NAME" = "linux" ]; then ../ci-report-coverage; fi matrix: From 63d657ad312d14b01ff28394dae537ab50d6dfca Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Thu, 22 Nov 2018 18:46:54 -0800 Subject: [PATCH 235/623] travis print linked libs --- .travis.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.travis.yml b/.travis.yml index 9a0e4e4e..a2f34845 100644 --- a/.travis.yml +++ b/.travis.yml @@ -32,6 +32,9 @@ script: - cat test/test-suite.log - ls -l /usr/local/lib/ - ls -l /usr/lib/ + - ldd test/basic + - ldd test/http_utils + - ldd test/threaded after_success: - if [ $DEBUG = "debug" ] && [ "$TRAVIS_OS_NAME" = "linux" ]; then ../ci-report-coverage; fi matrix: From 8c0e4debce22e1d642769593c3a8fd8df8bc14c8 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Thu, 22 Nov 2018 18:51:19 -0800 Subject: [PATCH 236/623] set dylib path in travis --- .travis.yml | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index a2f34845..dedf33e0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,10 +6,14 @@ compiler: - gcc - clang env: - - DEBUG="debug" LDFLAGS="$LDFLAGS -L/usr/local/lib -L/usr/lib" - - DEBUG="nodebug" LDFLAGS="$LDFLAGS -L/usr/local/lib -L/usr/lib" - - LINKING="static" LDFLAGS="$LDFLAGS -L/usr/local/lib -L/usr/lib" + - DEBUG="debug" + - DEBUG="nodebug" + - LINKING="static" before_install: + - export LDFLAGS="$LDFLAGS -L/usr/local/lib -L/usr/lib" + - export PATH=$PATH:/usr/local/lib + - export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib + - export DYLD_LIBRARY_PATH=$DYLD_LIBRARY_PATH:/usr/local/lib - if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo apt-get install info install-info; fi - if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo pip install cpp-coveralls; fi - if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo pip install gcovr; fi From d20ae8d8c4fabe877be17e73bcba316ba85b2801 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Thu, 22 Nov 2018 18:56:48 -0800 Subject: [PATCH 237/623] remove ldd commands from travis --- .travis.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index dedf33e0..dc3998db 100644 --- a/.travis.yml +++ b/.travis.yml @@ -36,9 +36,6 @@ script: - cat test/test-suite.log - ls -l /usr/local/lib/ - ls -l /usr/lib/ - - ldd test/basic - - ldd test/http_utils - - ldd test/threaded after_success: - if [ $DEBUG = "debug" ] && [ "$TRAVIS_OS_NAME" = "linux" ]; then ../ci-report-coverage; fi matrix: From f8cdaa2b3e2397584415ab4e296f9c1ab97d6c79 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Fri, 23 Nov 2018 03:51:22 +0000 Subject: [PATCH 238/623] Disable epoll and poll automatically. Disable epoll and poll functions if the system doesn't support them --- configure.ac | 79 ++++++++++++++++++++++++++ m4/ax_have_epoll.m4 | 104 ++++++++++++++++++++++++++++++++++ src/httpserver/http_utils.hpp | 12 ++++ 3 files changed, 195 insertions(+) create mode 100644 m4/ax_have_epoll.m4 diff --git a/configure.ac b/configure.ac index dc543e73..2660fea5 100644 --- a/configure.ac +++ b/configure.ac @@ -133,6 +133,83 @@ if test x"$fastopen" = x"yes"; then fi fi +AC_ARG_ENABLE([[poll]], + [AS_HELP_STRING([[--enable-poll[=ARG]]], [enable poll support (yes, no, auto) [auto]])], + [enable_poll=${enableval}], + [enable_poll='auto'] + ) + +if test "$enable_poll" != "no"; then + if test "$os_is_native_w32" != "yes"; then + AC_CHECK_HEADERS([poll.h], + [ + AC_CHECK_FUNCS([poll], [have_poll='yes'], [have_poll='no']) + ], [], [AC_INCLUDES_DEFAULT]) + else + AC_MSG_CHECKING([for WSAPoll()]) + AC_LINK_IFELSE([ + AC_LANG_PROGRAM([[ +#include + ]], [[ +WSAPOLLFD fda[2]; +WSAPoll(fda, 2, 0);]])], + [ + have_poll='yes' + AC_DEFINE([HAVE_POLL],[1]) + ], [have_poll='no']) + AC_MSG_RESULT([$have_poll]) + fi + if test "$enable_poll" = "yes" && test "$have_poll" != "yes"; then + AC_MSG_ERROR([[Support for poll was explicitly requested but cannot be enabled on this platform.]]) + fi + enable_poll="$have_poll" +fi + +if test x"$enable_poll" = x"yes"; then + AM_CXXFLAGS="$AM_CXXFLAGS -DENABLE_POLL" + AM_CFLAGS="$AM_CXXFLAGS -DENABLE_POLL" +fi + +AC_ARG_ENABLE([[epoll]], + [AS_HELP_STRING([[--enable-epoll[=ARG]]], [enable epoll support (yes, no, auto) [auto]])], + [enable_epoll=${enableval}], + [enable_epoll='auto'] + ) + +if test "$enable_epoll" != "no"; then + AX_HAVE_EPOLL + if test "${ax_cv_have_epoll}" = "yes"; then + AC_DEFINE([[EPOLL_SUPPORT]],[[1]],[Define to 1 to enable epoll support]) + enable_epoll='yes' + else + if test "$enable_epoll" = "yes"; then + AC_MSG_ERROR([[Support for epoll was explicitly requested but cannot be enabled on this platform.]]) + fi + enable_epoll='no' + fi +fi + +AM_CONDITIONAL([MHD_HAVE_EPOLL], [[test "x$enable_epoll" = xyes]]) + +if test "x$enable_epoll" = "xyes"; then + AC_CACHE_CHECK([for epoll_create1()], [mhd_cv_have_epoll_create1], [ + AC_LINK_IFELSE([ + AC_LANG_PROGRAM([[ +#include + ]], [[ +int fd; +fd = epoll_create1(EPOLL_CLOEXEC);]])], + [mhd_cv_have_epoll_create1=yes], + [mhd_cv_have_epoll_create1=no])]) + AS_IF([test "x$mhd_cv_have_epoll_create1" = "xyes"],[ + AC_DEFINE([[HAVE_EPOLL_CREATE1]], [[1]], [Define if you have epoll_create1 function.])]) +fi + +if test x"$enable_epoll" = x"yes"; then + AM_CXXFLAGS="$AM_CXXFLAGS -DENABLE_EPOLL" + AM_CFLAGS="$AM_CXXFLAGS -DENABLE_EPOLL" +fi + AC_MSG_CHECKING([whether to link statically]) AC_ARG_ENABLE([static], [AS_HELP_STRING([--enable-static], @@ -235,5 +312,7 @@ AC_MSG_NOTICE([Configuration Summary: Debug : ${debugit} TLS Enabled : ${have_gnutls} TCP_FASTOPEN : ${is_fastopen_supported} + poll support : ${enable_poll=no} + epoll support : ${enable_epoll=no} Static : ${static} ]) diff --git a/m4/ax_have_epoll.m4 b/m4/ax_have_epoll.m4 new file mode 100644 index 00000000..9d9bc873 --- /dev/null +++ b/m4/ax_have_epoll.m4 @@ -0,0 +1,104 @@ +# =========================================================================== +# https://www.gnu.org/software/autoconf-archive/ax_have_epoll.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_HAVE_EPOLL([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) +# AX_HAVE_EPOLL_PWAIT([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) +# +# DESCRIPTION +# +# This macro determines whether the system supports the epoll I/O event +# interface. A neat usage example would be: +# +# AX_HAVE_EPOLL( +# [AX_CONFIG_FEATURE_ENABLE(epoll)], +# [AX_CONFIG_FEATURE_DISABLE(epoll)]) +# AX_CONFIG_FEATURE( +# [epoll], [This platform supports epoll(7)], +# [HAVE_EPOLL], [This platform supports epoll(7).]) +# +# The epoll interface was added to the Linux kernel in version 2.5.45, and +# the macro verifies that a kernel newer than this is installed. This +# check is somewhat unreliable if doesn't match the +# running kernel, but it is necessary regardless, because glibc comes with +# stubs for the epoll_create(), epoll_wait(), etc. that allow programs to +# compile and link even if the kernel is too old; the problem would then +# be detected only at runtime. +# +# Linux kernel version 2.6.19 adds the epoll_pwait() call in addition to +# epoll_wait(). The availability of that function can be tested with the +# second macro. Generally speaking, it is safe to assume that +# AX_HAVE_EPOLL would succeed if AX_HAVE_EPOLL_PWAIT has, but not the +# other way round. +# +# LICENSE +# +# Copyright (c) 2008 Peter Simons +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 11 + +AC_DEFUN([AX_HAVE_EPOLL], [dnl + ax_have_epoll_cppflags="${CPPFLAGS}" + AC_CHECK_HEADER([linux/version.h], [CPPFLAGS="${CPPFLAGS} -DHAVE_LINUX_VERSION_H"]) + AC_MSG_CHECKING([for Linux epoll(7) interface]) + AC_CACHE_VAL([ax_cv_have_epoll], [dnl + AC_LINK_IFELSE([dnl + AC_LANG_PROGRAM([dnl +#include +#ifdef HAVE_LINUX_VERSION_H +# include +# if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,45) +# error linux kernel version is too old to have epoll +# endif +#endif +], [dnl +int fd, rc; +struct epoll_event ev; +fd = epoll_create(128); +rc = epoll_wait(fd, &ev, 1, 0);])], + [ax_cv_have_epoll=yes], + [ax_cv_have_epoll=no])]) + CPPFLAGS="${ax_have_epoll_cppflags}" + AS_IF([test "${ax_cv_have_epoll}" = "yes"], + [AC_MSG_RESULT([yes]) +$1],[AC_MSG_RESULT([no]) +$2]) +])dnl + +AC_DEFUN([AX_HAVE_EPOLL_PWAIT], [dnl + ax_have_epoll_cppflags="${CPPFLAGS}" + AC_CHECK_HEADER([linux/version.h], + [CPPFLAGS="${CPPFLAGS} -DHAVE_LINUX_VERSION_H"]) + AC_MSG_CHECKING([for Linux epoll(7) interface with signals extension]) + AC_CACHE_VAL([ax_cv_have_epoll_pwait], [dnl + AC_LINK_IFELSE([dnl + AC_LANG_PROGRAM([dnl +#ifdef HAVE_LINUX_VERSION_H +# include +# if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) +# error linux kernel version is too old to have epoll_pwait +# endif +#endif +#include +#include +], [dnl +int fd, rc; +struct epoll_event ev; +fd = epoll_create(128); +rc = epoll_wait(fd, &ev, 1, 0); +rc = epoll_pwait(fd, &ev, 1, 0, (sigset_t const *)(0));])], + [ax_cv_have_epoll_pwait=yes], + [ax_cv_have_epoll_pwait=no])]) + CPPFLAGS="${ax_have_epoll_cppflags}" + AS_IF([test "${ax_cv_have_epoll_pwait}" = "yes"], + [AC_MSG_RESULT([yes]) +$1],[AC_MSG_RESULT([no]) +$2]) +])dnl diff --git a/src/httpserver/http_utils.hpp b/src/httpserver/http_utils.hpp index a1a22068..2143c5fc 100644 --- a/src/httpserver/http_utils.hpp +++ b/src/httpserver/http_utils.hpp @@ -77,11 +77,23 @@ class http_utils enum start_method_T { #if defined(__MINGW32__) || defined(__CYGWIN32__) + #ifdef ENABLE_POLL INTERNAL_SELECT = MHD_USE_SELECT_INTERNALLY | MHD_USE_POLL, + #else + INTERNAL_SELECT = MHD_USE_SELECT_INTERNALLY, + #endif #else + #ifdef ENABLE_EPOLL INTERNAL_SELECT = MHD_USE_SELECT_INTERNALLY | MHD_USE_EPOLL | MHD_USE_EPOLL_TURBO, + #else + INTERNAL_SELECT = MHD_USE_SELECT_INTERNALLY, + #endif #endif +#ifdef ENABLE_POLL THREAD_PER_CONNECTION = MHD_USE_THREAD_PER_CONNECTION | MHD_USE_POLL +#else + THREAD_PER_CONNECTION = MHD_USE_THREAD_PER_CONNECTION +#endif }; enum policy_T From 0725a7c2cfa326f71df022c2e28dde5c0c6cef91 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Fri, 23 Nov 2018 04:58:51 +0000 Subject: [PATCH 239/623] Fixed served unable to start on mac os --- ChangeLog | 3 +++ configure.ac | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index 86e8cb2a..a01584b5 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,6 @@ +Thu Nov 22 20:58:00 2018 -0800 + Solved problem with the server not being able to start on mac os + Sun Nov 04 19:28:00 2018 -0800 Moved http_endpoint as a sub-class of webserver. This avoids usage of friends. diff --git a/configure.ac b/configure.ac index 2660fea5..e2ef43cb 100644 --- a/configure.ac +++ b/configure.ac @@ -21,7 +21,7 @@ AC_PREREQ(2.57) m4_define([libhttpserver_MAJOR_VERSION],[0])dnl -m4_define([libhttpserver_MINOR_VERSION],[14])dnl +m4_define([libhttpserver_MINOR_VERSION],[15])dnl m4_define([libhttpserver_REVISION],[0])dnl m4_define([libhttpserver_PKG_VERSION],[libhttpserver_MAJOR_VERSION.libhttpserver_MINOR_VERSION.libhttpserver_REVISION])dnl m4_define([libhttpserver_LDF_VERSION],[libhttpserver_MAJOR_VERSION:libhttpserver_MINOR_VERSION:libhttpserver_REVISION])dnl From e7f2ed0950b8e96d601931579f5b5b8a2f5fd782 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Thu, 22 Nov 2018 21:44:06 -0800 Subject: [PATCH 240/623] Separate test building from execution This in preparation for arm cross-compilation --- .travis.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index dc3998db..3976ae02 100644 --- a/.travis.yml +++ b/.travis.yml @@ -26,12 +26,14 @@ before_install: - sudo make install - cd .. - eval "${MATRIX_EVAL}" -script: +install: - ./bootstrap - mkdir build - cd build - if [ $LINKING = "static" ]; then ../configure --enable-static --disable-fastopen; elif [ $DEBUG = "debug" ]; then ../configure --enable-debug --disable-shared --disable-fastopen; else ../configure --disable-fastopen; fi - make + - make check TESTS= +script: - make check - cat test/test-suite.log - ls -l /usr/local/lib/ From 9aacb7706af342ba095e146c4b30ee777637ddf8 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Fri, 23 Nov 2018 06:45:15 +0000 Subject: [PATCH 241/623] Travis to cross-build on arm Ignoring arm failures --- .travis.yml | 76 ++++++++++++++++++++++++++++++++++++++++++++++++---- configure.ac | 10 +++++++ 2 files changed, 81 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 3976ae02..8435f553 100644 --- a/.travis.yml +++ b/.travis.yml @@ -27,17 +27,39 @@ before_install: - cd .. - eval "${MATRIX_EVAL}" install: + - if [[ "$CROSS_COMPILE" == 1 ]] ; then + if [[ "$ARM_ARCH_DIR" == "aarch64-linux-gnu" ]] ; then + mkdir $HOME/linker_bin ; + ln -s /usr/bin/aarch64-linux-gnu-ld $HOME/linker_bin/ld ; + echo "SETTING GNU LINKER DIR" ; + ls -al $HOME/linker_bin/ld ; + fi + fi - ./bootstrap - mkdir build - cd build - - if [ $LINKING = "static" ]; then ../configure --enable-static --disable-fastopen; elif [ $DEBUG = "debug" ]; then ../configure --enable-debug --disable-shared --disable-fastopen; else ../configure --disable-fastopen; fi + - if [ $LINKING = "static" ]; then + ../configure --enable-static --disable-fastopen; + elif [ $DEBUG = "debug" ]; then + ../configure --enable-debug --disable-shared --disable-fastopen; + else ../configure --disable-fastopen; + fi - make - make check TESTS= script: - - make check - - cat test/test-suite.log - - ls -l /usr/local/lib/ - - ls -l /usr/lib/ + - if [[ "$CROSS_COMPILE" == 1 ]]; then + cd test ; + if [[ "$ARM_ARCH_DIR" == "aarch64-linux-gnu" ]]; then + qemu-aarch64 -L /usr/aarch64-linux-gnu/ ./run_tests ; + else + qemu-arm -L /usr/arm-linux-gnueabi/ ./run_tests ; + fi + else + make check ; + cat test/test-suite.log ; + ls -l /usr/local/lib/ ; + ls -l /usr/lib/ ; + fi after_success: - if [ $DEBUG = "debug" ] && [ "$TRAVIS_OS_NAME" = "linux" ]; then ../ci-report-coverage; fi matrix: @@ -74,3 +96,47 @@ matrix: - g++-7 env: - MATRIX_EVAL="CC=gcc-7 && CXX=g++-7" + - os: linux + addons: + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - clang-3.9 + - g++-arm-linux-gnueabi + - g++-multilib + - gcc-multilib + - qemu + - qemu-system-arm + env: CXX="clang++ -B$HOME/linker_bin/" CC="clang -B$HOME/linker_bin/" CROSS_COMPILE=1 ARM_ARCH_DIR=arm-linux-gnueabi ARM_GCC_VER=4.7.3 ARM_SETTINGS="armv7-a -mfpu=neon -mfloat-abi=softfp -target arm-linux-gnueabi" + - os: linux + addons: + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - clang-3.9 + - g++-arm-linux-gnueabi + - g++-multilib + - gcc-multilib + - qemu + - qemu-system-arm + env: CXX="clang++ -B$HOME/linker_bin/" CC="clang -B$HOME/linker_bin/" CROSS_COMPILE=1 ARM_ARCH_DIR=arm-linux-gnueabi ARM_GCC_VER=4.7.3 ARM_SETTINGS="armv8-a -mfpu=neon -mfloat-abi=softfp -target arm-linux-gnueabi" + - os: linux + addons: + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - clang-3.9 + - g++-4.8-aarch64-linux-gnu + - gcc-4.8-aarch64-linux-gnu + - g++-4.8-multilib + - gcc-4.8-multilib + - qemu + - qemu-system-arm + env: CXX="clang++ -B$HOME/linker_bin/" CC="clang -B$HOME/linker_bin/" CROSS_COMPILE=1 ARM_ARCH_DIR=aarch64-linux-gnu ARM_GCC_VER=4.8.4 ARM_SETTINGS="armv8-a -target aarch64-linux-gnueabi" + allow_failures: + - env: CXX="clang++ -B$HOME/linker_bin/" CC="clang -B$HOME/linker_bin/" CROSS_COMPILE=1 ARM_ARCH_DIR=arm-linux-gnueabi ARM_GCC_VER=4.7.3 ARM_SETTINGS="armv7-a -mfpu=neon -mfloat-abi=softfp -target arm-linux-gnueabi" + - env: CXX="clang++ -B$HOME/linker_bin/" CC="clang -B$HOME/linker_bin/" CROSS_COMPILE=1 ARM_ARCH_DIR=arm-linux-gnueabi ARM_GCC_VER=4.7.3 ARM_SETTINGS="armv8-a -mfpu=neon -mfloat-abi=softfp -target arm-linux-gnueabi" + - env: CXX="clang++ -B$HOME/linker_bin/" CC="clang -B$HOME/linker_bin/" CROSS_COMPILE=1 ARM_ARCH_DIR=aarch64-linux-gnu ARM_GCC_VER=4.8.4 ARM_SETTINGS="armv8-a -target aarch64-linux-gnueabi" diff --git a/configure.ac b/configure.ac index e2ef43cb..14d0dda3 100644 --- a/configure.ac +++ b/configure.ac @@ -266,6 +266,16 @@ else AM_CFLAGS="$AM_CXXFLAGS -O3" fi +if test "$ARM_SETTINGS" != ""; then + AM_CXXFLAGS="$AM_CXXFLAGS -march=$ARM_SETTINGS" + AM_CFLAGS="$AM_CFLAGS -march=$ARM_SETTINGS" +fi + +if test "$CROSS_COMPILE" == "1"; then + AM_CXXFLAGS="$AM_CXXFLAGS -I/usr/${ARM_ARCH_DIR}/include/c++/${ARM_GCC_VER}/${ARM_ARCH_DIR}/ -I/usr/${ARM_ARCH_DIR}/include/c++/${ARM_GCC_VER}/ -I/usr/${ARM_ARCH_DIR}/include/" + AM_CFLAGS="$AM_CFLAGS -I/usr/${ARM_ARCH_DIR}/include/c++/${ARM_GCC_VER}/${ARM_ARCH_DIR}/ -I/usr/${ARM_ARCH_DIR}/include/c++/${ARM_GCC_VER}/ -I/usr/${ARM_ARCH_DIR}/include/" +fi + AM_CONDITIONAL([COND_GCOV],[test x"$cond_gcov" = x"yes"]) AC_SUBST(COND_GCOV) From af3d1a66f9523fe05f4b7ced85ec7b1b533898fb Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Fri, 23 Nov 2018 07:09:34 +0000 Subject: [PATCH 242/623] Force travis to use clang on cross-compilation --- .travis.yml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index 8435f553..08191498 100644 --- a/.travis.yml +++ b/.travis.yml @@ -108,7 +108,7 @@ matrix: - gcc-multilib - qemu - qemu-system-arm - env: CXX="clang++ -B$HOME/linker_bin/" CC="clang -B$HOME/linker_bin/" CROSS_COMPILE=1 ARM_ARCH_DIR=arm-linux-gnueabi ARM_GCC_VER=4.7.3 ARM_SETTINGS="armv7-a -mfpu=neon -mfloat-abi=softfp -target arm-linux-gnueabi" + env: MATRIX_EVAL="CC=clang -B$HOME/linker_bin/ && CXX=clang++ -B$HOME/linker_bin/" CROSS_COMPILE=1 ARM_ARCH_DIR=arm-linux-gnueabi ARM_GCC_VER=4.7.3 ARM_SETTINGS="armv7-a -mfpu=neon -mfloat-abi=softfp -target arm-linux-gnueabi" - os: linux addons: apt: @@ -121,7 +121,7 @@ matrix: - gcc-multilib - qemu - qemu-system-arm - env: CXX="clang++ -B$HOME/linker_bin/" CC="clang -B$HOME/linker_bin/" CROSS_COMPILE=1 ARM_ARCH_DIR=arm-linux-gnueabi ARM_GCC_VER=4.7.3 ARM_SETTINGS="armv8-a -mfpu=neon -mfloat-abi=softfp -target arm-linux-gnueabi" + env: MATRIX_EVAL="CC=clang -B$HOME/linker_bin/ && CXX=clang++ -B$HOME/linker_bin/" CROSS_COMPILE=1 ARM_ARCH_DIR=arm-linux-gnueabi ARM_GCC_VER=4.7.3 ARM_SETTINGS="armv8-a -mfpu=neon -mfloat-abi=softfp -target arm-linux-gnueabi" - os: linux addons: apt: @@ -135,8 +135,8 @@ matrix: - gcc-4.8-multilib - qemu - qemu-system-arm - env: CXX="clang++ -B$HOME/linker_bin/" CC="clang -B$HOME/linker_bin/" CROSS_COMPILE=1 ARM_ARCH_DIR=aarch64-linux-gnu ARM_GCC_VER=4.8.4 ARM_SETTINGS="armv8-a -target aarch64-linux-gnueabi" + env: MATRIX_EVAL="CC=clang -B$HOME/linker_bin/ && CXX=clang++ -B$HOME/linker_bin/" CROSS_COMPILE=1 ARM_ARCH_DIR=aarch64-linux-gnu ARM_GCC_VER=4.8.4 ARM_SETTINGS="armv8-a -target aarch64-linux-gnueabi" allow_failures: - - env: CXX="clang++ -B$HOME/linker_bin/" CC="clang -B$HOME/linker_bin/" CROSS_COMPILE=1 ARM_ARCH_DIR=arm-linux-gnueabi ARM_GCC_VER=4.7.3 ARM_SETTINGS="armv7-a -mfpu=neon -mfloat-abi=softfp -target arm-linux-gnueabi" - - env: CXX="clang++ -B$HOME/linker_bin/" CC="clang -B$HOME/linker_bin/" CROSS_COMPILE=1 ARM_ARCH_DIR=arm-linux-gnueabi ARM_GCC_VER=4.7.3 ARM_SETTINGS="armv8-a -mfpu=neon -mfloat-abi=softfp -target arm-linux-gnueabi" - - env: CXX="clang++ -B$HOME/linker_bin/" CC="clang -B$HOME/linker_bin/" CROSS_COMPILE=1 ARM_ARCH_DIR=aarch64-linux-gnu ARM_GCC_VER=4.8.4 ARM_SETTINGS="armv8-a -target aarch64-linux-gnueabi" + env: MATRIX_EVAL="CC=clang -B$HOME/linker_bin/ && CXX=clang++ -B$HOME/linker_bin/" CROSS_COMPILE=1 ARM_ARCH_DIR=arm-linux-gnueabi ARM_GCC_VER=4.7.3 ARM_SETTINGS="armv7-a -mfpu=neon -mfloat-abi=softfp -target arm-linux-gnueabi" + env: MATRIX_EVAL="CC=clang -B$HOME/linker_bin/ && CXX=clang++ -B$HOME/linker_bin/" CROSS_COMPILE=1 ARM_ARCH_DIR=arm-linux-gnueabi ARM_GCC_VER=4.7.3 ARM_SETTINGS="armv8-a -mfpu=neon -mfloat-abi=softfp -target arm-linux-gnueabi" + env: MATRIX_EVAL="CC=clang -B$HOME/linker_bin/ && CXX=clang++ -B$HOME/linker_bin/" CROSS_COMPILE=1 ARM_ARCH_DIR=aarch64-linux-gnu ARM_GCC_VER=4.8.4 ARM_SETTINGS="armv8-a -target aarch64-linux-gnueabi" From dc8061d0103052179353db6913e3800c0783d2d5 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Fri, 23 Nov 2018 07:14:17 +0000 Subject: [PATCH 243/623] Fix travis not ignoring cross-compilation failures --- .travis.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 08191498..0aee740d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -137,6 +137,6 @@ matrix: - qemu-system-arm env: MATRIX_EVAL="CC=clang -B$HOME/linker_bin/ && CXX=clang++ -B$HOME/linker_bin/" CROSS_COMPILE=1 ARM_ARCH_DIR=aarch64-linux-gnu ARM_GCC_VER=4.8.4 ARM_SETTINGS="armv8-a -target aarch64-linux-gnueabi" allow_failures: - env: MATRIX_EVAL="CC=clang -B$HOME/linker_bin/ && CXX=clang++ -B$HOME/linker_bin/" CROSS_COMPILE=1 ARM_ARCH_DIR=arm-linux-gnueabi ARM_GCC_VER=4.7.3 ARM_SETTINGS="armv7-a -mfpu=neon -mfloat-abi=softfp -target arm-linux-gnueabi" - env: MATRIX_EVAL="CC=clang -B$HOME/linker_bin/ && CXX=clang++ -B$HOME/linker_bin/" CROSS_COMPILE=1 ARM_ARCH_DIR=arm-linux-gnueabi ARM_GCC_VER=4.7.3 ARM_SETTINGS="armv8-a -mfpu=neon -mfloat-abi=softfp -target arm-linux-gnueabi" - env: MATRIX_EVAL="CC=clang -B$HOME/linker_bin/ && CXX=clang++ -B$HOME/linker_bin/" CROSS_COMPILE=1 ARM_ARCH_DIR=aarch64-linux-gnu ARM_GCC_VER=4.8.4 ARM_SETTINGS="armv8-a -target aarch64-linux-gnueabi" + - env: MATRIX_EVAL="CC=clang -B$HOME/linker_bin/ && CXX=clang++ -B$HOME/linker_bin/" CROSS_COMPILE=1 ARM_ARCH_DIR=arm-linux-gnueabi ARM_GCC_VER=4.7.3 ARM_SETTINGS="armv7-a -mfpu=neon -mfloat-abi=softfp -target arm-linux-gnueabi" + - env: MATRIX_EVAL="CC=clang -B$HOME/linker_bin/ && CXX=clang++ -B$HOME/linker_bin/" CROSS_COMPILE=1 ARM_ARCH_DIR=arm-linux-gnueabi ARM_GCC_VER=4.7.3 ARM_SETTINGS="armv8-a -mfpu=neon -mfloat-abi=softfp -target arm-linux-gnueabi" + - env: MATRIX_EVAL="CC=clang -B$HOME/linker_bin/ && CXX=clang++ -B$HOME/linker_bin/" CROSS_COMPILE=1 ARM_ARCH_DIR=aarch64-linux-gnu ARM_GCC_VER=4.8.4 ARM_SETTINGS="armv8-a -target aarch64-linux-gnueabi" From 937de73358356e2e682eb14c9949fd93095ed273 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Fri, 23 Nov 2018 07:27:28 +0000 Subject: [PATCH 244/623] Avoid path being evaluated by travis when cross-compiling --- .travis.yml | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index 0aee740d..cb8fc9e5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -97,6 +97,7 @@ matrix: env: - MATRIX_EVAL="CC=gcc-7 && CXX=g++-7" - os: linux + compiler: clang addons: apt: sources: @@ -108,8 +109,9 @@ matrix: - gcc-multilib - qemu - qemu-system-arm - env: MATRIX_EVAL="CC=clang -B$HOME/linker_bin/ && CXX=clang++ -B$HOME/linker_bin/" CROSS_COMPILE=1 ARM_ARCH_DIR=arm-linux-gnueabi ARM_GCC_VER=4.7.3 ARM_SETTINGS="armv7-a -mfpu=neon -mfloat-abi=softfp -target arm-linux-gnueabi" + env: MATRIX_EVAL="CC='clang -B$HOME/linker_bin/' && CXX='clang++ -B$HOME/linker_bin/'" CROSS_COMPILE=1 ARM_ARCH_DIR=arm-linux-gnueabi ARM_GCC_VER=4.7.3 ARM_SETTINGS="armv7-a -mfpu=neon -mfloat-abi=softfp -target arm-linux-gnueabi" - os: linux + compiler: clang addons: apt: sources: @@ -121,8 +123,9 @@ matrix: - gcc-multilib - qemu - qemu-system-arm - env: MATRIX_EVAL="CC=clang -B$HOME/linker_bin/ && CXX=clang++ -B$HOME/linker_bin/" CROSS_COMPILE=1 ARM_ARCH_DIR=arm-linux-gnueabi ARM_GCC_VER=4.7.3 ARM_SETTINGS="armv8-a -mfpu=neon -mfloat-abi=softfp -target arm-linux-gnueabi" + env: MATRIX_EVAL="CC='clang -B$HOME/linker_bin/' && CXX='clang++ -B$HOME/linker_bin/'" CROSS_COMPILE=1 ARM_ARCH_DIR=arm-linux-gnueabi ARM_GCC_VER=4.7.3 ARM_SETTINGS="armv8-a -mfpu=neon -mfloat-abi=softfp -target arm-linux-gnueabi" - os: linux + compiler: clang addons: apt: sources: @@ -135,8 +138,8 @@ matrix: - gcc-4.8-multilib - qemu - qemu-system-arm - env: MATRIX_EVAL="CC=clang -B$HOME/linker_bin/ && CXX=clang++ -B$HOME/linker_bin/" CROSS_COMPILE=1 ARM_ARCH_DIR=aarch64-linux-gnu ARM_GCC_VER=4.8.4 ARM_SETTINGS="armv8-a -target aarch64-linux-gnueabi" + env: MATRIX_EVAL="CC='clang -B$HOME/linker_bin/' && CXX='clang++ -B$HOME/linker_bin/'" CROSS_COMPILE=1 ARM_ARCH_DIR=aarch64-linux-gnu ARM_GCC_VER=4.8.4 ARM_SETTINGS="armv8-a -target aarch64-linux-gnueabi" allow_failures: - - env: MATRIX_EVAL="CC=clang -B$HOME/linker_bin/ && CXX=clang++ -B$HOME/linker_bin/" CROSS_COMPILE=1 ARM_ARCH_DIR=arm-linux-gnueabi ARM_GCC_VER=4.7.3 ARM_SETTINGS="armv7-a -mfpu=neon -mfloat-abi=softfp -target arm-linux-gnueabi" - - env: MATRIX_EVAL="CC=clang -B$HOME/linker_bin/ && CXX=clang++ -B$HOME/linker_bin/" CROSS_COMPILE=1 ARM_ARCH_DIR=arm-linux-gnueabi ARM_GCC_VER=4.7.3 ARM_SETTINGS="armv8-a -mfpu=neon -mfloat-abi=softfp -target arm-linux-gnueabi" - - env: MATRIX_EVAL="CC=clang -B$HOME/linker_bin/ && CXX=clang++ -B$HOME/linker_bin/" CROSS_COMPILE=1 ARM_ARCH_DIR=aarch64-linux-gnu ARM_GCC_VER=4.8.4 ARM_SETTINGS="armv8-a -target aarch64-linux-gnueabi" + - env: MATRIX_EVAL="CC='clang -B$HOME/linker_bin/' && CXX='clang++ -B$HOME/linker_bin/'" CROSS_COMPILE=1 ARM_ARCH_DIR=arm-linux-gnueabi ARM_GCC_VER=4.7.3 ARM_SETTINGS="armv7-a -mfpu=neon -mfloat-abi=softfp -target arm-linux-gnueabi" + - env: MATRIX_EVAL="CC='clang -B$HOME/linker_bin/' && CXX='clang++ -B$HOME/linker_bin/'" CROSS_COMPILE=1 ARM_ARCH_DIR=arm-linux-gnueabi ARM_GCC_VER=4.7.3 ARM_SETTINGS="armv8-a -mfpu=neon -mfloat-abi=softfp -target arm-linux-gnueabi" + - env: MATRIX_EVAL="CC='clang -B$HOME/linker_bin/' && CXX='clang++ -B$HOME/linker_bin/'" CROSS_COMPILE=1 ARM_ARCH_DIR=aarch64-linux-gnu ARM_GCC_VER=4.8.4 ARM_SETTINGS="armv8-a -target aarch64-linux-gnueabi" From 6b058cf89c4db5f05fa2616fdefe716788ada594 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Fri, 23 Nov 2018 07:49:09 +0000 Subject: [PATCH 245/623] Set-up the linker correctly when cross-compiling --- .travis.yml | 12 ++++++------ configure.ac | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.travis.yml b/.travis.yml index cb8fc9e5..2d162bed 100644 --- a/.travis.yml +++ b/.travis.yml @@ -109,7 +109,7 @@ matrix: - gcc-multilib - qemu - qemu-system-arm - env: MATRIX_EVAL="CC='clang -B$HOME/linker_bin/' && CXX='clang++ -B$HOME/linker_bin/'" CROSS_COMPILE=1 ARM_ARCH_DIR=arm-linux-gnueabi ARM_GCC_VER=4.7.3 ARM_SETTINGS="armv7-a -mfpu=neon -mfloat-abi=softfp -target arm-linux-gnueabi" + env: CROSS_COMPILE=1 ARM_LD_PATH=$HOME/linker_bin ARM_ARCH_DIR=arm-linux-gnueabi ARM_GCC_VER=4.7.3 ARM_SETTINGS="armv7-a -mfpu=neon -mfloat-abi=softfp -target arm-linux-gnueabi" - os: linux compiler: clang addons: @@ -123,7 +123,7 @@ matrix: - gcc-multilib - qemu - qemu-system-arm - env: MATRIX_EVAL="CC='clang -B$HOME/linker_bin/' && CXX='clang++ -B$HOME/linker_bin/'" CROSS_COMPILE=1 ARM_ARCH_DIR=arm-linux-gnueabi ARM_GCC_VER=4.7.3 ARM_SETTINGS="armv8-a -mfpu=neon -mfloat-abi=softfp -target arm-linux-gnueabi" + env: CROSS_COMPILE=1 ARM_LD_PATH=$HOME/linker_bin ARM_ARCH_DIR=arm-linux-gnueabi ARM_GCC_VER=4.7.3 ARM_SETTINGS="armv8-a -mfpu=neon -mfloat-abi=softfp -target arm-linux-gnueabi" - os: linux compiler: clang addons: @@ -138,8 +138,8 @@ matrix: - gcc-4.8-multilib - qemu - qemu-system-arm - env: MATRIX_EVAL="CC='clang -B$HOME/linker_bin/' && CXX='clang++ -B$HOME/linker_bin/'" CROSS_COMPILE=1 ARM_ARCH_DIR=aarch64-linux-gnu ARM_GCC_VER=4.8.4 ARM_SETTINGS="armv8-a -target aarch64-linux-gnueabi" + env: CROSS_COMPILE=1 ARM_LD_PATH=$HOME/linker_bin ARM_ARCH_DIR=aarch64-linux-gnu ARM_GCC_VER=4.8.4 ARM_SETTINGS="armv8-a -target aarch64-linux-gnueabi" allow_failures: - - env: MATRIX_EVAL="CC='clang -B$HOME/linker_bin/' && CXX='clang++ -B$HOME/linker_bin/'" CROSS_COMPILE=1 ARM_ARCH_DIR=arm-linux-gnueabi ARM_GCC_VER=4.7.3 ARM_SETTINGS="armv7-a -mfpu=neon -mfloat-abi=softfp -target arm-linux-gnueabi" - - env: MATRIX_EVAL="CC='clang -B$HOME/linker_bin/' && CXX='clang++ -B$HOME/linker_bin/'" CROSS_COMPILE=1 ARM_ARCH_DIR=arm-linux-gnueabi ARM_GCC_VER=4.7.3 ARM_SETTINGS="armv8-a -mfpu=neon -mfloat-abi=softfp -target arm-linux-gnueabi" - - env: MATRIX_EVAL="CC='clang -B$HOME/linker_bin/' && CXX='clang++ -B$HOME/linker_bin/'" CROSS_COMPILE=1 ARM_ARCH_DIR=aarch64-linux-gnu ARM_GCC_VER=4.8.4 ARM_SETTINGS="armv8-a -target aarch64-linux-gnueabi" + - env: CROSS_COMPILE=1 ARM_LD_PATH=$HOME/linker_bin ARM_ARCH_DIR=arm-linux-gnueabi ARM_GCC_VER=4.7.3 ARM_SETTINGS="armv7-a -mfpu=neon -mfloat-abi=softfp -target arm-linux-gnueabi" + - env: CROSS_COMPILE=1 ARM_LD_PATH=$HOME/linker_bin ARM_ARCH_DIR=arm-linux-gnueabi ARM_GCC_VER=4.7.3 ARM_SETTINGS="armv8-a -mfpu=neon -mfloat-abi=softfp -target arm-linux-gnueabi" + - env: CROSS_COMPILE=1 ARM_LD_PATH=$HOME/linker_bin ARM_ARCH_DIR=aarch64-linux-gnu ARM_GCC_VER=4.8.4 ARM_SETTINGS="armv8-a -target aarch64-linux-gnueabi" diff --git a/configure.ac b/configure.ac index 14d0dda3..665a0410 100644 --- a/configure.ac +++ b/configure.ac @@ -272,8 +272,8 @@ if test "$ARM_SETTINGS" != ""; then fi if test "$CROSS_COMPILE" == "1"; then - AM_CXXFLAGS="$AM_CXXFLAGS -I/usr/${ARM_ARCH_DIR}/include/c++/${ARM_GCC_VER}/${ARM_ARCH_DIR}/ -I/usr/${ARM_ARCH_DIR}/include/c++/${ARM_GCC_VER}/ -I/usr/${ARM_ARCH_DIR}/include/" - AM_CFLAGS="$AM_CFLAGS -I/usr/${ARM_ARCH_DIR}/include/c++/${ARM_GCC_VER}/${ARM_ARCH_DIR}/ -I/usr/${ARM_ARCH_DIR}/include/c++/${ARM_GCC_VER}/ -I/usr/${ARM_ARCH_DIR}/include/" + AM_CXXFLAGS="$AM_CXXFLAGS --prefix=${ARM_LD_PATH}/ -I/usr/${ARM_ARCH_DIR}/include/c++/${ARM_GCC_VER}/${ARM_ARCH_DIR}/ -I/usr/${ARM_ARCH_DIR}/include/c++/${ARM_GCC_VER}/ -I/usr/${ARM_ARCH_DIR}/include/" + AM_CFLAGS="$AM_CFLAGS --prefix=${ARM_LD_PATH}/ -I/usr/${ARM_ARCH_DIR}/include/c++/${ARM_GCC_VER}/${ARM_ARCH_DIR}/ -I/usr/${ARM_ARCH_DIR}/include/c++/${ARM_GCC_VER}/ -I/usr/${ARM_ARCH_DIR}/include/" fi AM_CONDITIONAL([COND_GCOV],[test x"$cond_gcov" = x"yes"]) From 2772d1959b3456acfe8fae06f3a4efb119738842 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Fri, 23 Nov 2018 08:04:36 +0000 Subject: [PATCH 246/623] Force custom linker in arm cross-compilation --- configure.ac | 2 ++ 1 file changed, 2 insertions(+) diff --git a/configure.ac b/configure.ac index 665a0410..759f45d9 100644 --- a/configure.ac +++ b/configure.ac @@ -274,6 +274,8 @@ fi if test "$CROSS_COMPILE" == "1"; then AM_CXXFLAGS="$AM_CXXFLAGS --prefix=${ARM_LD_PATH}/ -I/usr/${ARM_ARCH_DIR}/include/c++/${ARM_GCC_VER}/${ARM_ARCH_DIR}/ -I/usr/${ARM_ARCH_DIR}/include/c++/${ARM_GCC_VER}/ -I/usr/${ARM_ARCH_DIR}/include/" AM_CFLAGS="$AM_CFLAGS --prefix=${ARM_LD_PATH}/ -I/usr/${ARM_ARCH_DIR}/include/c++/${ARM_GCC_VER}/${ARM_ARCH_DIR}/ -I/usr/${ARM_ARCH_DIR}/include/c++/${ARM_GCC_VER}/ -I/usr/${ARM_ARCH_DIR}/include/" + CXXLINK="${ARM_LD_PATH}/ld" + LINK="${ARM_LD_PATH}/ld" fi AM_CONDITIONAL([COND_GCOV],[test x"$cond_gcov" = x"yes"]) From 2168ed80b63bd4152ee9b63e9b924001efba2b03 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Fri, 23 Nov 2018 01:16:06 -0800 Subject: [PATCH 247/623] Fixing path when cross-compiling --- configure.ac | 1 + 1 file changed, 1 insertion(+) diff --git a/configure.ac b/configure.ac index 759f45d9..c8994bba 100644 --- a/configure.ac +++ b/configure.ac @@ -274,6 +274,7 @@ fi if test "$CROSS_COMPILE" == "1"; then AM_CXXFLAGS="$AM_CXXFLAGS --prefix=${ARM_LD_PATH}/ -I/usr/${ARM_ARCH_DIR}/include/c++/${ARM_GCC_VER}/${ARM_ARCH_DIR}/ -I/usr/${ARM_ARCH_DIR}/include/c++/${ARM_GCC_VER}/ -I/usr/${ARM_ARCH_DIR}/include/" AM_CFLAGS="$AM_CFLAGS --prefix=${ARM_LD_PATH}/ -I/usr/${ARM_ARCH_DIR}/include/c++/${ARM_GCC_VER}/${ARM_ARCH_DIR}/ -I/usr/${ARM_ARCH_DIR}/include/c++/${ARM_GCC_VER}/ -I/usr/${ARM_ARCH_DIR}/include/" + PATH="${ARM_LD_PATH}/:$PATH" CXXLINK="${ARM_LD_PATH}/ld" LINK="${ARM_LD_PATH}/ld" fi From 2a60ac3aa557778bc000440e3ae6b778ebc11d37 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Fri, 23 Nov 2018 02:07:22 -0800 Subject: [PATCH 248/623] libtool to using right linker when cross-compiling --- configure.ac | 2 ++ 1 file changed, 2 insertions(+) diff --git a/configure.ac b/configure.ac index c8994bba..14ef11ed 100644 --- a/configure.ac +++ b/configure.ac @@ -277,6 +277,8 @@ if test "$CROSS_COMPILE" == "1"; then PATH="${ARM_LD_PATH}/:$PATH" CXXLINK="${ARM_LD_PATH}/ld" LINK="${ARM_LD_PATH}/ld" + LD="${ARM_LD_PATH}/ld" + LDFLAGS="$LDFLAGS -Wl,-rpath -Wl,/usr/lib -Wl,-rpath-link -Wl,${ARM_LD_PATH}/usr/lib -L${ARM_LD_PATH}/lib -L${ARM_LD_PATH}/usr/lib" fi AM_CONDITIONAL([COND_GCOV],[test x"$cond_gcov" = x"yes"]) From 9692f913ba0c9b59fbde17f49c4218bdccea104f Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sat, 24 Nov 2018 00:29:46 -0800 Subject: [PATCH 249/623] Force linker when cross-compiling --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 2d162bed..9adb0f73 100644 --- a/.travis.yml +++ b/.travis.yml @@ -31,6 +31,7 @@ install: if [[ "$ARM_ARCH_DIR" == "aarch64-linux-gnu" ]] ; then mkdir $HOME/linker_bin ; ln -s /usr/bin/aarch64-linux-gnu-ld $HOME/linker_bin/ld ; + LD=$HOME/linker_bin/ld ; echo "SETTING GNU LINKER DIR" ; ls -al $HOME/linker_bin/ld ; fi From 497c80fbdc132c0e38cfc552020376f544d2dafc Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sat, 24 Nov 2018 00:45:03 -0800 Subject: [PATCH 250/623] Print resulting makefile, libtool and config.log --- .travis.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.travis.yml b/.travis.yml index 9adb0f73..70e55ba9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -39,12 +39,15 @@ install: - ./bootstrap - mkdir build - cd build + - cat Makefile + - cat libtool - if [ $LINKING = "static" ]; then ../configure --enable-static --disable-fastopen; elif [ $DEBUG = "debug" ]; then ../configure --enable-debug --disable-shared --disable-fastopen; else ../configure --disable-fastopen; fi + - cat config.log - make - make check TESTS= script: From b3b46ea526d19729f2214ee53af47cb2c4bf0e9d Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sat, 24 Nov 2018 00:48:26 -0800 Subject: [PATCH 251/623] Print makefile and libtool --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 70e55ba9..1a802856 100644 --- a/.travis.yml +++ b/.travis.yml @@ -39,8 +39,6 @@ install: - ./bootstrap - mkdir build - cd build - - cat Makefile - - cat libtool - if [ $LINKING = "static" ]; then ../configure --enable-static --disable-fastopen; elif [ $DEBUG = "debug" ]; then @@ -48,6 +46,8 @@ install: else ../configure --disable-fastopen; fi - cat config.log + - cat Makefile + - cat libtool - make - make check TESTS= script: From d218c5169ca0f5adcad95eb31e1efa6ce91a771b Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sat, 24 Nov 2018 01:13:39 -0800 Subject: [PATCH 252/623] Replace instances of the linker when x-compiling --- .travis.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 1a802856..834be690 100644 --- a/.travis.yml +++ b/.travis.yml @@ -45,9 +45,8 @@ install: ../configure --enable-debug --disable-shared --disable-fastopen; else ../configure --disable-fastopen; fi - - cat config.log - - cat Makefile - - cat libtool + - if [ "$CROSS_COMPILE" = 1 ] && [ "$ARM_ARCH_DIR" = "aarch64-linux-gnu" ]; then sed -i 's+/usr/bin/ld+$HOME/linker_bin/ld+g' libtool; fi + - if [ "$CROSS_COMPILE" = 1 ] && [ "$ARM_ARCH_DIR" = "aarch64-linux-gnu" ]; then sed -i 's+/usr/bin/ld+$HOME/linker_bin/ld+g' Makefile; fi - make - make check TESTS= script: From 96a9693a7551bc4f19f3bc3bdbf6a7e8d64d157c Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sat, 24 Nov 2018 13:49:34 -0800 Subject: [PATCH 253/623] Forcing linker at configure level --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index 834be690..1d4c96d1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -43,6 +43,8 @@ install: ../configure --enable-static --disable-fastopen; elif [ $DEBUG = "debug" ]; then ../configure --enable-debug --disable-shared --disable-fastopen; + elif [ "$CROSS_COMPILE" = 1 ] && [ "$ARM_ARCH_DIR" = "aarch64-linux-gnu" ]; then + ../configure --disable-fastopen CC="gcc -B$HOME/linker_bin" CXX="g++ -B$HOME/linker_bin" else ../configure --disable-fastopen; fi - if [ "$CROSS_COMPILE" = 1 ] && [ "$ARM_ARCH_DIR" = "aarch64-linux-gnu" ]; then sed -i 's+/usr/bin/ld+$HOME/linker_bin/ld+g' libtool; fi From 21603b0384b10d4aa967b7c3bb891c503aafbf30 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sat, 24 Nov 2018 13:55:33 -0800 Subject: [PATCH 254/623] setting linker only for aarch-64 --- .travis.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 1d4c96d1..1ae71561 100644 --- a/.travis.yml +++ b/.travis.yml @@ -114,7 +114,7 @@ matrix: - gcc-multilib - qemu - qemu-system-arm - env: CROSS_COMPILE=1 ARM_LD_PATH=$HOME/linker_bin ARM_ARCH_DIR=arm-linux-gnueabi ARM_GCC_VER=4.7.3 ARM_SETTINGS="armv7-a -mfpu=neon -mfloat-abi=softfp -target arm-linux-gnueabi" + env: CROSS_COMPILE=1 ARM_ARCH_DIR=arm-linux-gnueabi ARM_GCC_VER=4.7.3 ARM_SETTINGS="armv7-a -mfpu=neon -mfloat-abi=softfp -target arm-linux-gnueabi" - os: linux compiler: clang addons: @@ -128,7 +128,7 @@ matrix: - gcc-multilib - qemu - qemu-system-arm - env: CROSS_COMPILE=1 ARM_LD_PATH=$HOME/linker_bin ARM_ARCH_DIR=arm-linux-gnueabi ARM_GCC_VER=4.7.3 ARM_SETTINGS="armv8-a -mfpu=neon -mfloat-abi=softfp -target arm-linux-gnueabi" + env: CROSS_COMPILE=1 ARM_ARCH_DIR=arm-linux-gnueabi ARM_GCC_VER=4.7.3 ARM_SETTINGS="armv8-a -mfpu=neon -mfloat-abi=softfp -target arm-linux-gnueabi" - os: linux compiler: clang addons: @@ -145,6 +145,6 @@ matrix: - qemu-system-arm env: CROSS_COMPILE=1 ARM_LD_PATH=$HOME/linker_bin ARM_ARCH_DIR=aarch64-linux-gnu ARM_GCC_VER=4.8.4 ARM_SETTINGS="armv8-a -target aarch64-linux-gnueabi" allow_failures: - - env: CROSS_COMPILE=1 ARM_LD_PATH=$HOME/linker_bin ARM_ARCH_DIR=arm-linux-gnueabi ARM_GCC_VER=4.7.3 ARM_SETTINGS="armv7-a -mfpu=neon -mfloat-abi=softfp -target arm-linux-gnueabi" - - env: CROSS_COMPILE=1 ARM_LD_PATH=$HOME/linker_bin ARM_ARCH_DIR=arm-linux-gnueabi ARM_GCC_VER=4.7.3 ARM_SETTINGS="armv8-a -mfpu=neon -mfloat-abi=softfp -target arm-linux-gnueabi" + - env: CROSS_COMPILE=1 ARM_ARCH_DIR=arm-linux-gnueabi ARM_GCC_VER=4.7.3 ARM_SETTINGS="armv7-a -mfpu=neon -mfloat-abi=softfp -target arm-linux-gnueabi" + - env: CROSS_COMPILE=1 ARM_ARCH_DIR=arm-linux-gnueabi ARM_GCC_VER=4.7.3 ARM_SETTINGS="armv8-a -mfpu=neon -mfloat-abi=softfp -target arm-linux-gnueabi" - env: CROSS_COMPILE=1 ARM_LD_PATH=$HOME/linker_bin ARM_ARCH_DIR=aarch64-linux-gnu ARM_GCC_VER=4.8.4 ARM_SETTINGS="armv8-a -target aarch64-linux-gnueabi" From d3db5529be63db117851368827fabbabee1c827e Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sat, 24 Nov 2018 13:59:49 -0800 Subject: [PATCH 255/623] No need to force the linker off aarch64 --- configure.ac | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/configure.ac b/configure.ac index 14ef11ed..4eb800e5 100644 --- a/configure.ac +++ b/configure.ac @@ -272,13 +272,17 @@ if test "$ARM_SETTINGS" != ""; then fi if test "$CROSS_COMPILE" == "1"; then - AM_CXXFLAGS="$AM_CXXFLAGS --prefix=${ARM_LD_PATH}/ -I/usr/${ARM_ARCH_DIR}/include/c++/${ARM_GCC_VER}/${ARM_ARCH_DIR}/ -I/usr/${ARM_ARCH_DIR}/include/c++/${ARM_GCC_VER}/ -I/usr/${ARM_ARCH_DIR}/include/" - AM_CFLAGS="$AM_CFLAGS --prefix=${ARM_LD_PATH}/ -I/usr/${ARM_ARCH_DIR}/include/c++/${ARM_GCC_VER}/${ARM_ARCH_DIR}/ -I/usr/${ARM_ARCH_DIR}/include/c++/${ARM_GCC_VER}/ -I/usr/${ARM_ARCH_DIR}/include/" - PATH="${ARM_LD_PATH}/:$PATH" - CXXLINK="${ARM_LD_PATH}/ld" - LINK="${ARM_LD_PATH}/ld" - LD="${ARM_LD_PATH}/ld" - LDFLAGS="$LDFLAGS -Wl,-rpath -Wl,/usr/lib -Wl,-rpath-link -Wl,${ARM_LD_PATH}/usr/lib -L${ARM_LD_PATH}/lib -L${ARM_LD_PATH}/usr/lib" + AM_CXXFLAGS="$AM_CXXFLAGS -I/usr/${ARM_ARCH_DIR}/include/c++/${ARM_GCC_VER}/${ARM_ARCH_DIR}/ -I/usr/${ARM_ARCH_DIR}/include/c++/${ARM_GCC_VER}/ -I/usr/${ARM_ARCH_DIR}/include/" + AM_CFLAGS="$AM_CFLAGS -I/usr/${ARM_ARCH_DIR}/include/c++/${ARM_GCC_VER}/${ARM_ARCH_DIR}/ -I/usr/${ARM_ARCH_DIR}/include/c++/${ARM_GCC_VER}/ -I/usr/${ARM_ARCH_DIR}/include/" + if test "$ARM_ARCH_DIR" == "aarch64-linux-gnu"; then + AM_CXXFLAGS="$AM_CXXFLAGS --prefix=${ARM_LD_PATH}/" + AM_CFLAGS="$AM_CFLAGS --prefix=${ARM_LD_PATH}/" + PATH="${ARM_LD_PATH}/:$PATH" + CXXLINK="${ARM_LD_PATH}/ld" + LINK="${ARM_LD_PATH}/ld" + LD="${ARM_LD_PATH}/ld" + LDFLAGS="$LDFLAGS -Wl,-rpath -Wl,/usr/lib -Wl,-rpath-link -Wl,${ARM_LD_PATH}/usr/lib -L${ARM_LD_PATH}/lib -L${ARM_LD_PATH}/usr/lib" + fi fi AM_CONDITIONAL([COND_GCOV],[test x"$cond_gcov" = x"yes"]) From ffe3dead175e2a2c862ba6005f97e49cdd94981b Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sat, 24 Nov 2018 14:01:43 -0800 Subject: [PATCH 256/623] Fixing travis-ci script --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 1ae71561..b8c1335b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -44,7 +44,7 @@ install: elif [ $DEBUG = "debug" ]; then ../configure --enable-debug --disable-shared --disable-fastopen; elif [ "$CROSS_COMPILE" = 1 ] && [ "$ARM_ARCH_DIR" = "aarch64-linux-gnu" ]; then - ../configure --disable-fastopen CC="gcc -B$HOME/linker_bin" CXX="g++ -B$HOME/linker_bin" + ../configure --disable-fastopen CC="gcc -B$HOME/linker_bin" CXX="g++ -B$HOME/linker_bin"; else ../configure --disable-fastopen; fi - if [ "$CROSS_COMPILE" = 1 ] && [ "$ARM_ARCH_DIR" = "aarch64-linux-gnu" ]; then sed -i 's+/usr/bin/ld+$HOME/linker_bin/ld+g' libtool; fi From d25965605e24e4223891b6beab2706ca20673a13 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sat, 24 Nov 2018 14:34:28 -0800 Subject: [PATCH 257/623] Fixing script .travis.yml --- .travis.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index b8c1335b..7bc327e7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -43,12 +43,12 @@ install: ../configure --enable-static --disable-fastopen; elif [ $DEBUG = "debug" ]; then ../configure --enable-debug --disable-shared --disable-fastopen; - elif [ "$CROSS_COMPILE" = 1 ] && [ "$ARM_ARCH_DIR" = "aarch64-linux-gnu" ]; then + elif [ "$CROSS_COMPILE" = "1" ] && [ "$ARM_ARCH_DIR" = "aarch64-linux-gnu" ]; then ../configure --disable-fastopen CC="gcc -B$HOME/linker_bin" CXX="g++ -B$HOME/linker_bin"; else ../configure --disable-fastopen; fi - - if [ "$CROSS_COMPILE" = 1 ] && [ "$ARM_ARCH_DIR" = "aarch64-linux-gnu" ]; then sed -i 's+/usr/bin/ld+$HOME/linker_bin/ld+g' libtool; fi - - if [ "$CROSS_COMPILE" = 1 ] && [ "$ARM_ARCH_DIR" = "aarch64-linux-gnu" ]; then sed -i 's+/usr/bin/ld+$HOME/linker_bin/ld+g' Makefile; fi + - if [ "$CROSS_COMPILE" = "1" ] && [ "$ARM_ARCH_DIR" = "aarch64-linux-gnu" ]; then sed -i 's+/usr/bin/ld+$HOME/linker_bin/ld+g' libtool; fi + - if [ "$CROSS_COMPILE" = "1" ] && [ "$ARM_ARCH_DIR" = "aarch64-linux-gnu" ]; then sed -i 's+/usr/bin/ld+$HOME/linker_bin/ld+g' Makefile; fi - make - make check TESTS= script: @@ -66,7 +66,7 @@ script: ls -l /usr/lib/ ; fi after_success: - - if [ $DEBUG = "debug" ] && [ "$TRAVIS_OS_NAME" = "linux" ]; then ../ci-report-coverage; fi + - if [ "$DEBUG" = "debug" ] && [ "$TRAVIS_OS_NAME" = "linux" ]; then ../ci-report-coverage; fi matrix: exclude: - compiler: clang From a59083cdc40621a4a469f8094e51c5cc3be9794b Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sat, 24 Nov 2018 14:44:42 -0800 Subject: [PATCH 258/623] Fix .travis.yml script --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 7bc327e7..282ac3f0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -39,9 +39,9 @@ install: - ./bootstrap - mkdir build - cd build - - if [ $LINKING = "static" ]; then + - if [ "$LINKING" = "static" ]; then ../configure --enable-static --disable-fastopen; - elif [ $DEBUG = "debug" ]; then + elif [ "$DEBUG" = "debug" ]; then ../configure --enable-debug --disable-shared --disable-fastopen; elif [ "$CROSS_COMPILE" = "1" ] && [ "$ARM_ARCH_DIR" = "aarch64-linux-gnu" ]; then ../configure --disable-fastopen CC="gcc -B$HOME/linker_bin" CXX="g++ -B$HOME/linker_bin"; From 452afe9d23eb4c3a0dcb252211d43ba63395628a Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sat, 24 Nov 2018 15:31:43 -0800 Subject: [PATCH 259/623] Cross-compile libmicrohttpd also --- .travis.yml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 282ac3f0..249a2c66 100644 --- a/.travis.yml +++ b/.travis.yml @@ -21,7 +21,7 @@ before_install: - curl https://s3.amazonaws.com/libhttpserver/libmicrohttpd_releases/libmicrohttpd-0.9.59.tar.gz -o libmicrohttpd-0.9.59.tar.gz - tar -xvzf libmicrohttpd-0.9.59.tar.gz - cd libmicrohttpd-0.9.59 - - ./configure --disable-examples + - if [[ "$ARM_ARCH_DIR" != "" ]]; then ./configure --host $ARM_ARCH_DIR --disable-examples; else ./configure --disable-examples; fi - make - sudo make install - cd .. @@ -44,8 +44,11 @@ install: elif [ "$DEBUG" = "debug" ]; then ../configure --enable-debug --disable-shared --disable-fastopen; elif [ "$CROSS_COMPILE" = "1" ] && [ "$ARM_ARCH_DIR" = "aarch64-linux-gnu" ]; then - ../configure --disable-fastopen CC="gcc -B$HOME/linker_bin" CXX="g++ -B$HOME/linker_bin"; - else ../configure --disable-fastopen; + ../configure --disable-fastopen --host aarch64-linux-gnu CC="gcc -B$HOME/linker_bin" CXX="g++ -B$HOME/linker_bin"; + elif [ "$CROSS_COMPILE" = "1" ] && [ "$ARM_ARCH_DIR" = "arm-linux-gnueabi" ]; then + ../configure --disable-fastopen --host arm-linux-gnueabi; + else + ../configure --disable-fastopen; fi - if [ "$CROSS_COMPILE" = "1" ] && [ "$ARM_ARCH_DIR" = "aarch64-linux-gnu" ]; then sed -i 's+/usr/bin/ld+$HOME/linker_bin/ld+g' libtool; fi - if [ "$CROSS_COMPILE" = "1" ] && [ "$ARM_ARCH_DIR" = "aarch64-linux-gnu" ]; then sed -i 's+/usr/bin/ld+$HOME/linker_bin/ld+g' Makefile; fi From 915fc4694c42de1a9343315b6d2062f67257de7d Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sat, 24 Nov 2018 23:54:30 +0000 Subject: [PATCH 260/623] Simplifying cross-copilation logic --- .travis.yml | 23 ++++------------------- configure.ac | 7 ------- 2 files changed, 4 insertions(+), 26 deletions(-) diff --git a/.travis.yml b/.travis.yml index 249a2c66..100f001d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -117,21 +117,7 @@ matrix: - gcc-multilib - qemu - qemu-system-arm - env: CROSS_COMPILE=1 ARM_ARCH_DIR=arm-linux-gnueabi ARM_GCC_VER=4.7.3 ARM_SETTINGS="armv7-a -mfpu=neon -mfloat-abi=softfp -target arm-linux-gnueabi" - - os: linux - compiler: clang - addons: - apt: - sources: - - ubuntu-toolchain-r-test - packages: - - clang-3.9 - - g++-arm-linux-gnueabi - - g++-multilib - - gcc-multilib - - qemu - - qemu-system-arm - env: CROSS_COMPILE=1 ARM_ARCH_DIR=arm-linux-gnueabi ARM_GCC_VER=4.7.3 ARM_SETTINGS="armv8-a -mfpu=neon -mfloat-abi=softfp -target arm-linux-gnueabi" + env: CROSS_COMPILE=1 ARM_ARCH_DIR=arm-linux-gnueabi - os: linux compiler: clang addons: @@ -146,8 +132,7 @@ matrix: - gcc-4.8-multilib - qemu - qemu-system-arm - env: CROSS_COMPILE=1 ARM_LD_PATH=$HOME/linker_bin ARM_ARCH_DIR=aarch64-linux-gnu ARM_GCC_VER=4.8.4 ARM_SETTINGS="armv8-a -target aarch64-linux-gnueabi" + env: CROSS_COMPILE=1 ARM_LD_PATH=$HOME/linker_bin ARM_ARCH_DIR=aarch64-linux-gnu allow_failures: - - env: CROSS_COMPILE=1 ARM_ARCH_DIR=arm-linux-gnueabi ARM_GCC_VER=4.7.3 ARM_SETTINGS="armv7-a -mfpu=neon -mfloat-abi=softfp -target arm-linux-gnueabi" - - env: CROSS_COMPILE=1 ARM_ARCH_DIR=arm-linux-gnueabi ARM_GCC_VER=4.7.3 ARM_SETTINGS="armv8-a -mfpu=neon -mfloat-abi=softfp -target arm-linux-gnueabi" - - env: CROSS_COMPILE=1 ARM_LD_PATH=$HOME/linker_bin ARM_ARCH_DIR=aarch64-linux-gnu ARM_GCC_VER=4.8.4 ARM_SETTINGS="armv8-a -target aarch64-linux-gnueabi" + - env: CROSS_COMPILE=1 ARM_ARCH_DIR=arm-linux-gnueabi + - env: CROSS_COMPILE=1 ARM_LD_PATH=$HOME/linker_bin ARM_ARCH_DIR=aarch64-linux-gnu diff --git a/configure.ac b/configure.ac index 4eb800e5..7116ccef 100644 --- a/configure.ac +++ b/configure.ac @@ -266,14 +266,7 @@ else AM_CFLAGS="$AM_CXXFLAGS -O3" fi -if test "$ARM_SETTINGS" != ""; then - AM_CXXFLAGS="$AM_CXXFLAGS -march=$ARM_SETTINGS" - AM_CFLAGS="$AM_CFLAGS -march=$ARM_SETTINGS" -fi - if test "$CROSS_COMPILE" == "1"; then - AM_CXXFLAGS="$AM_CXXFLAGS -I/usr/${ARM_ARCH_DIR}/include/c++/${ARM_GCC_VER}/${ARM_ARCH_DIR}/ -I/usr/${ARM_ARCH_DIR}/include/c++/${ARM_GCC_VER}/ -I/usr/${ARM_ARCH_DIR}/include/" - AM_CFLAGS="$AM_CFLAGS -I/usr/${ARM_ARCH_DIR}/include/c++/${ARM_GCC_VER}/${ARM_ARCH_DIR}/ -I/usr/${ARM_ARCH_DIR}/include/c++/${ARM_GCC_VER}/ -I/usr/${ARM_ARCH_DIR}/include/" if test "$ARM_ARCH_DIR" == "aarch64-linux-gnu"; then AM_CXXFLAGS="$AM_CXXFLAGS --prefix=${ARM_LD_PATH}/" AM_CFLAGS="$AM_CFLAGS --prefix=${ARM_LD_PATH}/" From a342f04694716742ae5361350f8e05d559c9a189 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sat, 24 Nov 2018 16:20:56 -0800 Subject: [PATCH 261/623] Run tests in qemu using make check --- .travis.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 100f001d..99a49198 100644 --- a/.travis.yml +++ b/.travis.yml @@ -56,11 +56,10 @@ install: - make check TESTS= script: - if [[ "$CROSS_COMPILE" == 1 ]]; then - cd test ; if [[ "$ARM_ARCH_DIR" == "aarch64-linux-gnu" ]]; then - qemu-aarch64 -L /usr/aarch64-linux-gnu/ ./run_tests ; + qemu-aarch64 -L /usr/aarch64-linux-gnu/ make check ; else - qemu-arm -L /usr/arm-linux-gnueabi/ ./run_tests ; + qemu-arm -L /usr/arm-linux-gnueabi/ make check ; fi else make check ; From dd53911385d76429413c16eb350b720f2bfa6d68 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 25 Nov 2018 00:32:24 +0000 Subject: [PATCH 262/623] Run tests on qemu --- .travis.yml | 5 +++-- configure.ac | 2 ++ test/run_tests | 5 +++++ 3 files changed, 10 insertions(+), 2 deletions(-) create mode 100755 test/run_tests diff --git a/.travis.yml b/.travis.yml index 99a49198..100f001d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -56,10 +56,11 @@ install: - make check TESTS= script: - if [[ "$CROSS_COMPILE" == 1 ]]; then + cd test ; if [[ "$ARM_ARCH_DIR" == "aarch64-linux-gnu" ]]; then - qemu-aarch64 -L /usr/aarch64-linux-gnu/ make check ; + qemu-aarch64 -L /usr/aarch64-linux-gnu/ ./run_tests ; else - qemu-arm -L /usr/arm-linux-gnueabi/ make check ; + qemu-arm -L /usr/arm-linux-gnueabi/ ./run_tests ; fi else make check ; diff --git a/configure.ac b/configure.ac index 7116ccef..e2db99e7 100644 --- a/configure.ac +++ b/configure.ac @@ -308,6 +308,8 @@ AC_SUBST(LDFLAGS) AC_SUBST(EXT_LIB_PATH) AC_SUBST(EXT_LIBS) +AC_CONFIG_LINKS([test/run_tests:test/run_tests]) + AC_OUTPUT( libhttpserver.pc Makefile diff --git a/test/run_tests b/test/run_tests new file mode 100755 index 00000000..46870932 --- /dev/null +++ b/test/run_tests @@ -0,0 +1,5 @@ +#!/bin/sh + +./basic +./http_utils +./threaded From 73f22a1ddf3f4e4a1b362c8c98fd9bb1f30462b1 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 25 Nov 2018 00:57:56 +0000 Subject: [PATCH 263/623] Runs specific tests on travis --- .travis.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 100f001d..54de7b40 100644 --- a/.travis.yml +++ b/.travis.yml @@ -58,9 +58,13 @@ script: - if [[ "$CROSS_COMPILE" == 1 ]]; then cd test ; if [[ "$ARM_ARCH_DIR" == "aarch64-linux-gnu" ]]; then - qemu-aarch64 -L /usr/aarch64-linux-gnu/ ./run_tests ; + qemu-aarch64 -L /usr/aarch64-linux-gnu/ ./.libs/basic ; + qemu-aarch64 -L /usr/aarch64-linux-gnu/ ./.libs/http_utils ; + qemu-aarch64 -L /usr/aarch64-linux-gnu/ ./.libs/threaded ; else - qemu-arm -L /usr/arm-linux-gnueabi/ ./run_tests ; + qemu-arm -L /usr/arm-linux-gnueabi/ ./.libs/basic ; + qemu-arm -L /usr/arm-linux-gnueabi/ ./.libs/http_utils ; + qemu-arm -L /usr/arm-linux-gnueabi/ ./.libs/threaded ; fi else make check ; From e5ac002e46f8cd87416ec91da71b03ede2ad9bed Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 25 Nov 2018 01:10:11 +0000 Subject: [PATCH 264/623] Print file type for tests --- .travis.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.travis.yml b/.travis.yml index 54de7b40..378d8457 100644 --- a/.travis.yml +++ b/.travis.yml @@ -58,10 +58,18 @@ script: - if [[ "$CROSS_COMPILE" == 1 ]]; then cd test ; if [[ "$ARM_ARCH_DIR" == "aarch64-linux-gnu" ]]; then + file ./.libs/basic; + file ./.libs/http_utils; + file ./.libs/threaded; + qemu-aarch64 -L /usr/aarch64-linux-gnu/ ./.libs/basic ; qemu-aarch64 -L /usr/aarch64-linux-gnu/ ./.libs/http_utils ; qemu-aarch64 -L /usr/aarch64-linux-gnu/ ./.libs/threaded ; else + file ./.libs/basic; + file ./.libs/http_utils; + file ./.libs/threaded; + qemu-arm -L /usr/arm-linux-gnueabi/ ./.libs/basic ; qemu-arm -L /usr/arm-linux-gnueabi/ ./.libs/http_utils ; qemu-arm -L /usr/arm-linux-gnueabi/ ./.libs/threaded ; From 1fb06921ee22d9b5292482d24d5d403ee419abed Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 25 Nov 2018 01:31:21 +0000 Subject: [PATCH 265/623] Check type of built libs --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 378d8457..d03542e6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -54,6 +54,7 @@ install: - if [ "$CROSS_COMPILE" = "1" ] && [ "$ARM_ARCH_DIR" = "aarch64-linux-gnu" ]; then sed -i 's+/usr/bin/ld+$HOME/linker_bin/ld+g' Makefile; fi - make - make check TESTS= + - file src/.libs/libhttpserver.so* script: - if [[ "$CROSS_COMPILE" == 1 ]]; then cd test ; From dd7b3f425494004d56aa0262ce96d1bffabc4abd Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 25 Nov 2018 01:41:25 +0000 Subject: [PATCH 266/623] Revert "Check type of built libs" This reverts commit 1fb06921ee22d9b5292482d24d5d403ee419abed. --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index d03542e6..378d8457 100644 --- a/.travis.yml +++ b/.travis.yml @@ -54,7 +54,6 @@ install: - if [ "$CROSS_COMPILE" = "1" ] && [ "$ARM_ARCH_DIR" = "aarch64-linux-gnu" ]; then sed -i 's+/usr/bin/ld+$HOME/linker_bin/ld+g' Makefile; fi - make - make check TESTS= - - file src/.libs/libhttpserver.so* script: - if [[ "$CROSS_COMPILE" == 1 ]]; then cd test ; From 8e03b4b815ad1cbfec5b53440149038c1850790b Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 25 Nov 2018 02:04:12 +0000 Subject: [PATCH 267/623] Specify build param when cross-compiling --- .travis.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 378d8457..3e14ecad 100644 --- a/.travis.yml +++ b/.travis.yml @@ -21,7 +21,7 @@ before_install: - curl https://s3.amazonaws.com/libhttpserver/libmicrohttpd_releases/libmicrohttpd-0.9.59.tar.gz -o libmicrohttpd-0.9.59.tar.gz - tar -xvzf libmicrohttpd-0.9.59.tar.gz - cd libmicrohttpd-0.9.59 - - if [[ "$ARM_ARCH_DIR" != "" ]]; then ./configure --host $ARM_ARCH_DIR --disable-examples; else ./configure --disable-examples; fi + - if [[ "$ARM_ARCH_DIR" != "" ]]; then ./configure --build `./config.guess` --host $ARM_ARCH_DIR --disable-examples; else ./configure --disable-examples; fi - make - sudo make install - cd .. @@ -44,9 +44,9 @@ install: elif [ "$DEBUG" = "debug" ]; then ../configure --enable-debug --disable-shared --disable-fastopen; elif [ "$CROSS_COMPILE" = "1" ] && [ "$ARM_ARCH_DIR" = "aarch64-linux-gnu" ]; then - ../configure --disable-fastopen --host aarch64-linux-gnu CC="gcc -B$HOME/linker_bin" CXX="g++ -B$HOME/linker_bin"; + ../configure --disable-fastopen --build `../config.guess` --host aarch64-linux-gnu CC="gcc -B$HOME/linker_bin" CXX="g++ -B$HOME/linker_bin"; elif [ "$CROSS_COMPILE" = "1" ] && [ "$ARM_ARCH_DIR" = "arm-linux-gnueabi" ]; then - ../configure --disable-fastopen --host arm-linux-gnueabi; + ../configure --disable-fastopen --build `../config.guess` --host arm-linux-gnueabi; else ../configure --disable-fastopen; fi From 4cad19386549ae7e35d8f5b967dce6760225ceae Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 25 Nov 2018 09:11:45 -0800 Subject: [PATCH 268/623] Specify to use CC=arm-linux-gnueabi-gcc --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 3e14ecad..74a67b9b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -129,7 +129,7 @@ matrix: - gcc-multilib - qemu - qemu-system-arm - env: CROSS_COMPILE=1 ARM_ARCH_DIR=arm-linux-gnueabi + env: CROSS_COMPILE=1 ARM_ARCH_DIR=arm-linux-gnueabi CC=arm-linux-gnueabi-gcc - os: linux compiler: clang addons: @@ -146,5 +146,5 @@ matrix: - qemu-system-arm env: CROSS_COMPILE=1 ARM_LD_PATH=$HOME/linker_bin ARM_ARCH_DIR=aarch64-linux-gnu allow_failures: - - env: CROSS_COMPILE=1 ARM_ARCH_DIR=arm-linux-gnueabi + - env: CROSS_COMPILE=1 ARM_ARCH_DIR=arm-linux-gnueabi CC=arm-linux-gnueabi-gcc - env: CROSS_COMPILE=1 ARM_LD_PATH=$HOME/linker_bin ARM_ARCH_DIR=aarch64-linux-gnu From b9eaf2ee11c853f9667430703906b8536314e837 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Tue, 27 Nov 2018 00:46:08 -0800 Subject: [PATCH 269/623] Add more combinations of gcc and clang --- .travis.yml | 87 ++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 83 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 74a67b9b..6aeef65f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -116,6 +116,85 @@ matrix: - g++-7 env: - MATRIX_EVAL="CC=gcc-7 && CXX=g++-7" + - os: osx + osx_image: xcode8 + env: + - MATRIX_EVAL="CC=gcc-4.9 && CXX=g++-4.9" + - os: osx + osx_image: xcode8 + env: + - MATRIX_EVAL="brew install gcc5 && CC=gcc-5 && CXX=g++-5" + - os: osx + osx_image: xcode8 + env: + - MATRIX_EVAL="brew install gcc6 && CC=gcc-6 && CXX=g++-6" + - os: osx + osx_image: xcode8 + env: + - MATRIX_EVAL="brew install gcc && CC=gcc-7 && CXX=g++-7" + # works on Precise and Trusty + - os: linux + addons: + apt: + sources: + - ubuntu-toolchain-r-test + - llvm-toolchain-precise-3.6 + packages: + - clang-3.6 + env: + - MATRIX_EVAL="CC=clang-3.6 && CXX=clang++-3.6" + # works on Precise and Trusty + - os: linux + addons: + apt: + sources: + - ubuntu-toolchain-r-test + - llvm-toolchain-precise-3.7 + packages: + - clang-3.7 + env: + - MATRIX_EVAL="CC=clang-3.7 && CXX=clang++-3.7" + # works on Precise and Trusty + - os: linux + addons: + apt: + sources: + - ubuntu-toolchain-r-test + - llvm-toolchain-precise-3.8 + packages: + - clang-3.8 + env: + - MATRIX_EVAL="CC=clang-3.8 && CXX=clang++-3.8" + # works on Trusty + - os: linux + addons: + apt: + sources: + - llvm-toolchain-trusty-3.9 + packages: + - clang-3.9 + env: + - MATRIX_EVAL="CC=clang-3.9 && CXX=clang++-3.9" + # works on Trusty + - os: linux + addons: + apt: + sources: + - llvm-toolchain-trusty-4.0 + packages: + - clang-4.0 + env: + - MATRIX_EVAL="CC=clang-4.0 && CXX=clang++-4.0" + # works on Trusty + - os: linux + addons: + apt: + sources: + - llvm-toolchain-trusty-5.0 + packages: + - clang-5.0 + env: + - MATRIX_EVAL="CC=clang-5.0 && CXX=clang++-5.0" - os: linux compiler: clang addons: @@ -129,7 +208,7 @@ matrix: - gcc-multilib - qemu - qemu-system-arm - env: CROSS_COMPILE=1 ARM_ARCH_DIR=arm-linux-gnueabi CC=arm-linux-gnueabi-gcc + env: MATRIX_EVAL="CROSS_COMPILE=1 ARM_ARCH_DIR=arm-linux-gnueabi CC=arm-linux-gnueabi-gcc" - os: linux compiler: clang addons: @@ -144,7 +223,7 @@ matrix: - gcc-4.8-multilib - qemu - qemu-system-arm - env: CROSS_COMPILE=1 ARM_LD_PATH=$HOME/linker_bin ARM_ARCH_DIR=aarch64-linux-gnu + env: MATRIX_EVAL="CROSS_COMPILE=1 ARM_LD_PATH=$HOME/linker_bin ARM_ARCH_DIR=aarch64-linux-gnu" allow_failures: - - env: CROSS_COMPILE=1 ARM_ARCH_DIR=arm-linux-gnueabi CC=arm-linux-gnueabi-gcc - - env: CROSS_COMPILE=1 ARM_LD_PATH=$HOME/linker_bin ARM_ARCH_DIR=aarch64-linux-gnu + - env: MATRIX_EVAL="CROSS_COMPILE=1 ARM_ARCH_DIR=arm-linux-gnueabi CC=arm-linux-gnueabi-gcc" + - env: MATRIX_EVAL="CROSS_COMPILE=1 ARM_LD_PATH=$HOME/linker_bin ARM_ARCH_DIR=aarch64-linux-gnu" From 621d8fa9776f95eee35a9ef2fd53eecd8e0653ba Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Tue, 27 Nov 2018 01:04:39 -0800 Subject: [PATCH 270/623] Remove the gcc on mac use cases --- .travis.yml | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/.travis.yml b/.travis.yml index 6aeef65f..51cbeb26 100644 --- a/.travis.yml +++ b/.travis.yml @@ -116,22 +116,22 @@ matrix: - g++-7 env: - MATRIX_EVAL="CC=gcc-7 && CXX=g++-7" - - os: osx - osx_image: xcode8 - env: - - MATRIX_EVAL="CC=gcc-4.9 && CXX=g++-4.9" - - os: osx - osx_image: xcode8 - env: - - MATRIX_EVAL="brew install gcc5 && CC=gcc-5 && CXX=g++-5" - - os: osx - osx_image: xcode8 - env: - - MATRIX_EVAL="brew install gcc6 && CC=gcc-6 && CXX=g++-6" - - os: osx - osx_image: xcode8 - env: - - MATRIX_EVAL="brew install gcc && CC=gcc-7 && CXX=g++-7" + #- os: osx + # osx_image: xcode8 + # env: + # - MATRIX_EVAL="CC=gcc-4.9 && CXX=g++-4.9" + #- os: osx + # osx_image: xcode8 + # env: + # - MATRIX_EVAL="brew install gcc5 && CC=gcc-5 && CXX=g++-5" + #- os: osx + # osx_image: xcode8 + # env: + # - MATRIX_EVAL="brew install gcc6 && CC=gcc-6 && CXX=g++-6" + #- os: osx + # osx_image: xcode8 + # env: + # - MATRIX_EVAL="brew install gcc && CC=gcc-7 && CXX=g++-7" # works on Precise and Trusty - os: linux addons: From a8e9bef033766038d592dfd38ee7dba97a07ceae Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Tue, 27 Nov 2018 01:07:45 -0800 Subject: [PATCH 271/623] Fixing matrix_eval in cross-compilation --- .travis.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 51cbeb26..e1131969 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,6 +10,7 @@ env: - DEBUG="nodebug" - LINKING="static" before_install: + - eval "${MATRIX_EVAL}" - export LDFLAGS="$LDFLAGS -L/usr/local/lib -L/usr/lib" - export PATH=$PATH:/usr/local/lib - export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib @@ -25,7 +26,6 @@ before_install: - make - sudo make install - cd .. - - eval "${MATRIX_EVAL}" install: - if [[ "$CROSS_COMPILE" == 1 ]] ; then if [[ "$ARM_ARCH_DIR" == "aarch64-linux-gnu" ]] ; then @@ -208,7 +208,7 @@ matrix: - gcc-multilib - qemu - qemu-system-arm - env: MATRIX_EVAL="CROSS_COMPILE=1 ARM_ARCH_DIR=arm-linux-gnueabi CC=arm-linux-gnueabi-gcc" + env: MATRIX_EVAL="CROSS_COMPILE=1 && ARM_ARCH_DIR=arm-linux-gnueabi && CC=arm-linux-gnueabi-gcc" - os: linux compiler: clang addons: @@ -223,7 +223,7 @@ matrix: - gcc-4.8-multilib - qemu - qemu-system-arm - env: MATRIX_EVAL="CROSS_COMPILE=1 ARM_LD_PATH=$HOME/linker_bin ARM_ARCH_DIR=aarch64-linux-gnu" + env: MATRIX_EVAL="CROSS_COMPILE=1 && ARM_LD_PATH=$HOME/linker_bin && ARM_ARCH_DIR=aarch64-linux-gnu" allow_failures: - - env: MATRIX_EVAL="CROSS_COMPILE=1 ARM_ARCH_DIR=arm-linux-gnueabi CC=arm-linux-gnueabi-gcc" - - env: MATRIX_EVAL="CROSS_COMPILE=1 ARM_LD_PATH=$HOME/linker_bin ARM_ARCH_DIR=aarch64-linux-gnu" + - env: MATRIX_EVAL="CROSS_COMPILE=1 && ARM_ARCH_DIR=arm-linux-gnueabi && CC=arm-linux-gnueabi-gcc" + - env: MATRIX_EVAL="CROSS_COMPILE=1 && ARM_LD_PATH=$HOME/linker_bin && ARM_ARCH_DIR=aarch64-linux-gnu" From 92041feed534d8f6277882d0d4ea5ee3b3151229 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Wed, 28 Nov 2018 22:07:11 -0800 Subject: [PATCH 272/623] Run cppcheck on the code --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index e1131969..4787fe5f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,6 +18,7 @@ before_install: - if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo apt-get install info install-info; fi - if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo pip install cpp-coveralls; fi - if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo pip install gcovr; fi + - if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo apt-get install cppcheck; fi - if [ "$TRAVIS_OS_NAME" = "osx" ]; then export CFLAGS='-mtune=generic'; fi - curl https://s3.amazonaws.com/libhttpserver/libmicrohttpd_releases/libmicrohttpd-0.9.59.tar.gz -o libmicrohttpd-0.9.59.tar.gz - tar -xvzf libmicrohttpd-0.9.59.tar.gz @@ -79,6 +80,7 @@ script: cat test/test-suite.log ; ls -l /usr/local/lib/ ; ls -l /usr/lib/ ; + if [ "$TRAVIS_OS_NAME" = "linux" ]; then cd ../src/; cppcheck --error-exitcode=1 .; cd ../build; fi fi after_success: - if [ "$DEBUG" = "debug" ] && [ "$TRAVIS_OS_NAME" = "linux" ]; then ../ci-report-coverage; fi From a810f1653b609574444089bf0b1207069c47a45a Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Wed, 28 Nov 2018 22:22:32 -0800 Subject: [PATCH 273/623] Move from coveralls to codecov --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 4787fe5f..9f3ace93 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,7 +16,7 @@ before_install: - export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib - export DYLD_LIBRARY_PATH=$DYLD_LIBRARY_PATH:/usr/local/lib - if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo apt-get install info install-info; fi - - if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo pip install cpp-coveralls; fi + - if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo pip install codecov; fi - if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo pip install gcovr; fi - if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo apt-get install cppcheck; fi - if [ "$TRAVIS_OS_NAME" = "osx" ]; then export CFLAGS='-mtune=generic'; fi From e390de09771ce67765f70de6ab21e7cd07765516 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Wed, 28 Nov 2018 22:22:48 -0800 Subject: [PATCH 274/623] Move from coveralls to codecov --- ci-report-coverage | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/ci-report-coverage b/ci-report-coverage index 1b017d6c..d8f72e16 100755 --- a/ci-report-coverage +++ b/ci-report-coverage @@ -10,5 +10,10 @@ cp -R ../test/* ./test/ cp -R ../test/integ/* ./test/integ/ cp -R ../test/unit/* ./test/unit/ -echo "Sending coveralls json report" -coveralls --gcov-options '\-lp' +echo "Sending json report" +for filename in `find . | egrep '\.cpp'`; +do + gcov -n -o . $filename > /dev/null; +done + +codecov From 64af4db192fafc17fde6b9c83e21e0c2ef469d10 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Wed, 28 Nov 2018 22:24:05 -0800 Subject: [PATCH 275/623] Change coveralls to codecov --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 9f3ace93..c5da85fb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -83,7 +83,7 @@ script: if [ "$TRAVIS_OS_NAME" = "linux" ]; then cd ../src/; cppcheck --error-exitcode=1 .; cd ../build; fi fi after_success: - - if [ "$DEBUG" = "debug" ] && [ "$TRAVIS_OS_NAME" = "linux" ]; then ../ci-report-coverage; fi + - if [ "$DEBUG" = "debug" ] && [ "$TRAVIS_OS_NAME" = "linux" ]; then bash <(curl -s https://codecov.io/bash); fi matrix: exclude: - compiler: clang From 9590198f01932b83aaa3ddffd30bf86ef5af814b Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Fri, 7 Dec 2018 04:11:03 +0000 Subject: [PATCH 276/623] Use proper return (rather then paramer returns) --- src/http_endpoint.cpp | 2 +- src/http_request.cpp | 2 +- src/http_utils.cpp | 53 ++++++++++++----------------- src/httpserver/http_request.hpp | 3 +- src/httpserver/http_utils.hpp | 16 +++------ src/httpserver/string_utilities.hpp | 11 +++--- src/string_utilities.cpp | 27 +++++++++------ src/webserver.cpp | 5 ++- test/unit/http_utils_test.cpp | 12 +++---- 9 files changed, 59 insertions(+), 72 deletions(-) diff --git a/src/http_endpoint.cpp b/src/http_endpoint.cpp index f1be0414..2d66b3df 100644 --- a/src/http_endpoint.cpp +++ b/src/http_endpoint.cpp @@ -59,7 +59,7 @@ webserver::http_endpoint::http_endpoint if(url_complete[0] != '/') url_complete = "/" + url_complete; - http_utils::tokenize_url(url, parts); + parts = http_utils::tokenize_url(url); string buffered; bool first = true; diff --git a/src/http_request.cpp b/src/http_request.cpp index a6e3112f..ec61d06a 100644 --- a/src/http_request.cpp +++ b/src/http_request.cpp @@ -31,7 +31,7 @@ namespace httpserver void http_request::set_method(const std::string& method) { - string_utilities::to_upper_copy(method, this->method); + this->method = string_utilities::to_upper_copy(method); } bool http_request::check_digest_auth( diff --git a/src/http_utils.cpp b/src/http_utils.cpp index 1eb29ed1..4b3aac84 100644 --- a/src/http_utils.cpp +++ b/src/http_utils.cpp @@ -210,22 +210,21 @@ const std::string http_utils::http_post_encoding_multipart_formdata = MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA; -size_t http_utils::tokenize_url( +std::vector http_utils::tokenize_url( const std::string& str, - std::vector& result, const char separator ) { - string_utilities::string_split(str, result, separator); - return result.size(); + return string_utilities::string_split(str, separator); } -void http_utils::standardize_url(const std::string& url, std::string& result) +std::string http_utils::standardize_url(const std::string& url) { - std::string n_url; - string_utilities::regex_replace(url, "(\\/)+", "/", n_url); + std::string n_url = string_utilities::regex_replace(url, "(\\/)+", "/"); std::string::size_type n_url_length = n_url.length(); + std::string result; + if (n_url_length > 1 && n_url[n_url_length - 1] == '/') { result = n_url.substr(0, n_url_length - 1); @@ -234,14 +233,17 @@ void http_utils::standardize_url(const std::string& url, std::string& result) { result = n_url; } + + return result; } -void get_ip_str( +std::string get_ip_str( const struct sockaddr *sa, - std::string& result, socklen_t maxlen ) { + std::string result; + if(sa) { int addrlen = sizeof(sockaddr_in); @@ -256,16 +258,8 @@ void get_ip_str( result = to_ret; } } -} -std::string get_ip_str_new( - const struct sockaddr* sa, - socklen_t maxlen -) -{ - std::string to_ret; - get_ip_str(sa, to_ret, maxlen); - return to_ret; + return result; } unsigned short get_port(const struct sockaddr* sa) @@ -355,7 +349,7 @@ ip_representation::ip_representation(const std::string& ip) if(ip.find(':') != std::string::npos) //IPV6 { ip_version = http_utils::IPV6; - string_utilities::string_split(ip, parts, ':', false); + parts = string_utilities::string_split(ip, ':', false); int y = 0; for(unsigned int i = 0; i < parts.size(); i++) { @@ -385,8 +379,7 @@ ip_representation::ip_representation(const std::string& ip) } if(parts[i].find('.') != std::string::npos) { - vector subparts; - string_utilities::string_split(parts[i], subparts, '.'); + vector subparts = string_utilities::string_split(parts[i], '.'); if(subparts.size() == 4) { for(unsigned int ii = 0; ii < subparts.size(); ii++) @@ -447,7 +440,7 @@ ip_representation::ip_representation(const std::string& ip) else //IPV4 { ip_version = http_utils::IPV4; - string_utilities::string_split(ip, parts, '.'); + parts = string_utilities::string_split(ip, '.'); if(parts.size() == 4) { for(unsigned int i = 0; i < parts.size(); i++) @@ -487,26 +480,22 @@ bool ip_representation::operator <(const ip_representation& b) const return false; } -size_t load_file (const char* filename, char** content) +char* load_file (const char *filename) { + char* content = NULL; + ifstream fp(filename, ios::in | ios::binary | ios::ate); if(fp.is_open()) { int size = fp.tellg(); - *content = (char*) malloc(size * sizeof(char)); + content = (char*) malloc(size * sizeof(char)); fp.seekg(0, ios::beg); - fp.read(*content, size); + fp.read(content, size); fp.close(); - return size; + return content; } else throw file_access_exception(); -} - -char* load_file (const char *filename) -{ - char* content = NULL; - load_file(filename, &content); return content; } diff --git a/src/httpserver/http_request.hpp b/src/httpserver/http_request.hpp index 83ae85aa..c3f328c6 100644 --- a/src/httpserver/http_request.hpp +++ b/src/httpserver/http_request.hpp @@ -505,8 +505,7 @@ class http_request void set_path(const std::string& path) { this->path = path; - std::vector complete_path; - http::http_utils::tokenize_url(this->path, complete_path); + std::vector complete_path = http::http_utils::tokenize_url(this->path); for(unsigned int i = 0; i < complete_path.size(); i++) { this->post_path.push_back(complete_path[i]); diff --git a/src/httpserver/http_utils.hpp b/src/httpserver/http_utils.hpp index 2143c5fc..2914f9ed 100644 --- a/src/httpserver/http_utils.hpp +++ b/src/httpserver/http_utils.hpp @@ -240,10 +240,10 @@ class http_utils static const std::string http_post_encoding_form_urlencoded; static const std::string http_post_encoding_multipart_formdata; - static size_t tokenize_url(const std::string&, - std::vector& result, const char separator = '/' + static std::vector tokenize_url(const std::string&, + const char separator = '/' ); - static void standardize_url(const std::string&, std::string& result); + static std::string standardize_url(const std::string&); }; #define COMPARATOR(x, y, op) \ @@ -331,13 +331,9 @@ struct ip_representation * @param maxlen Maxlen of the address (automatically discovered if not passed) * @return string containing the ip address **/ -void get_ip_str(const struct sockaddr *sa, - std::string& result, socklen_t maxlen = 0 -); +std::string get_ip_str(const struct sockaddr *sa, socklen_t maxlen = 0); -std::string get_ip_str_new(const struct sockaddr* sa, - socklen_t maxlen = 0 -); +std::string get_ip_str_new(const struct sockaddr* sa, socklen_t maxlen = 0); /** * Method used to get a port from a sockaddr * @param sa The sockaddr object to find the port from @@ -376,8 +372,6 @@ size_t http_unescape (char *val); char* load_file (const char *filename); -size_t load_file (const char* filename, char** content); - }; }; #endif diff --git a/src/httpserver/string_utilities.hpp b/src/httpserver/string_utilities.hpp index 655daaf7..c3ac948f 100644 --- a/src/httpserver/string_utilities.hpp +++ b/src/httpserver/string_utilities.hpp @@ -39,14 +39,13 @@ namespace string_utilities * @param str The string to turn uppercase * @return a string that is the uppercase version of the previous **/ -void to_upper_copy(const std::string& str, std::string& result); -void to_lower_copy(const std::string& str, std::string& result); -size_t string_split(const std::string& s, - std::vector& result, +std::string to_upper_copy(const std::string& str); +std::string to_lower_copy(const std::string& str); +std::vector string_split(const std::string& s, char sep = ' ', bool collapse = true ); -void regex_replace(const std::string& str, const std::string& pattern, - const std::string& replace_str, std::string& result +std::string regex_replace(const std::string& str, const std::string& pattern, + const std::string& replace_str ); void to_upper(std::string& str); }; diff --git a/src/string_utilities.cpp b/src/string_utilities.cpp index eaa321ea..170f6adf 100644 --- a/src/string_utilities.cpp +++ b/src/string_utilities.cpp @@ -34,14 +34,16 @@ namespace httpserver namespace string_utilities { -void to_upper_copy(const std::string& str, std::string& result) +std::string to_upper_copy(const std::string& str) { - result = str; + std::string result = str; std::transform(result.begin(), result.end(), result.begin(), (int(*)(int)) std::toupper ); + + return result; } void to_upper(std::string& str) @@ -53,41 +55,44 @@ void to_upper(std::string& str) ); } -void to_lower_copy(const std::string& str, std::string& result) +std::string to_lower_copy(const std::string& str) { - result = str; + std::string result = str; std::transform(result.begin(), result.end(), result.begin(), (int(*)(int)) std::tolower ); + + return result; } -size_t string_split( +std::vector string_split( const std::string& s, - std::vector& result, char sep, bool collapse ) { + std::vector result; + std::istringstream buf(s); for(std::string token; getline(buf, token, sep); ) { if((collapse && token != "") || !collapse) result.push_back(token); } - return result.size(); + return result; } -void regex_replace(const std::string& str, +std::string regex_replace(const std::string& str, const std::string& pattern, - const std::string& replace_str, - std::string& result + const std::string& replace_str ) { regex_t preg; regmatch_t substmatch[1]; regcomp(&preg, pattern.c_str(), REG_EXTENDED|REG_ICASE); + std::string result; if ( regexec(&preg, str.c_str(), 1, substmatch, 0) == 0 ) { char ns[substmatch[0].rm_so + 1 + @@ -113,6 +118,8 @@ void regex_replace(const std::string& str, result = std::string((char*)ns); } regfree(&preg); + + return result; } }; diff --git a/src/webserver.cpp b/src/webserver.cpp index 4603b11a..ca85d670 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -775,8 +775,7 @@ void webserver::end_request_construction( connection, MHD_CONNECTION_INFO_CLIENT_ADDRESS ); - std::string ip_str; - get_ip_str(conninfo->client_addr, ip_str); + std::string ip_str = get_ip_str(conninfo->client_addr); mr->dhr->set_requestor(ip_str); mr->dhr->set_requestor_port(get_port(conninfo->client_addr)); if(pass != 0x0) @@ -989,7 +988,7 @@ int webserver::answer_to_connection(void* cls, MHD_Connection* connection, mr->standardized_url = new string(); internal_unescaper((void*) static_cast(cls), (char*) url); - http_utils::standardize_url(url, *mr->standardized_url); + mr->standardized_url = new string(http_utils::standardize_url(url)); bool body = false; diff --git a/test/unit/http_utils_test.cpp b/test/unit/http_utils_test.cpp index e721f4bd..5f917a78 100644 --- a/test/unit/http_utils_test.cpp +++ b/test/unit/http_utils_test.cpp @@ -60,23 +60,23 @@ LT_END_AUTO_TEST(unescape) LT_BEGIN_AUTO_TEST(http_utils_suite, standardize_url) string url = "/", result; - http::http_utils::standardize_url(url, result); + result = http::http_utils::standardize_url(url); LT_CHECK_EQ(result, "/"); url = "/abc/", result = ""; - http::http_utils::standardize_url(url, result); + result = http::http_utils::standardize_url(url); LT_CHECK_EQ(result, "/abc"); url = "/abc", result = ""; - http::http_utils::standardize_url(url, result); + result = http::http_utils::standardize_url(url); LT_CHECK_EQ(result, "/abc"); url = "/abc/pqr/", result = ""; - http::http_utils::standardize_url(url, result); + result = http::http_utils::standardize_url(url); LT_CHECK_EQ(result, "/abc/pqr"); url = "/abc/pqr", result = ""; - http::http_utils::standardize_url(url, result); + result = http::http_utils::standardize_url(url); LT_CHECK_EQ(result, "/abc/pqr"); LT_END_AUTO_TEST(standardize_url) @@ -88,7 +88,7 @@ LT_BEGIN_AUTO_TEST(http_utils_suite, ip_to_str) ip4addr.sin_addr.s_addr = inet_addr("127.0.0.1"); string result = ""; - http::get_ip_str((struct sockaddr *) &ip4addr, result); + result = http::get_ip_str((struct sockaddr *) &ip4addr); LT_CHECK_EQ(result, "127.0.0.1"); LT_END_AUTO_TEST(ip_to_str) From 8686134635012930bf585f623a01c206a5362dde Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sat, 8 Dec 2018 02:31:53 +0000 Subject: [PATCH 277/623] Removed methods with return in args --- src/http_request.cpp | 20 ++-- src/httpserver/http_request.hpp | 174 +++++++++----------------------- 2 files changed, 56 insertions(+), 138 deletions(-) diff --git a/src/http_request.cpp b/src/http_request.cpp index ec61d06a..f9525666 100644 --- a/src/http_request.cpp +++ b/src/http_request.cpp @@ -62,28 +62,24 @@ bool http_request::check_digest_auth( return true; } -size_t http_request::get_headers(std::map& result) const +const std::map http_request::get_headers() const { - result = this->headers; - return result.size(); + return this->headers; } -size_t http_request::get_footers(std::map& result) const +const std::map http_request::get_footers() const { - result = this->footers; - return result.size(); + return this->footers; } -size_t http_request::get_cookies(std::map& result) const +const std::map http_request::get_cookies() const { - result = this->cookies; - return result.size(); + return this->cookies; } -size_t http_request::get_args(std::map& result) const +const std::map http_request::get_args() const { - result = this->args; - return result.size(); + return this->args; } std::ostream &operator<< (std::ostream &os, const http_request &r) diff --git a/src/httpserver/http_request.hpp b/src/httpserver/http_request.hpp index c3f328c6..6777fcdb 100644 --- a/src/httpserver/http_request.hpp +++ b/src/httpserver/http_request.hpp @@ -59,14 +59,7 @@ class http_request { return this->user; } - /** - * Method used to get the username eventually passed through basic authentication. - * @param result string that will be filled with the username - **/ - void get_user(std::string& result) const - { - result = this->user; - } + /** * Method used to get the username extracted from a digest authentication * @return the username @@ -75,14 +68,7 @@ class http_request { return this->digested_user; } - /** - * Method used to get the username extracted from a digest authentication - * @param result string that will be filled with the username - **/ - void get_digested_user(std::string& result) const - { - result = this->digested_user; - } + /** * Method used to get the password eventually passed through basic authentication. * @return string representation of the password. @@ -91,14 +77,7 @@ class http_request { return this->pass; } - /** - * Method used to get the password eventually passed through basic authentication. - * @param result string that will be filled with the password. - **/ - void get_pass(std::string& result) const - { - result = this->pass; - } + /** * Method used to get the path requested * @return string representing the path requested. @@ -107,14 +86,7 @@ class http_request { return this->path; } - /** - * Method used to get the path requested - * @param result string that will be filled with the path. - **/ - void get_path(std::string& result) const - { - result = this->path; - } + /** * Method used to get all pieces of the path requested; considering an url splitted by '/'. * @return a vector of strings containing all pieces @@ -123,16 +95,7 @@ class http_request { return this->post_path; } - /** - * Method used to get all pieces of the path requested; considering an url splitted by '/'. - * @param result vector of strings containing the path - * @return the size of the vector filled - **/ - size_t get_path_pieces(std::vector& result) const - { - result = this->post_path; - return result.size(); - } + /** * Method used to obtain the size of path in terms of pieces; considering an url splitted by '/'. * @return an integer representing the number of pieces @@ -141,6 +104,7 @@ class http_request { return this->post_path.size(); } + /** * Method used to obtain a specified piece of the path; considering an url splitted by '/'. * @param index the index of the piece selected @@ -152,25 +116,7 @@ class http_request return this->post_path[index]; return ""; } - /** - * Method used to obtain a specified piece of the path; considering an url splitted by '/'. - * @param index the index of the piece selected - * @param result a string that will be filled with the piece found - * @return the length of the piece found - **/ - size_t get_path_piece(int index, std::string& result) const - { - if(((int)(this->post_path.size())) > index) - { - result = this->post_path[index]; - return result.size(); - } - else - { - result = ""; - return result.size(); - } - } + /** * Method used to get the METHOD used to make the request. * @return string representing the method. @@ -179,38 +125,35 @@ class http_request { return this->method; } - /** - * Method used to get the METHOD used to make the request. - * @param result string that will be filled with the method. - **/ - void get_method(std::string& result) const - { - result = this->method; - } + /** * Method used to get all headers passed with the request. * @param result a map > that will be filled with all headers * @result the size of the map **/ - size_t get_headers(std::map& result) const; + const std::map get_headers() const; + /** * Method used to get all footers passed with the request. * @param result a map > that will be filled with all footers * @result the size of the map **/ - size_t get_footers(std::map& result) const; + const std::map get_footers() const; + /** * Method used to get all cookies passed with the request. * @param result a map > that will be filled with all cookies * @result the size of the map **/ - size_t get_cookies(std::map& result) const; + const std::map get_cookies() const; + /** * Method used to get all args passed with the request. * @param result a map > that will be filled with all args * @result the size of the map **/ - size_t get_args(std::map& result) const; + const std::map get_args() const; + /** * Method used to get a specific header passed with the request. * @param key the specific header to get the value from @@ -225,15 +168,7 @@ class http_request else return ""; } - void get_header(const std::string& key, std::string& result) const - { - std::map::const_iterator it = - this->headers.find(key); - if(it != this->headers.end()) - result = it->second; - else - result = ""; - } + const std::string get_cookie(const std::string& key) const { std::map::const_iterator it = @@ -243,15 +178,7 @@ class http_request else return ""; } - void get_cookie(const std::string& key, std::string& result) const - { - std::map::const_iterator it = - this->cookies.find(key); - if(it != this->cookies.end()) - result = it->second; - else - result = ""; - } + /** * Method used to get a specific footer passed with the request. * @param key the specific footer to get the value from @@ -266,15 +193,7 @@ class http_request else return ""; } - void get_footer(const std::string& key, std::string& result) const - { - std::map::const_iterator it = - this->footers.find(key); - if(it != this->footers.end()) - result = it->second; - else - result = ""; - } + /** * Method used to get a specific argument passed with the request. * @param ket the specific argument to get the value from @@ -289,15 +208,7 @@ class http_request else return ""; } - void get_arg(const std::string& key, std::string& result) const - { - std::map::const_iterator it = - this->args.find(key); - if(it != this->args.end()) - result = it->second; - else - result = ""; - } + /** * Method used to get the content of the request. * @return the content in string representation @@ -306,10 +217,7 @@ class http_request { return this->content; } - void get_content(std::string& result) const - { - result = this->content; - } + /** * Method to check whether the size of the content reached or exceeded content_size_limit. * @return boolean @@ -326,10 +234,7 @@ class http_request { return this->querystring; } - void get_querystring(std::string& result) const - { - result = this->querystring; - } + /** * Method used to get the version of the request. * @return the version in string representation @@ -338,10 +243,7 @@ class http_request { return this->version; } - void get_version(std::string& result) const - { - result = this->version; - } + /** * Method used to get the requestor. * @return the requestor @@ -350,10 +252,7 @@ class http_request { return this->requestor; } - void get_requestor(std::string& result) const - { - result = this->requestor; - } + /** * Method used to get the requestor port used. * @return the requestor port @@ -362,12 +261,13 @@ class http_request { return this->requestor_port; } + bool check_digest_auth(const std::string& realm, const std::string& password, int nonce_timeout, bool& reload_nonce ) const; - friend std::ostream &operator<< (std::ostream &os, const http_request &r); + friend std::ostream &operator<< (std::ostream &os, const http_request &r); private: /** * Default constructor of the class. It is a specific responsibility of apis to initialize this type of objects. @@ -376,6 +276,7 @@ class http_request content(""), content_size_limit(static_cast(-1)) { } + /** * Copy constructor. * @param b http_request b to copy attributes from. @@ -422,6 +323,7 @@ class http_request { this->underlying_connection = conn; } + /** * Method used to set an header value by key. * @param key The name identifying the header @@ -431,6 +333,7 @@ class http_request { this->headers[key] = value; } + /** * Method used to set a footer value by key. * @param key The name identifying the footer @@ -440,6 +343,7 @@ class http_request { this->footers[key] = value; } + /** * Method used to set a cookie value by key. * @param key The name identifying the cookie @@ -449,6 +353,7 @@ class http_request { this->cookies[key] = value; } + /** * Method used to set an argument value by key. * @param key The name identifying the argument @@ -458,6 +363,7 @@ class http_request { this->args[key] = value.substr(0,content_size_limit); } + /** * Method used to set an argument value by key. * @param key The name identifying the argument @@ -469,6 +375,7 @@ class http_request this->args[key] = std::string(value, std::min(size, content_size_limit)); } + /** * Method used to set the content of the request * @param content The content to set. @@ -477,6 +384,7 @@ class http_request { this->content = content.substr(0,content_size_limit); } + /** * Method used to set the maximum size of the content * @param content_size_limit The limit on the maximum size of the content and arg's. @@ -485,6 +393,7 @@ class http_request { this->content_size_limit = content_size_limit; } + /** * Method used to append content to the request preserving the previous inserted content * @param content The content to append. @@ -498,6 +407,7 @@ class http_request this->content.resize (content_size_limit); } } + /** * Method used to set the path requested. * @param path The path searched by the request. @@ -511,11 +421,13 @@ class http_request this->post_path.push_back(complete_path[i]); } } + /** * Method used to set the request METHOD * @param method The method to set for the request **/ void set_method(const std::string& method); + /** * Method used to set the request http version (ie http 1.1) * @param version The version to set in form of string @@ -524,6 +436,7 @@ class http_request { this->version = version; } + /** * Method used to set the requestor * @param requestor The requestor to set @@ -532,6 +445,7 @@ class http_request { this->requestor = requestor; } + /** * Method used to set the requestor port * @param requestor The requestor port to set @@ -540,6 +454,7 @@ class http_request { this->requestor_port = requestor_port; } + /** * Method used to remove an header previously inserted * @param key The key identifying the header to remove. @@ -548,6 +463,7 @@ class http_request { this->headers.erase(key); } + /** * Method used to set all headers of the request. * @param headers The headers key-value map to set for the request. @@ -558,6 +474,7 @@ class http_request for(it = headers.begin(); it != headers.end(); ++it) this->headers[it->first] = it->second; } + /** * Method used to set all footers of the request. * @param footers The footers key-value map to set for the request. @@ -568,6 +485,7 @@ class http_request for(it = footers.begin(); it != footers.end(); ++it) this->footers[it->first] = it->second; } + /** * Method used to set all cookies of the request. * @param cookies The cookies key-value map to set for the request. @@ -578,6 +496,7 @@ class http_request for(it = cookies.begin(); it != cookies.end(); ++it) this->cookies[it->first] = it->second; } + /** * Method used to set all arguments of the request. * @param args The args key-value map to set for the request. @@ -588,6 +507,7 @@ class http_request for(it = args.begin(); it != args.end(); ++it) this->args[it->first] = it->second.substr(0,content_size_limit); } + /** * Method used to set the username of the request. * @param user The username to set. @@ -596,10 +516,12 @@ class http_request { this->user = user; } + void set_digested_user(const std::string& digested_user) { this->digested_user = digested_user; } + /** * Method used to set the password of the request. * @param pass The password to set. From 27b230ab77e730eb1850342e5bcd9489019372aa Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sat, 8 Dec 2018 02:34:19 +0000 Subject: [PATCH 278/623] Change methods to declare constness of returns --- src/httpserver/string_utilities.hpp | 8 ++++---- src/string_utilities.cpp | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/httpserver/string_utilities.hpp b/src/httpserver/string_utilities.hpp index c3ac948f..9a3dbd2b 100644 --- a/src/httpserver/string_utilities.hpp +++ b/src/httpserver/string_utilities.hpp @@ -39,12 +39,12 @@ namespace string_utilities * @param str The string to turn uppercase * @return a string that is the uppercase version of the previous **/ -std::string to_upper_copy(const std::string& str); -std::string to_lower_copy(const std::string& str); -std::vector string_split(const std::string& s, +const std::string to_upper_copy(const std::string& str); +const std::string to_lower_copy(const std::string& str); +const std::vector string_split(const std::string& s, char sep = ' ', bool collapse = true ); -std::string regex_replace(const std::string& str, const std::string& pattern, +const std::string regex_replace(const std::string& str, const std::string& pattern, const std::string& replace_str ); void to_upper(std::string& str); diff --git a/src/string_utilities.cpp b/src/string_utilities.cpp index 170f6adf..b03d4733 100644 --- a/src/string_utilities.cpp +++ b/src/string_utilities.cpp @@ -34,7 +34,7 @@ namespace httpserver namespace string_utilities { -std::string to_upper_copy(const std::string& str) +const std::string to_upper_copy(const std::string& str) { std::string result = str; std::transform(result.begin(), @@ -55,7 +55,7 @@ void to_upper(std::string& str) ); } -std::string to_lower_copy(const std::string& str) +const std::string to_lower_copy(const std::string& str) { std::string result = str; std::transform(result.begin(), @@ -67,7 +67,7 @@ std::string to_lower_copy(const std::string& str) return result; } -std::vector string_split( +const std::vector string_split( const std::string& s, char sep, bool collapse @@ -84,7 +84,7 @@ std::vector string_split( return result; } -std::string regex_replace(const std::string& str, +const std::string regex_replace(const std::string& str, const std::string& pattern, const std::string& replace_str ) From 5ef3d11a81f2075a9bf381a935f242db91307b64 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sat, 8 Dec 2018 03:41:25 +0000 Subject: [PATCH 279/623] Add tests for string_utilities --- test/Makefile.am | 3 +- test/unit/string_utilities_test.cpp | 102 ++++++++++++++++++++++++++++ 2 files changed, 104 insertions(+), 1 deletion(-) create mode 100644 test/unit/string_utilities_test.cpp diff --git a/test/Makefile.am b/test/Makefile.am index cb92d0d4..3b6b112e 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -19,13 +19,14 @@ LDADD = $(top_builddir)/src/libhttpserver.la AM_CPPFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/src/httpserver/ METASOURCES = AUTO -check_PROGRAMS = basic http_utils threaded +check_PROGRAMS = basic http_utils threaded string_utilities MOSTLYCLEANFILES = *.gcda *.gcno *.gcov basic_SOURCES = integ/basic.cpp threaded_SOURCES = integ/threaded.cpp http_utils_SOURCES = unit/http_utils_test.cpp +string_utilities_SOURCES = unit/string_utilities_test.cpp noinst_HEADERS = littletest.hpp AM_CXXFLAGS += -lcurl -Wall -fPIC diff --git a/test/unit/string_utilities_test.cpp b/test/unit/string_utilities_test.cpp new file mode 100644 index 00000000..86aaf3da --- /dev/null +++ b/test/unit/string_utilities_test.cpp @@ -0,0 +1,102 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#include "littletest.hpp" +#include "string_utilities.hpp" + +#include + +using namespace httpserver; +using namespace std; + +LT_BEGIN_SUITE(string_utilities_suite) + void set_up() + { + } + + void tear_down() + { + } +LT_END_SUITE(string_utilities_suite) + +LT_BEGIN_AUTO_TEST(string_utilities_suite, to_upper_copy) + LT_CHECK_EQ(string_utilities::to_upper_copy("test message"), string("TEST MESSAGE")); + LT_CHECK_EQ(string_utilities::to_upper_copy("tEsT mEssAge 245&$"), string("TEST MESSAGE 245&$")); +LT_END_AUTO_TEST(to_upper_copy) + +LT_BEGIN_AUTO_TEST(string_utilities_suite, to_upper) + string value = "test message"; + string_utilities::to_upper(value); + LT_CHECK_EQ(value, string("TEST MESSAGE")); + + value = "tEsT mEssAge 245&$"; + string_utilities::to_upper(value); + LT_CHECK_EQ(value, string("TEST MESSAGE 245&$")); +LT_END_AUTO_TEST(to_upper) + +LT_BEGIN_AUTO_TEST(string_utilities_suite, to_lower_copy) + LT_CHECK_EQ(string_utilities::to_lower_copy("TEST MESSAGE"), string("test message")); + LT_CHECK_EQ(string_utilities::to_lower_copy("tEsT mEssAge 245&$"), string("test message 245&$")); +LT_END_AUTO_TEST(to_lower_copy) + +LT_BEGIN_AUTO_TEST(string_utilities_suite, split_string) + string value = "test this message here"; + string expected_arr[] = { "test", "this", "message", "here" }; + vector expected(expected_arr, expected_arr + sizeof(expected_arr) / sizeof(expected_arr[0])); + vector actual = string_utilities::string_split(value, ' ', false); + + LT_CHECK_COLLECTIONS_EQ(expected.begin(), expected.end(), actual.begin()); +LT_END_AUTO_TEST(split_string) + +LT_BEGIN_AUTO_TEST(string_utilities_suite, split_string_multiple_spaces) + string value = "test this message here"; + string expected_arr[] = { "test", "", "this", "", "message", "", "here" }; + vector expected(expected_arr, expected_arr + sizeof(expected_arr) / sizeof(expected_arr[0])); + vector actual = string_utilities::string_split(value, ' ', false); + + LT_CHECK_COLLECTIONS_EQ(expected.begin(), expected.end(), actual.begin()); +LT_END_AUTO_TEST(split_string_multiple_spaces) + +LT_BEGIN_AUTO_TEST(string_utilities_suite, split_string_multiple_spaces_collapse) + string value = "test this message here"; + string expected_arr[] = { "test", "this", "message", "here" }; + vector expected(expected_arr, expected_arr + sizeof(expected_arr) / sizeof(expected_arr[0])); + vector actual = string_utilities::string_split(value, ' ', true); + + LT_CHECK_COLLECTIONS_EQ(expected.begin(), expected.end(), actual.begin()); +LT_END_AUTO_TEST(split_string_multiple_spaces_collapse) + +LT_BEGIN_AUTO_TEST(string_utilities_suite, split_string_end_space) + string value = "test this message here "; + string expected_arr[] = { "test", "this", "message", "here" }; + vector expected(expected_arr, expected_arr + sizeof(expected_arr) / sizeof(expected_arr[0])); + vector actual = string_utilities::string_split(value, ' ', false); + + LT_CHECK_COLLECTIONS_EQ(expected.begin(), expected.end(), actual.begin()); +LT_END_AUTO_TEST(split_string_end_space) + +LT_BEGIN_AUTO_TEST(string_utilities_suite, regex_replace) + LT_CHECK_EQ(string_utilities::regex_replace("test///message", "(\\/)+", "/"), "test/message"); + LT_CHECK_EQ(string_utilities::regex_replace("test 1234 message", "([0-9])+", "bob"), "test bob message"); +LT_END_AUTO_TEST(regex_replace) + +LT_BEGIN_AUTO_TEST_ENV() + AUTORUN_TESTS() +LT_END_AUTO_TEST_ENV() From 1687cfa042c7d426a902c1d2fc4ddb1e1a45bf39 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sat, 8 Dec 2018 21:54:07 +0000 Subject: [PATCH 280/623] Updated copyright --- src/Makefile.am | 2 +- src/details/comet_manager.cpp | 2 +- src/http_endpoint.cpp | 2 +- src/http_request.cpp | 2 +- src/http_resource.cpp | 2 +- src/http_response.cpp | 2 +- src/http_utils.cpp | 2 +- src/httpserver.hpp | 2 +- src/httpserver/binders.hpp | 2 +- src/httpserver/create_webserver.hpp | 2 +- src/httpserver/details/cache_entry.hpp | 2 +- src/httpserver/details/comet_manager.hpp | 2 +- src/httpserver/details/http_response_ptr.hpp | 2 +- src/httpserver/details/modded_request.hpp | 2 +- src/httpserver/http_endpoint.hpp | 2 +- src/httpserver/http_request.hpp | 2 +- src/httpserver/http_resource.hpp | 2 +- src/httpserver/http_response.hpp | 2 +- src/httpserver/http_response_builder.hpp | 2 +- src/httpserver/http_utils.hpp | 2 +- src/httpserver/string_utilities.hpp | 2 +- src/httpserver/webserver.hpp | 2 +- src/string_utilities.cpp | 2 +- src/webserver.cpp | 2 +- test/Makefile.am | 2 +- test/integ/basic.cpp | 2 +- test/integ/threaded.cpp | 2 +- test/unit/http_utils_test.cpp | 2 +- test/unit/string_utilities_test.cpp | 2 +- 29 files changed, 29 insertions(+), 29 deletions(-) diff --git a/src/Makefile.am b/src/Makefile.am index ce693016..8b0a0893 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,6 +1,6 @@ # # This file is part of libhttpserver -# Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino +# Copyright (C) 2011-2019 Sebastiano Merlino # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public diff --git a/src/details/comet_manager.cpp b/src/details/comet_manager.cpp index 3443b618..0a1fbd47 100644 --- a/src/details/comet_manager.cpp +++ b/src/details/comet_manager.cpp @@ -1,6 +1,6 @@ /* This file is part of libhttpserver - Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino + Copyright (C) 2011-2019 Sebastiano Merlino This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public diff --git a/src/http_endpoint.cpp b/src/http_endpoint.cpp index 2d66b3df..c6359820 100644 --- a/src/http_endpoint.cpp +++ b/src/http_endpoint.cpp @@ -1,6 +1,6 @@ /* This file is part of libhttpserver - Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino + Copyright (C) 2011-2019 Sebastiano Merlino This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public diff --git a/src/http_request.cpp b/src/http_request.cpp index f9525666..c2aae54a 100644 --- a/src/http_request.cpp +++ b/src/http_request.cpp @@ -1,6 +1,6 @@ /* This file is part of libhttpserver - Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino + Copyright (C) 2011-2019 Sebastiano Merlino This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public diff --git a/src/http_resource.cpp b/src/http_resource.cpp index 93e481e3..19c3279f 100644 --- a/src/http_resource.cpp +++ b/src/http_resource.cpp @@ -1,6 +1,6 @@ /* This file is part of libhttpserver - Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino + Copyright (C) 2011-2019 Sebastiano Merlino This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public diff --git a/src/http_response.cpp b/src/http_response.cpp index 6bae7995..83533808 100644 --- a/src/http_response.cpp +++ b/src/http_response.cpp @@ -1,6 +1,6 @@ /* This file is part of libhttpserver - Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino + Copyright (C) 2011-2019 Sebastiano Merlino This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public diff --git a/src/http_utils.cpp b/src/http_utils.cpp index 4b3aac84..a9ed665a 100644 --- a/src/http_utils.cpp +++ b/src/http_utils.cpp @@ -1,6 +1,6 @@ /* This file is part of libhttpserver - Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino + Copyright (C) 2011-2019 Sebastiano Merlino This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public diff --git a/src/httpserver.hpp b/src/httpserver.hpp index 7028f68a..2174240f 100644 --- a/src/httpserver.hpp +++ b/src/httpserver.hpp @@ -1,6 +1,6 @@ /* This file is part of libhttpserver - Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino + Copyright (C) 2011-2019 Sebastiano Merlino This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public diff --git a/src/httpserver/binders.hpp b/src/httpserver/binders.hpp index c1ce3007..83bae04b 100644 --- a/src/httpserver/binders.hpp +++ b/src/httpserver/binders.hpp @@ -1,6 +1,6 @@ /* This file is part of libhttpserver - Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino + Copyright (C) 2011-2019 Sebastiano Merlino This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public diff --git a/src/httpserver/create_webserver.hpp b/src/httpserver/create_webserver.hpp index 9cdf81a7..5476a95d 100644 --- a/src/httpserver/create_webserver.hpp +++ b/src/httpserver/create_webserver.hpp @@ -1,6 +1,6 @@ /* This file is part of libhttpserver - Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino + Copyright (C) 2011-2019 Sebastiano Merlino This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public diff --git a/src/httpserver/details/cache_entry.hpp b/src/httpserver/details/cache_entry.hpp index c1fc560a..e7fdc03d 100644 --- a/src/httpserver/details/cache_entry.hpp +++ b/src/httpserver/details/cache_entry.hpp @@ -1,6 +1,6 @@ /* This file is part of libhttpserver - Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino + Copyright (C) 2011-2019 Sebastiano Merlino This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public diff --git a/src/httpserver/details/comet_manager.hpp b/src/httpserver/details/comet_manager.hpp index a1762276..067a4279 100644 --- a/src/httpserver/details/comet_manager.hpp +++ b/src/httpserver/details/comet_manager.hpp @@ -1,6 +1,6 @@ /* This file is part of libhttpserver - Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino + Copyright (C) 2011-2019 Sebastiano Merlino This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public diff --git a/src/httpserver/details/http_response_ptr.hpp b/src/httpserver/details/http_response_ptr.hpp index b5fa0c43..20059cdd 100644 --- a/src/httpserver/details/http_response_ptr.hpp +++ b/src/httpserver/details/http_response_ptr.hpp @@ -1,6 +1,6 @@ /* This file is part of libhttpserver - Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino + Copyright (C) 2011-2019 Sebastiano Merlino This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public diff --git a/src/httpserver/details/modded_request.hpp b/src/httpserver/details/modded_request.hpp index f47f9a5d..a567250c 100644 --- a/src/httpserver/details/modded_request.hpp +++ b/src/httpserver/details/modded_request.hpp @@ -1,6 +1,6 @@ /* This file is part of libhttpserver - Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino + Copyright (C) 2011-2019 Sebastiano Merlino This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public diff --git a/src/httpserver/http_endpoint.hpp b/src/httpserver/http_endpoint.hpp index 35d13464..7a845047 100644 --- a/src/httpserver/http_endpoint.hpp +++ b/src/httpserver/http_endpoint.hpp @@ -1,6 +1,6 @@ /* This file is part of libhttpserver - Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino + Copyright (C) 2011-2019 Sebastiano Merlino This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public diff --git a/src/httpserver/http_request.hpp b/src/httpserver/http_request.hpp index 6777fcdb..ab3901b1 100644 --- a/src/httpserver/http_request.hpp +++ b/src/httpserver/http_request.hpp @@ -1,6 +1,6 @@ /* This file is part of libhttpserver - Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino + Copyright (C) 2011-2019 Sebastiano Merlino This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public diff --git a/src/httpserver/http_resource.hpp b/src/httpserver/http_resource.hpp index 6465f5c3..7cbff849 100644 --- a/src/httpserver/http_resource.hpp +++ b/src/httpserver/http_resource.hpp @@ -1,6 +1,6 @@ /* This file is part of libhttpserver - Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino + Copyright (C) 2011-2019 Sebastiano Merlino This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public diff --git a/src/httpserver/http_response.hpp b/src/httpserver/http_response.hpp index c3807a7c..ac5c6b50 100644 --- a/src/httpserver/http_response.hpp +++ b/src/httpserver/http_response.hpp @@ -1,6 +1,6 @@ /* This file is part of libhttpserver - Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino + Copyright (C) 2011-2019 Sebastiano Merlino This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public diff --git a/src/httpserver/http_response_builder.hpp b/src/httpserver/http_response_builder.hpp index e1bc9108..4b7b8cb9 100644 --- a/src/httpserver/http_response_builder.hpp +++ b/src/httpserver/http_response_builder.hpp @@ -1,6 +1,6 @@ /* This file is part of libhttpserver - Copyright (C) 2011, 2012, 2013, 2014 Sebastiano Merlino + Copyright (C) 2011-2019 Sebastiano Merlino This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public diff --git a/src/httpserver/http_utils.hpp b/src/httpserver/http_utils.hpp index 2914f9ed..6119204e 100644 --- a/src/httpserver/http_utils.hpp +++ b/src/httpserver/http_utils.hpp @@ -1,6 +1,6 @@ /* This file is part of libhttpserver - Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino + Copyright (C) 2011-2019 Sebastiano Merlino This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public diff --git a/src/httpserver/string_utilities.hpp b/src/httpserver/string_utilities.hpp index 9a3dbd2b..adc248c2 100644 --- a/src/httpserver/string_utilities.hpp +++ b/src/httpserver/string_utilities.hpp @@ -1,6 +1,6 @@ /* This file is part of libhttpserver - Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino + Copyright (C) 2011-2019 Sebastiano Merlino This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public diff --git a/src/httpserver/webserver.hpp b/src/httpserver/webserver.hpp index f5b9acf0..5fc45017 100644 --- a/src/httpserver/webserver.hpp +++ b/src/httpserver/webserver.hpp @@ -1,6 +1,6 @@ /* This file is part of libhttpserver - Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino + Copyright (C) 2011-2019 Sebastiano Merlino This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public diff --git a/src/string_utilities.cpp b/src/string_utilities.cpp index b03d4733..30f28c73 100644 --- a/src/string_utilities.cpp +++ b/src/string_utilities.cpp @@ -1,6 +1,6 @@ /* This file is part of libhttpserver - Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino + Copyright (C) 2011-2019 Sebastiano Merlino This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public diff --git a/src/webserver.cpp b/src/webserver.cpp index ca85d670..12539ce1 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -1,6 +1,6 @@ /* This file is part of libhttpserver - Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino + Copyright (C) 2011-2019 Sebastiano Merlino This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public diff --git a/test/Makefile.am b/test/Makefile.am index 3b6b112e..cef3ed09 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -1,6 +1,6 @@ # # This file is part of libhttpserver -# Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino +# Copyright (C) 2011-2019 Sebastiano Merlino # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public diff --git a/test/integ/basic.cpp b/test/integ/basic.cpp index 61307281..80969202 100644 --- a/test/integ/basic.cpp +++ b/test/integ/basic.cpp @@ -1,6 +1,6 @@ /* This file is part of libhttpserver - Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino + Copyright (C) 2011-2019 Sebastiano Merlino This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public diff --git a/test/integ/threaded.cpp b/test/integ/threaded.cpp index 575e5ab5..ad03b721 100644 --- a/test/integ/threaded.cpp +++ b/test/integ/threaded.cpp @@ -1,6 +1,6 @@ /* This file is part of libhttpserver - Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino + Copyright (C) 2011-2019 Sebastiano Merlino This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public diff --git a/test/unit/http_utils_test.cpp b/test/unit/http_utils_test.cpp index 5f917a78..f6e60236 100644 --- a/test/unit/http_utils_test.cpp +++ b/test/unit/http_utils_test.cpp @@ -1,6 +1,6 @@ /* This file is part of libhttpserver - Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino + Copyright (C) 2011-2019 Sebastiano Merlino This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public diff --git a/test/unit/string_utilities_test.cpp b/test/unit/string_utilities_test.cpp index 86aaf3da..49913e67 100644 --- a/test/unit/string_utilities_test.cpp +++ b/test/unit/string_utilities_test.cpp @@ -1,6 +1,6 @@ /* This file is part of libhttpserver - Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino + Copyright (C) 2011-2019 Sebastiano Merlino This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public From fc37c08ed1792ea35dc4f17be8c4672f46935f8c Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 23 Dec 2018 07:01:22 +0000 Subject: [PATCH 281/623] Fixed logic to get ip and port. Added tests to verify the logic. --- src/http_utils.cpp | 17 +++++++++-------- test/unit/http_utils_test.cpp | 19 +++++++++++++++++-- 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/src/http_utils.cpp b/src/http_utils.cpp index a9ed665a..52e5ff4d 100644 --- a/src/http_utils.cpp +++ b/src/http_utils.cpp @@ -30,6 +30,7 @@ #else #include #include +#include #endif #include #include @@ -246,15 +247,15 @@ std::string get_ip_str( if(sa) { - int addrlen = sizeof(sockaddr_in); + char to_ret[NI_MAXHOST]; if (AF_INET6 == sa->sa_family) { - addrlen = sizeof(sockaddr_in6); + inet_ntop(AF_INET6, &(((sockaddr_in6*) sa)->sin6_addr), to_ret, INET6_ADDRSTRLEN); + result = to_ret; } - - char to_ret[NI_MAXHOST]; - if (0 == getnameinfo(sa, addrlen, to_ret, NI_MAXHOST, NULL, 0, NI_NUMERICHOST)) + else { + inet_ntop(AF_INET, &(((sockaddr_in*) sa)->sin_addr), to_ret, INET_ADDRSTRLEN); result = to_ret; } } @@ -269,9 +270,9 @@ unsigned short get_port(const struct sockaddr* sa) switch(sa->sa_family) { case AF_INET: - return ((struct sockaddr_in *)sa)->sin_port; + return ((struct sockaddr_in*) sa)->sin_port; case AF_INET6: - return ((struct sockaddr_in *)sa)->sin_port; + return ((struct sockaddr_in6*) sa)->sin6_port; default: return 0; } @@ -279,7 +280,7 @@ unsigned short get_port(const struct sockaddr* sa) return 0; } -size_t http_unescape (char *val) +size_t http_unescape(char *val) { char *rpos = val; char *wpos = val; diff --git a/test/unit/http_utils_test.cpp b/test/unit/http_utils_test.cpp index f6e60236..cf417bdc 100644 --- a/test/unit/http_utils_test.cpp +++ b/test/unit/http_utils_test.cpp @@ -87,12 +87,27 @@ LT_BEGIN_AUTO_TEST(http_utils_suite, ip_to_str) ip4addr.sin_port = htons(3490); ip4addr.sin_addr.s_addr = inet_addr("127.0.0.1"); - string result = ""; - result = http::get_ip_str((struct sockaddr *) &ip4addr); + string result = http::get_ip_str((struct sockaddr*) &ip4addr); + unsigned short port = http::get_port((struct sockaddr*) &ip4addr); LT_CHECK_EQ(result, "127.0.0.1"); + LT_CHECK_EQ(port, htons(3490)); LT_END_AUTO_TEST(ip_to_str) +LT_BEGIN_AUTO_TEST(http_utils_suite, ip_to_str6) + struct sockaddr_in6 ip6addr; + + ip6addr.sin6_family = AF_INET6; + ip6addr.sin6_port = htons(3490); + inet_pton(AF_INET6, "2001:db8:8714:3a90::12", &(ip6addr.sin6_addr)); + + string result = http::get_ip_str((struct sockaddr *) &ip6addr); + unsigned short port = http::get_port((struct sockaddr*) &ip6addr); + + LT_CHECK_EQ(result, "2001:db8:8714:3a90::12"); + LT_CHECK_EQ(port, htons(3490)); +LT_END_AUTO_TEST(ip_to_str6) + LT_BEGIN_AUTO_TEST_ENV() AUTORUN_TESTS() LT_END_AUTO_TEST_ENV() From ce1bb5682014b0024c363562f20b26847487e485 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 23 Dec 2018 07:29:38 +0000 Subject: [PATCH 282/623] Throwing on invalid ip address --- src/http_utils.cpp | 52 +++++++++++++++++------------------ test/unit/http_utils_test.cpp | 28 +++++++++++++++++++ 2 files changed, 54 insertions(+), 26 deletions(-) diff --git a/src/http_utils.cpp b/src/http_utils.cpp index 52e5ff4d..faf828d4 100644 --- a/src/http_utils.cpp +++ b/src/http_utils.cpp @@ -243,41 +243,41 @@ std::string get_ip_str( socklen_t maxlen ) { - std::string result; + if (!sa) throw new std::invalid_argument("socket pointer is null"); - if(sa) + char to_ret[NI_MAXHOST]; + if (AF_INET6 == sa->sa_family) { - char to_ret[NI_MAXHOST]; - if (AF_INET6 == sa->sa_family) - { - inet_ntop(AF_INET6, &(((sockaddr_in6*) sa)->sin6_addr), to_ret, INET6_ADDRSTRLEN); - result = to_ret; - } - else - { - inet_ntop(AF_INET, &(((sockaddr_in*) sa)->sin_addr), to_ret, INET_ADDRSTRLEN); - result = to_ret; - } + inet_ntop(AF_INET6, &(((sockaddr_in6*) sa)->sin6_addr), to_ret, INET6_ADDRSTRLEN); + return to_ret; + } + else if (AF_INET == sa->sa_family) + { + inet_ntop(AF_INET, &(((sockaddr_in*) sa)->sin_addr), to_ret, INET_ADDRSTRLEN); + return to_ret; + } + else + { + throw new std::invalid_argument("IP family must be either AF_INET or AF_INET6"); } - - return result; } unsigned short get_port(const struct sockaddr* sa) { - if(sa) + if (!sa) throw new std::invalid_argument("socket pointer is null"); + + if (sa->sa_family == AF_INET) { - switch(sa->sa_family) - { - case AF_INET: - return ((struct sockaddr_in*) sa)->sin_port; - case AF_INET6: - return ((struct sockaddr_in6*) sa)->sin6_port; - default: - return 0; - } + return ((struct sockaddr_in*) sa)->sin_port; + } + else if (sa->sa_family == AF_INET6) + { + return ((struct sockaddr_in6*) sa)->sin6_port; + } + else + { + throw new std::invalid_argument("IP family must be either AF_INET or AF_INET6"); } - return 0; } size_t http_unescape(char *val) diff --git a/test/unit/http_utils_test.cpp b/test/unit/http_utils_test.cpp index cf417bdc..b77f422c 100644 --- a/test/unit/http_utils_test.cpp +++ b/test/unit/http_utils_test.cpp @@ -108,6 +108,34 @@ LT_BEGIN_AUTO_TEST(http_utils_suite, ip_to_str6) LT_CHECK_EQ(port, htons(3490)); LT_END_AUTO_TEST(ip_to_str6) +LT_BEGIN_AUTO_TEST(http_utils_suite, ip_to_str_invalid_family) + struct sockaddr_in ip4addr; + + ip4addr.sin_family = 55; + ip4addr.sin_port = htons(3490); + ip4addr.sin_addr.s_addr = inet_addr("127.0.0.1"); + + LT_CHECK_THROW(http::get_ip_str((struct sockaddr*) &ip4addr)); +LT_END_AUTO_TEST(ip_to_str_invalid_family) + +LT_BEGIN_AUTO_TEST(http_utils_suite, ip_to_str_null) + LT_CHECK_THROW(http::get_ip_str((struct sockaddr*) 0x0)); +LT_END_AUTO_TEST(ip_to_str_null) + +LT_BEGIN_AUTO_TEST(http_utils_suite, get_port_invalid_family) + struct sockaddr_in ip4addr; + + ip4addr.sin_family = 55; + ip4addr.sin_port = htons(3490); + ip4addr.sin_addr.s_addr = inet_addr("127.0.0.1"); + + LT_CHECK_THROW(http::get_port((struct sockaddr*) &ip4addr)); +LT_END_AUTO_TEST(get_port_invalid_family) + +LT_BEGIN_AUTO_TEST(http_utils_suite, get_port_null) + LT_CHECK_THROW(http::get_port((struct sockaddr*) 0x0)); +LT_END_AUTO_TEST(get_port_null) + LT_BEGIN_AUTO_TEST_ENV() AUTORUN_TESTS() LT_END_AUTO_TEST_ENV() From 09df543730cb2d4e76d46339a78848060aa32b72 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 23 Dec 2018 07:44:09 +0000 Subject: [PATCH 283/623] Adding tests --- src/http_utils.cpp | 8 ++++---- test/unit/http_utils_test.cpp | 12 ++++++++++++ 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/http_utils.cpp b/src/http_utils.cpp index faf828d4..a38b3461 100644 --- a/src/http_utils.cpp +++ b/src/http_utils.cpp @@ -243,7 +243,7 @@ std::string get_ip_str( socklen_t maxlen ) { - if (!sa) throw new std::invalid_argument("socket pointer is null"); + if (!sa) throw std::invalid_argument("socket pointer is null"); char to_ret[NI_MAXHOST]; if (AF_INET6 == sa->sa_family) @@ -258,13 +258,13 @@ std::string get_ip_str( } else { - throw new std::invalid_argument("IP family must be either AF_INET or AF_INET6"); + throw std::invalid_argument("IP family must be either AF_INET or AF_INET6"); } } unsigned short get_port(const struct sockaddr* sa) { - if (!sa) throw new std::invalid_argument("socket pointer is null"); + if (!sa) throw std::invalid_argument("socket pointer is null"); if (sa->sa_family == AF_INET) { @@ -276,7 +276,7 @@ unsigned short get_port(const struct sockaddr* sa) } else { - throw new std::invalid_argument("IP family must be either AF_INET or AF_INET6"); + throw std::invalid_argument("IP family must be either AF_INET or AF_INET6"); } } diff --git a/test/unit/http_utils_test.cpp b/test/unit/http_utils_test.cpp index b77f422c..39a23038 100644 --- a/test/unit/http_utils_test.cpp +++ b/test/unit/http_utils_test.cpp @@ -58,6 +58,18 @@ LT_BEGIN_AUTO_TEST(http_utils_suite, unescape) LT_CHECK_EQ(expected_size, 3); LT_END_AUTO_TEST(unescape) +LT_BEGIN_AUTO_TEST(http_utils_suite, unescape_plus) + char* string_with_plus = (char*) malloc(6 * sizeof(char)); + sprintf(string_with_plus, "%s", "A+B"); + int expected_size = http::http_unescape(string_with_plus); + + char* expected = (char*) malloc(4 * sizeof(char)); + sprintf(expected, "%s", "A B"); + + LT_CHECK_EQ(string(string_with_plus), string(expected)); + LT_CHECK_EQ(expected_size, 3); +LT_END_AUTO_TEST(unescape_plus) + LT_BEGIN_AUTO_TEST(http_utils_suite, standardize_url) string url = "/", result; result = http::http_utils::standardize_url(url); From 2fc8f6fdb36b11f58d7a49ae709040dd4d058fba Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 23 Dec 2018 07:47:23 +0000 Subject: [PATCH 284/623] Added import of exception --- src/http_utils.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/http_utils.cpp b/src/http_utils.cpp index a38b3461..c0485778 100644 --- a/src/http_utils.cpp +++ b/src/http_utils.cpp @@ -36,6 +36,7 @@ #include #include #include +#include #include "string_utilities.hpp" #include "http_utils.hpp" From 768dec32c014354cb48c836f3229b39eb520c524 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 23 Dec 2018 08:01:07 +0000 Subject: [PATCH 285/623] Remove specific exceptions. --- src/http_utils.cpp | 12 ++++++------ src/httpserver/http_utils.hpp | 16 ---------------- src/webserver.cpp | 3 ++- 3 files changed, 8 insertions(+), 23 deletions(-) diff --git a/src/http_utils.cpp b/src/http_utils.cpp index c0485778..9ca9b32d 100644 --- a/src/http_utils.cpp +++ b/src/http_utils.cpp @@ -377,7 +377,7 @@ ip_representation::ip_representation(const std::string& ip) { if(y != 12) { - throw bad_ip_format_exception(); + throw std::invalid_argument("IP is badly formatted"); } if(parts[i].find('.') != std::string::npos) { @@ -403,12 +403,12 @@ ip_representation::ip_representation(const std::string& ip) } else { - throw bad_ip_format_exception(); + throw std::invalid_argument("IP is badly formatted"); } } else { - throw bad_ip_format_exception(); + throw std::invalid_argument("IP is badly formatted"); } } } @@ -434,7 +434,7 @@ ip_representation::ip_representation(const std::string& ip) } else { - throw bad_ip_format_exception(); + throw std::invalid_argument("IP is badly formatted"); } } } @@ -459,7 +459,7 @@ ip_representation::ip_representation(const std::string& ip) } else { - throw bad_ip_format_exception(); + throw std::invalid_argument("IP is badly formatted"); } } } @@ -497,7 +497,7 @@ char* load_file (const char *filename) return content; } else - throw file_access_exception(); + throw std::invalid_argument("Unable to open file"); return content; } diff --git a/src/httpserver/http_utils.hpp b/src/httpserver/http_utils.hpp index 6119204e..f3568797 100644 --- a/src/httpserver/http_utils.hpp +++ b/src/httpserver/http_utils.hpp @@ -42,22 +42,6 @@ namespace httpserver { namespace http { -class bad_ip_format_exception: public std::exception -{ - virtual const char* what() const throw() - { - return "IP is badly formatted!"; - } -}; - -class file_access_exception: public std::exception -{ - virtual const char* what() const throw() - { - return "Unable to open file!"; - } -}; - class http_utils { public: diff --git a/src/webserver.cpp b/src/webserver.cpp index 12539ce1..759a95b2 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #if defined(__MINGW32__) || defined(__CYGWIN32__) #include @@ -904,7 +905,7 @@ int webserver::finalize_answer( { mr->dhrs->get_raw_response(&raw_response, this); } - catch(const file_access_exception& fae) + catch(const std::invalid_argument& iae) { mr->dhrs = NEW_OR_MOVE(http_response, not_found_page(mr)); mr->dhrs->get_raw_response(&raw_response, this); From 75c852efcae0e6802e3b60c4ac6c88294151264a Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 23 Dec 2018 08:15:01 +0000 Subject: [PATCH 286/623] Mark method const --- src/httpserver/http_response.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/httpserver/http_response.hpp b/src/httpserver/http_response.hpp index ac5c6b50..074708a8 100644 --- a/src/httpserver/http_response.hpp +++ b/src/httpserver/http_response.hpp @@ -187,7 +187,7 @@ class http_response * Method used to get the response code from the response * @return The response code **/ - int get_response_code() + int get_response_code() const { return this->response_code; } From 6826ba8ee487395d86557f27fb7d352fb2d1a275 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 23 Dec 2018 08:26:47 +0000 Subject: [PATCH 287/623] Remove needless exception --- src/http_endpoint.cpp | 2 +- src/httpserver/http_endpoint.hpp | 22 +--------------------- 2 files changed, 2 insertions(+), 22 deletions(-) diff --git a/src/http_endpoint.cpp b/src/http_endpoint.cpp index c6359820..a041bee1 100644 --- a/src/http_endpoint.cpp +++ b/src/http_endpoint.cpp @@ -92,7 +92,7 @@ webserver::http_endpoint::http_endpoint } if((parts[i].size() < 3) || (parts[i][0] != '{') || (parts[i][parts[i].size() - 1] != '}')) - throw webserver::http_endpoint::bad_http_endpoint(); + throw std::invalid_argument("Bad URL format"); std::string::size_type bar = parts[i].find_first_of('|'); this->url_pars.push_back(parts[i].substr(1, bar != string::npos ? bar - 1 : parts[i].size() - 2)); diff --git a/src/httpserver/http_endpoint.hpp b/src/httpserver/http_endpoint.hpp index 7a845047..a4ad1086 100644 --- a/src/httpserver/http_endpoint.hpp +++ b/src/httpserver/http_endpoint.hpp @@ -29,6 +29,7 @@ #include #include #include +#include #include "webserver.hpp" @@ -38,33 +39,12 @@ namespace httpserver class webserver; class http_resource; -namespace details -{ - -/** - * Exception class throwed when a bad formatted http url is used -**/ - -}; - /** * Class representing an Http Endpoint. It is an abstraction used by the APIs. **/ class webserver::http_endpoint { public: - class bad_http_endpoint : public std::exception - { - /** - * Method used to see error details - * @return a const char* containing the error message - **/ - virtual const char* what() const throw() - { - return "Bad url format!"; - } - }; - /** * Copy constructor. It is useful expecially to copy regex_t structure that contains dinamically allocated data. * @param h The http_endpoint to copy From 9dc6a34e5b425d9698046763fb2e3a21760a5dbf Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 23 Dec 2018 08:35:39 +0000 Subject: [PATCH 288/623] Avoid returns as parameters in http_endpoint --- src/httpserver/http_endpoint.hpp | 67 +++++++++----------------------- src/webserver.cpp | 18 ++++----- 2 files changed, 25 insertions(+), 60 deletions(-) diff --git a/src/httpserver/http_endpoint.hpp b/src/httpserver/http_endpoint.hpp index a4ad1086..0b56dc7b 100644 --- a/src/httpserver/http_endpoint.hpp +++ b/src/httpserver/http_endpoint.hpp @@ -50,16 +50,19 @@ class webserver::http_endpoint * @param h The http_endpoint to copy **/ http_endpoint(const http_endpoint& h); + /** * Class Destructor **/ ~http_endpoint(); //if inlined it causes problems during ruby wrapper compiling + /** * Operator overload for "less than operator". It is used to order endpoints in maps. * @param b The http_endpoint to compare to * @return boolean indicating if this is less than b. **/ bool operator <(const http_endpoint& b) const; + /** * Operator overload for "assignment operator". It is used to copy endpoints to existing objects. * Is is functional expecially to copy regex_t structure that contains dinamically allocated data. @@ -67,6 +70,7 @@ class webserver::http_endpoint * @return a reference to the http_endpoint obtained **/ http_endpoint& operator =(const http_endpoint& h); + /** * Method indicating if this endpoint 'matches' with the one passed. A passed endpoint matches a registered endpoint if * the regex represented by the registered endpoint matches the passed one. @@ -74,6 +78,7 @@ class webserver::http_endpoint * @return true if the passed endpoint matches this. **/ bool match(const http_endpoint& url) const; + /** * Method used to get the complete endpoint url * @return a string representing the url @@ -82,22 +87,7 @@ class webserver::http_endpoint { return this->url_complete; } - /** - * Method used to get the complete endpoint url - * @param result a string reference that will be filled with the url - **/ - void get_url_complete(std::string& result) const - { - result = this->url_complete; - } - /** - * Method used to find the size of the complete endpoint url - * @return the size - **/ - size_t get_url_complete_size() const - { - return this->url_complete.size(); - } + /** * Method used to get all pars defined inside an url. * @return a vector of strings representing all found pars. @@ -106,11 +96,7 @@ class webserver::http_endpoint { return this->url_pars; } - size_t get_url_pars(std::vector& result) const - { - result = this->url_pars; - return result.size(); - } + /** * Method used to get all pieces of an url; considering an url splitted by '/'. * @return a vector of strings representing all found pieces. @@ -119,24 +105,7 @@ class webserver::http_endpoint { return this->url_pieces; } - /** - * Method used to get all pieces of an url; considering an url splittet by '/'. - * @param result a vector of strings to fill with url pieces. - * @return the size of the vector in output - **/ - size_t get_url_pieces(std::vector& result) const - { - result = this->url_pieces; - return result.size(); - } - /** - * Method used to get the number of pieces the url is composed of - * @return the number of pieces - **/ - size_t get_url_pieces_num() const - { - return this->url_pieces.size(); - } + /** * Method used to get indexes of all parameters inside url * @return a vector of int indicating all positions. @@ -145,16 +114,7 @@ class webserver::http_endpoint { return this->chunk_positions; } - /** - * Method used to get indexes of all parameters inside url - * @param result a vector to fill with ints indicating chunk positions - * @return the size of the vector filled - **/ - size_t get_chunk_positions(std::vector& result) const - { - result = this->chunk_positions; - return result.size(); - } + /** * Default constructor of the class. * @param family boolean that indicates if the endpoint is a family endpoint. @@ -169,6 +129,7 @@ class webserver::http_endpoint reg_compiled(false) { } + /** * Constructor of the class http_endpoint. It is used to initialize an http_endpoint starting from a string form URL. * @param url The string representation of the endpoint. All endpoints are in the form "/path/to/resource". @@ -185,34 +146,42 @@ class webserver::http_endpoint bool registration = false, bool use_regex = true ); + /** * The complete url extracted **/ std::string url_complete; + /** * The url standardized in order to use standard comparisons or regexes **/ std::string url_modded; + /** * Vector containing parameters extracted from url **/ std::vector url_pars; + /** * Pieces the url can be splitted into (consider '/' as separator) **/ std::vector url_pieces; + /** * Position of url pieces representing parameters **/ std::vector chunk_positions; + /** * Regex used in comparisons **/ regex_t re_url_modded; + /** * Boolean indicating wheter the endpoint represents a family **/ bool family_url; + /** * Boolean indicating if the regex is compiled **/ diff --git a/src/webserver.cpp b/src/webserver.cpp index 759a95b2..3f8af704 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -823,8 +823,8 @@ int webserver::finalize_answer( size_t tot_len = 0; for(it=registered_resources.begin(); it!=registered_resources.end(); ++it) { - size_t endpoint_pieces_len = (*it).first.get_url_pieces_num(); - size_t endpoint_tot_len = (*it).first.get_url_complete_size(); + size_t endpoint_pieces_len = (*it).first.get_url_pieces().size(); + size_t endpoint_tot_len = (*it).first.get_url_complete().size(); if(!found || endpoint_pieces_len > len || (endpoint_pieces_len == len && endpoint_tot_len > tot_len)) { if((*it).first.match(endpoint)) @@ -838,17 +838,13 @@ int webserver::finalize_answer( } if(found) { - vector url_pars; + vector url_pars = found_endpoint->first.get_url_pars(); - size_t pars_size = found_endpoint->first.get_url_pars(url_pars); - - vector url_pieces; - endpoint.get_url_pieces(url_pieces); - vector chunkes; - found_endpoint->first.get_chunk_positions(chunkes); - for(unsigned int i = 0; i < pars_size; i++) + vector url_pieces = endpoint.get_url_pieces(); + vector chunks = found_endpoint->first.get_chunk_positions(); + for(unsigned int i = 0; i < url_pars.size(); i++) { - mr->dhr->set_arg(url_pars[i], url_pieces[chunkes[i]]); + mr->dhr->set_arg(url_pars[i], url_pieces[chunks[i]]); } hrm = found_endpoint->second; From bbff8bc082701721b7e480eca0ceb7fbca7a9d92 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 23 Dec 2018 08:41:44 +0000 Subject: [PATCH 289/623] Return reference --- src/httpserver/http_endpoint.hpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/httpserver/http_endpoint.hpp b/src/httpserver/http_endpoint.hpp index 0b56dc7b..1efa99d3 100644 --- a/src/httpserver/http_endpoint.hpp +++ b/src/httpserver/http_endpoint.hpp @@ -83,7 +83,7 @@ class webserver::http_endpoint * Method used to get the complete endpoint url * @return a string representing the url **/ - const std::string get_url_complete() const + const std::string& get_url_complete() const { return this->url_complete; } @@ -92,7 +92,7 @@ class webserver::http_endpoint * Method used to get all pars defined inside an url. * @return a vector of strings representing all found pars. **/ - const std::vector get_url_pars() const + const std::vector& get_url_pars() const { return this->url_pars; } @@ -101,7 +101,7 @@ class webserver::http_endpoint * Method used to get all pieces of an url; considering an url splitted by '/'. * @return a vector of strings representing all found pieces. **/ - const std::vector get_url_pieces() const + const std::vector& get_url_pieces() const { return this->url_pieces; } @@ -110,7 +110,7 @@ class webserver::http_endpoint * Method used to get indexes of all parameters inside url * @return a vector of int indicating all positions. **/ - const std::vector get_chunk_positions() const + const std::vector& get_chunk_positions() const { return this->chunk_positions; } From 2865cfdf6802f187bd7dcecb4a86c62d89e24acb Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 23 Dec 2018 09:08:02 +0000 Subject: [PATCH 290/623] Return reference rather than copying --- src/http_request.cpp | 22 +---------- src/httpserver/http_request.hpp | 70 +++++++++++++++++---------------- 2 files changed, 39 insertions(+), 53 deletions(-) diff --git a/src/http_request.cpp b/src/http_request.cpp index c2aae54a..bca1012d 100644 --- a/src/http_request.cpp +++ b/src/http_request.cpp @@ -29,6 +29,8 @@ using namespace std; namespace httpserver { +const std::string http_request::EMPTY = ""; + void http_request::set_method(const std::string& method) { this->method = string_utilities::to_upper_copy(method); @@ -62,26 +64,6 @@ bool http_request::check_digest_auth( return true; } -const std::map http_request::get_headers() const -{ - return this->headers; -} - -const std::map http_request::get_footers() const -{ - return this->footers; -} - -const std::map http_request::get_cookies() const -{ - return this->cookies; -} - -const std::map http_request::get_args() const -{ - return this->args; -} - std::ostream &operator<< (std::ostream &os, const http_request &r) { os << r.method << " Request [user:\"" << r.user << "\" pass:\"" << r.pass << "\"] path:\"" diff --git a/src/httpserver/http_request.hpp b/src/httpserver/http_request.hpp index ab3901b1..93a23f7b 100644 --- a/src/httpserver/http_request.hpp +++ b/src/httpserver/http_request.hpp @@ -50,12 +50,13 @@ namespace http class http_request { public: + static const std::string EMPTY; /** * Method used to get the username eventually passed through basic authentication. * @return string representation of the username. **/ - const std::string get_user() const + const std::string& get_user() const { return this->user; } @@ -64,7 +65,7 @@ class http_request * Method used to get the username extracted from a digest authentication * @return the username **/ - const std::string get_digested_user() const + const std::string& get_digested_user() const { return this->digested_user; } @@ -73,7 +74,7 @@ class http_request * Method used to get the password eventually passed through basic authentication. * @return string representation of the password. **/ - const std::string get_pass() const + const std::string& get_pass() const { return this->pass; } @@ -82,7 +83,7 @@ class http_request * Method used to get the path requested * @return string representing the path requested. **/ - const std::string get_path() const + const std::string& get_path() const { return this->path; } @@ -91,37 +92,28 @@ class http_request * Method used to get all pieces of the path requested; considering an url splitted by '/'. * @return a vector of strings containing all pieces **/ - const std::vector get_path_pieces() const + const std::vector& get_path_pieces() const { return this->post_path; } - /** - * Method used to obtain the size of path in terms of pieces; considering an url splitted by '/'. - * @return an integer representing the number of pieces - **/ - size_t get_path_pieces_size() const - { - return this->post_path.size(); - } - /** * Method used to obtain a specified piece of the path; considering an url splitted by '/'. * @param index the index of the piece selected * @return the selected piece in form of string **/ - const std::string get_path_piece(int index) const + const std::string& get_path_piece(int index) const { if(((int)(this->post_path.size())) > index) return this->post_path[index]; - return ""; + return EMPTY; } /** * Method used to get the METHOD used to make the request. * @return string representing the method. **/ - const std::string get_method() const + const std::string& get_method() const { return this->method; } @@ -131,52 +123,64 @@ class http_request * @param result a map > that will be filled with all headers * @result the size of the map **/ - const std::map get_headers() const; + const std::map& get_headers() const + { + return this->headers; + } /** * Method used to get all footers passed with the request. * @param result a map > that will be filled with all footers * @result the size of the map **/ - const std::map get_footers() const; + const std::map& get_footers() const + { + return this->footers; + } /** * Method used to get all cookies passed with the request. * @param result a map > that will be filled with all cookies * @result the size of the map **/ - const std::map get_cookies() const; + const std::map& get_cookies() const + { + return this->cookies; + } /** * Method used to get all args passed with the request. * @param result a map > that will be filled with all args * @result the size of the map **/ - const std::map get_args() const; + const std::map& get_args() const + { + return this->args; + } /** * Method used to get a specific header passed with the request. * @param key the specific header to get the value from * @return the value of the header. **/ - const std::string get_header(const std::string& key) const + const std::string& get_header(const std::string& key) const { std::map::const_iterator it = this->headers.find(key); if(it != this->headers.end()) return it->second; else - return ""; + return EMPTY; } - const std::string get_cookie(const std::string& key) const + const std::string& get_cookie(const std::string& key) const { std::map::const_iterator it = this->cookies.find(key); if(it != this->cookies.end()) return it->second; else - return ""; + return EMPTY; } /** @@ -184,14 +188,14 @@ class http_request * @param key the specific footer to get the value from * @return the value of the footer. **/ - const std::string get_footer(const std::string& key) const + const std::string& get_footer(const std::string& key) const { std::map::const_iterator it = this->footers.find(key); if(it != this->footers.end()) return it->second; else - return ""; + return EMPTY; } /** @@ -199,21 +203,21 @@ class http_request * @param ket the specific argument to get the value from * @return the value of the arg. **/ - const std::string get_arg(const std::string& key) const + const std::string& get_arg(const std::string& key) const { std::map::const_iterator it = this->args.find(key); if(it != this->args.end()) return it->second; else - return ""; + return EMPTY; } /** * Method used to get the content of the request. * @return the content in string representation **/ - const std::string get_content() const + const std::string& get_content() const { return this->content; } @@ -230,7 +234,7 @@ class http_request * Method used to get the content of the query string.. * @return the query string in string representation **/ - const std::string get_querystring() const + const std::string& get_querystring() const { return this->querystring; } @@ -239,7 +243,7 @@ class http_request * Method used to get the version of the request. * @return the version in string representation **/ - const std::string get_version() const + const std::string& get_version() const { return this->version; } @@ -248,7 +252,7 @@ class http_request * Method used to get the requestor. * @return the requestor **/ - const std::string get_requestor() const + const std::string& get_requestor() const { return this->requestor; } From f64bd7b93420a6a48b0d10c76586602a832d34a1 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 23 Dec 2018 09:18:18 +0000 Subject: [PATCH 291/623] Avoid returns as parameters --- src/http_response.cpp | 18 -------- src/httpserver/http_response.hpp | 70 +++++++++----------------------- 2 files changed, 20 insertions(+), 68 deletions(-) diff --git a/src/http_response.cpp b/src/http_response.cpp index 83533808..48fb238b 100644 --- a/src/http_response.cpp +++ b/src/http_response.cpp @@ -73,24 +73,6 @@ http_response::~http_response() webserver::unlock_cache_entry(ce); } -size_t http_response::get_headers(std::map& result) const -{ - result = this->headers; - return result.size(); -} - -size_t http_response::get_footers(std::map& result) const -{ - result = this->footers; - return result.size(); -} - -size_t http_response::get_cookies(std::map& result) const -{ - result = this->cookies; - return result.size(); -} - //RESPONSE void http_response::get_raw_response_str(MHD_Response** response, webserver* ws) { diff --git a/src/httpserver/http_response.hpp b/src/httpserver/http_response.hpp index 074708a8..63cf7ee8 100644 --- a/src/httpserver/http_response.hpp +++ b/src/httpserver/http_response.hpp @@ -113,75 +113,58 @@ class http_response * Method used to get the content from the response. * @return the content in string form **/ - std::string get_content() + const std::string& get_content() { return this->content; } - void get_content(std::string& result) - { - result = this->content; - } - /** * Method used to get a specified header defined for the response * @param key The header identification * @return a string representing the value assumed by the header **/ - std::string get_header(const std::string& key) + const std::string& get_header(const std::string& key) { return this->headers[key]; } - void get_header(const std::string& key, std::string& result) - { - result = this->headers[key]; - } - /** * Method used to get a specified footer defined for the response * @param key The footer identification * @return a string representing the value assumed by the footer **/ - std::string get_footer(const std::string& key) + const std::string& get_footer(const std::string& key) { return this->footers[key]; } - void get_footer(const std::string& key, std::string& result) - { - result = this->footers[key]; - } - - std::string get_cookie(const std::string& key) + const std::string& get_cookie(const std::string& key) { return this->cookies[key]; } - void get_cookie(const std::string& key, std::string& result) - { - result = this->cookies[key]; - } - /** * Method used to get all headers passed with the request. * @return a map containing all headers. **/ - size_t get_headers( - std::map& result - ) const; + const std::map& get_headers() const + { + return this->headers; + } /** * Method used to get all footers passed with the request. * @return a map containing all footers. **/ - size_t get_footers( - std::map& result - ) const; + const std::map& get_footers() const + { + return this->footers; + } - size_t get_cookies( - std::map& result - ) const; + const std::map& get_cookies() const + { + return this->cookies; + } /** * Method used to get the response code from the response @@ -192,26 +175,16 @@ class http_response return this->response_code; } - std::string get_realm() const + const std::string& get_realm() const { return this->realm; } - void get_realm(std::string& result) const - { - result = this->realm; - } - - std::string get_opaque() const + const std::string& get_opaque() const { return this->opaque; } - void get_opaque(std::string& result) const - { - result = this->opaque; - } - bool need_nonce_reload() const { return this->reload_nonce; @@ -227,12 +200,9 @@ class http_response return autodelete; } - size_t get_topics(std::vector& topics) const + const std::vector& get_topics() const { - typedef std::vector::const_iterator topics_it; - for(topics_it it=this->topics.begin();it != this->topics.end();++it) - topics.push_back(*it); - return topics.size(); + return this->topics; } protected: typedef details::binders::functor_two get_raw_response_t; From ec2d5cbf2aba8c19cfb6db10b6da3dea646b8465 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 23 Dec 2018 19:58:58 +0000 Subject: [PATCH 292/623] Added tests on ip_representation IPV4 --- src/httpserver/http_utils.hpp | 2 +- test/unit/http_utils_test.cpp | 61 +++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 1 deletion(-) diff --git a/src/httpserver/http_utils.hpp b/src/httpserver/http_utils.hpp index f3568797..d8483f6f 100644 --- a/src/httpserver/http_utils.hpp +++ b/src/httpserver/http_utils.hpp @@ -286,7 +286,7 @@ struct ip_representation { http_utils::IP_version_T ip_version; unsigned short pieces[16]; - unsigned int mask:16; + unsigned short mask; ip_representation(http_utils::IP_version_T ip_version) : ip_version(ip_version) diff --git a/test/unit/http_utils_test.cpp b/test/unit/http_utils_test.cpp index 39a23038..279749de 100644 --- a/test/unit/http_utils_test.cpp +++ b/test/unit/http_utils_test.cpp @@ -148,6 +148,67 @@ LT_BEGIN_AUTO_TEST(http_utils_suite, get_port_null) LT_CHECK_THROW(http::get_port((struct sockaddr*) 0x0)); LT_END_AUTO_TEST(get_port_null) +LT_BEGIN_AUTO_TEST(http_utils_suite, ip_representation4_str) + http::ip_representation test_ip("192.168.5.5"); + + LT_CHECK_EQ(test_ip.ip_version, http::http_utils::IPV4); + + for (int i = 0; i < 12; i++) { + LT_CHECK_EQ(test_ip.pieces[i], 0); + } + + LT_CHECK_EQ(test_ip.pieces[12], 192); + LT_CHECK_EQ(test_ip.pieces[13], 168); + LT_CHECK_EQ(test_ip.pieces[14], 5); + LT_CHECK_EQ(test_ip.pieces[15], 5); + + LT_CHECK_EQ(test_ip.mask, 0xFFFF); +LT_END_AUTO_TEST(ip_representation4_str) + +LT_BEGIN_AUTO_TEST(http_utils_suite, ip_representation4_str_mask) + http::ip_representation test_ip("192.168.*.*"); + + LT_CHECK_EQ(test_ip.ip_version, http::http_utils::IPV4); + + for (int i = 0; i < 12; i++) { + LT_CHECK_EQ(test_ip.pieces[i], 0); + } + + LT_CHECK_EQ(test_ip.pieces[12], 192); + LT_CHECK_EQ(test_ip.pieces[13], 168); + LT_CHECK_EQ(test_ip.pieces[14], 0); + LT_CHECK_EQ(test_ip.pieces[15], 0); + + LT_CHECK_EQ(test_ip.mask, 0x3FFF); +LT_END_AUTO_TEST(ip_representation4_str_mask) + +LT_BEGIN_AUTO_TEST(http_utils_suite, ip_representation4_str_invalid) + LT_CHECK_THROW(http::ip_representation("192.168.5.5.5")); +LT_END_AUTO_TEST(ip_representation4_str_invalid) + +LT_BEGIN_AUTO_TEST(http_utils_suite, ip_representation6_str) + http::ip_representation test_ip("2001:db8:8714:3a90::12"); + + LT_CHECK_EQ(test_ip.ip_version, http::http_utils::IPV6); + + LT_CHECK_EQ(test_ip.pieces[0], 32); + LT_CHECK_EQ(test_ip.pieces[1], 1); + LT_CHECK_EQ(test_ip.pieces[2], 13); + LT_CHECK_EQ(test_ip.pieces[3], 184); + LT_CHECK_EQ(test_ip.pieces[4], 135); + LT_CHECK_EQ(test_ip.pieces[5], 20); + LT_CHECK_EQ(test_ip.pieces[6], 58); + LT_CHECK_EQ(test_ip.pieces[7], 144); + LT_CHECK_EQ(test_ip.pieces[8], 0); + LT_CHECK_EQ(test_ip.pieces[9], 0); + LT_CHECK_EQ(test_ip.pieces[10], 0); + LT_CHECK_EQ(test_ip.pieces[11], 0); + LT_CHECK_EQ(test_ip.pieces[12], 18); + LT_CHECK_EQ(test_ip.pieces[13], 0); + LT_CHECK_EQ(test_ip.pieces[14], 0); + LT_CHECK_EQ(test_ip.pieces[15], 0); +LT_END_AUTO_TEST(ip_representation6_str) + LT_BEGIN_AUTO_TEST_ENV() AUTORUN_TESTS() LT_END_AUTO_TEST_ENV() From 29779b88265ceb02a5cdde25b1294943cf28abcf Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Mon, 24 Dec 2018 00:31:16 +0000 Subject: [PATCH 293/623] Test ip_representation building from string --- src/http_utils.cpp | 111 +++++++++++++------ test/unit/http_utils_test.cpp | 194 +++++++++++++++++++++++++++++++++- 2 files changed, 268 insertions(+), 37 deletions(-) diff --git a/src/http_utils.cpp b/src/http_utils.cpp index 9ca9b32d..7b59c35d 100644 --- a/src/http_utils.cpp +++ b/src/http_utils.cpp @@ -352,18 +352,61 @@ ip_representation::ip_representation(const std::string& ip) { ip_version = http_utils::IPV6; parts = string_utilities::string_split(ip, ':', false); + if (parts.size() > 8) + { + throw std::invalid_argument("IP is badly formatted. Max 8 parts in IPV6."); + } + + int omitted = 8 - (parts.size() - 1); + if (omitted != 0) + { + int empty_count = 0; + for (unsigned int i = 0; i < parts.size(); i++) + { + if (parts[i].size() == 0) empty_count++; + } + + if (empty_count > 1) + { + if (parts[parts.size() - 1].find(".") != std::string::npos) omitted -= 1; + + if (empty_count == 2 && parts[0] == "" && parts[1] == "") + { + omitted += 1; + parts = std::vector(parts.begin() + 1, parts.end()); + } + else + { + throw std::invalid_argument("IP is badly formatted. Cannot have more than one omitted segment in IPV6."); + } + } + } + int y = 0; - for(unsigned int i = 0; i < parts.size(); i++) + for (unsigned int i = 0; i < parts.size(); i++) { - if(parts[i] != "*" && parts[i] != "") + if (parts[i] != "*") { - if(parts[i].size() < 4) + if (parts[i].size() == 0) + { + for (unsigned int omitted_idx = 0; omitted_idx < omitted; omitted_idx++) + { + pieces[y] = 0; + pieces[y+1] = 0; + y += 2; + } + + continue; + } + + if (parts[i].size() < 4) { stringstream ss; ss << setfill('0') << setw(4) << parts[i]; parts[i] = ss.str(); } - if(parts[i].size() == 4) + + if (parts[i].size() == 4) { pieces[y] = strtol((parts[i].substr(0,2)).c_str(),NULL,16); pieces[y+1] = strtol( @@ -371,19 +414,36 @@ ip_representation::ip_representation(const std::string& ip) NULL, 16 ); + y += 2; } else - { - if(y != 12) - { - throw std::invalid_argument("IP is badly formatted"); - } + { if(parts[i].find('.') != std::string::npos) { + if(y != 12) + { + throw std::invalid_argument("IP is badly formatted. Missing parts before nested IPV4."); + } + + if (i != parts.size() - 1) + { + throw std::invalid_argument("IP is badly formatted. Nested IPV4 should be at the end"); + } + vector subparts = string_utilities::string_split(parts[i], '.'); if(subparts.size() == 4) { + for (unsigned int k = 0; k < 10; k++) + { + if (pieces[k] != 0) throw std::invalid_argument("IP is badly formatted. Nested IPV4 can be preceded only by 0 (and, optionally, two 255 octects)"); + } + + if ((pieces[10] != 0 && pieces[10] != 255) || (pieces[11] != 0 && pieces[11] != 255)) + { + throw std::invalid_argument("IP is badly formatted. Nested IPV4 can be preceded only by 0 (and, optionally, two 255 octects)"); + } + for(unsigned int ii = 0; ii < subparts.size(); ii++) { if(subparts[ii] != "*") @@ -393,50 +453,30 @@ ip_representation::ip_representation(const std::string& ip) NULL, 10 ); + if (pieces[y+ii] > 255) throw std::invalid_argument("IP is badly formatted. 255 is max value for ip part."); } else { - CLEAR_BIT(mask, y+11); + CLEAR_BIT(mask, y+ii); } - y++; } } else { - throw std::invalid_argument("IP is badly formatted"); + throw std::invalid_argument("IP is badly formatted. Nested IPV4 can have max 4 parts."); } } else { - throw std::invalid_argument("IP is badly formatted"); + throw std::invalid_argument("IP is badly formatted. IPV6 parts can have max 4 characters (or nest an IPV4)"); } } } - else if(parts[i] == "*") + else { CLEAR_BIT(mask, y); y++; } - else - { - if(parts.size() <= 8) - { - int covered_pieces = 1 + (8 - parts.size()); - if(parts[parts.size() - 1].find('.') != std::string::npos) - { - covered_pieces -= 2; - } - for(int k = 0; k < covered_pieces; k++) - { - pieces[y] = 0; - y++; - } - } - else - { - throw std::invalid_argument("IP is badly formatted"); - } - } } } else //IPV4 @@ -450,6 +490,7 @@ ip_representation::ip_representation(const std::string& ip) if(parts[i] != "*") { pieces[12+i] = strtol(parts[i].c_str(), NULL, 10); + if (pieces[12+i] > 255) throw std::invalid_argument("IP is badly formatted. 255 is max value for ip part."); } else { @@ -459,7 +500,7 @@ ip_representation::ip_representation(const std::string& ip) } else { - throw std::invalid_argument("IP is badly formatted"); + throw std::invalid_argument("IP is badly formatted. Max 4 parts in IPV4."); } } } diff --git a/test/unit/http_utils_test.cpp b/test/unit/http_utils_test.cpp index 279749de..b5edc4d6 100644 --- a/test/unit/http_utils_test.cpp +++ b/test/unit/http_utils_test.cpp @@ -186,6 +186,10 @@ LT_BEGIN_AUTO_TEST(http_utils_suite, ip_representation4_str_invalid) LT_CHECK_THROW(http::ip_representation("192.168.5.5.5")); LT_END_AUTO_TEST(ip_representation4_str_invalid) +LT_BEGIN_AUTO_TEST(http_utils_suite, ip_representation4_str_beyond255) + LT_CHECK_THROW(http::ip_representation("192.168.256.5")); +LT_END_AUTO_TEST(ip_representation4_str_beyond255) + LT_BEGIN_AUTO_TEST(http_utils_suite, ip_representation6_str) http::ip_representation test_ip("2001:db8:8714:3a90::12"); @@ -203,12 +207,198 @@ LT_BEGIN_AUTO_TEST(http_utils_suite, ip_representation6_str) LT_CHECK_EQ(test_ip.pieces[9], 0); LT_CHECK_EQ(test_ip.pieces[10], 0); LT_CHECK_EQ(test_ip.pieces[11], 0); - LT_CHECK_EQ(test_ip.pieces[12], 18); + LT_CHECK_EQ(test_ip.pieces[12], 0); LT_CHECK_EQ(test_ip.pieces[13], 0); LT_CHECK_EQ(test_ip.pieces[14], 0); - LT_CHECK_EQ(test_ip.pieces[15], 0); + LT_CHECK_EQ(test_ip.pieces[15], 18); + + LT_CHECK_EQ(test_ip.mask, 0xFFFF); LT_END_AUTO_TEST(ip_representation6_str) +LT_BEGIN_AUTO_TEST(http_utils_suite, ip_representation6_str_mask) + http::ip_representation test_ip("2001:db8:8714:3a90:*:*"); + + LT_CHECK_EQ(test_ip.ip_version, http::http_utils::IPV6); + + LT_CHECK_EQ(test_ip.pieces[0], 32); + LT_CHECK_EQ(test_ip.pieces[1], 1); + LT_CHECK_EQ(test_ip.pieces[2], 13); + LT_CHECK_EQ(test_ip.pieces[3], 184); + LT_CHECK_EQ(test_ip.pieces[4], 135); + LT_CHECK_EQ(test_ip.pieces[5], 20); + LT_CHECK_EQ(test_ip.pieces[6], 58); + LT_CHECK_EQ(test_ip.pieces[7], 144); + LT_CHECK_EQ(test_ip.pieces[8], 0); + LT_CHECK_EQ(test_ip.pieces[9], 0); + LT_CHECK_EQ(test_ip.pieces[10], 0); + LT_CHECK_EQ(test_ip.pieces[11], 0); + LT_CHECK_EQ(test_ip.pieces[12], 0); + LT_CHECK_EQ(test_ip.pieces[13], 0); + LT_CHECK_EQ(test_ip.pieces[14], 0); + LT_CHECK_EQ(test_ip.pieces[15], 0); + + LT_CHECK_EQ(test_ip.mask, 0xFCFF); +LT_END_AUTO_TEST(ip_representation6_str_mask) + +LT_BEGIN_AUTO_TEST(http_utils_suite, ip_representation6_str_nested) + http::ip_representation test_ip("::ffff:192.0.2.128"); + + LT_CHECK_EQ(test_ip.ip_version, http::http_utils::IPV6); + + LT_CHECK_EQ(test_ip.pieces[0], 0); + LT_CHECK_EQ(test_ip.pieces[1], 0); + LT_CHECK_EQ(test_ip.pieces[2], 0); + LT_CHECK_EQ(test_ip.pieces[3], 0); + LT_CHECK_EQ(test_ip.pieces[4], 0); + LT_CHECK_EQ(test_ip.pieces[5], 0); + LT_CHECK_EQ(test_ip.pieces[6], 0); + LT_CHECK_EQ(test_ip.pieces[7], 0); + LT_CHECK_EQ(test_ip.pieces[8], 0); + LT_CHECK_EQ(test_ip.pieces[9], 0); + LT_CHECK_EQ(test_ip.pieces[10], 255); + LT_CHECK_EQ(test_ip.pieces[11], 255); + LT_CHECK_EQ(test_ip.pieces[12], 192); + LT_CHECK_EQ(test_ip.pieces[13], 0); + LT_CHECK_EQ(test_ip.pieces[14], 2); + LT_CHECK_EQ(test_ip.pieces[15], 128); + + LT_CHECK_EQ(test_ip.mask, 0xFFFF); +LT_END_AUTO_TEST(ip_representation6_str_nested) + +LT_BEGIN_AUTO_TEST(http_utils_suite, ip_representation6_str_nested_deprecated) + LT_CHECK_NOTHROW(http::ip_representation("::192.0.2.128")); + http::ip_representation test_ip("::192.0.2.128"); + + LT_CHECK_EQ(test_ip.ip_version, http::http_utils::IPV6); + + LT_CHECK_EQ(test_ip.pieces[0], 0); + LT_CHECK_EQ(test_ip.pieces[1], 0); + LT_CHECK_EQ(test_ip.pieces[2], 0); + LT_CHECK_EQ(test_ip.pieces[3], 0); + LT_CHECK_EQ(test_ip.pieces[4], 0); + LT_CHECK_EQ(test_ip.pieces[5], 0); + LT_CHECK_EQ(test_ip.pieces[6], 0); + LT_CHECK_EQ(test_ip.pieces[7], 0); + LT_CHECK_EQ(test_ip.pieces[8], 0); + LT_CHECK_EQ(test_ip.pieces[9], 0); + LT_CHECK_EQ(test_ip.pieces[10], 0); + LT_CHECK_EQ(test_ip.pieces[11], 0); + LT_CHECK_EQ(test_ip.pieces[12], 192); + LT_CHECK_EQ(test_ip.pieces[13], 0); + LT_CHECK_EQ(test_ip.pieces[14], 2); + LT_CHECK_EQ(test_ip.pieces[15], 128); + + LT_CHECK_EQ(test_ip.mask, 0xFFFF); +LT_END_AUTO_TEST(ip_representation6_str_nested_deprecated) + +LT_BEGIN_AUTO_TEST(http_utils_suite, ip_representation6_str_ipv4_mask) + http::ip_representation test_ip("::ffff:192.0.*.*"); + + LT_CHECK_EQ(test_ip.ip_version, http::http_utils::IPV6); + + LT_CHECK_EQ(test_ip.pieces[0], 0); + LT_CHECK_EQ(test_ip.pieces[1], 0); + LT_CHECK_EQ(test_ip.pieces[2], 0); + LT_CHECK_EQ(test_ip.pieces[3], 0); + LT_CHECK_EQ(test_ip.pieces[4], 0); + LT_CHECK_EQ(test_ip.pieces[5], 0); + LT_CHECK_EQ(test_ip.pieces[6], 0); + LT_CHECK_EQ(test_ip.pieces[7], 0); + LT_CHECK_EQ(test_ip.pieces[8], 0); + LT_CHECK_EQ(test_ip.pieces[9], 0); + LT_CHECK_EQ(test_ip.pieces[10], 255); + LT_CHECK_EQ(test_ip.pieces[11], 255); + LT_CHECK_EQ(test_ip.pieces[12], 192); + LT_CHECK_EQ(test_ip.pieces[13], 0); + LT_CHECK_EQ(test_ip.pieces[14], 0); + LT_CHECK_EQ(test_ip.pieces[15], 0); + + LT_CHECK_EQ(test_ip.mask, 0x3FFF); +LT_END_AUTO_TEST(ip_representation6_str_ipv4_mask) + +LT_BEGIN_AUTO_TEST(http_utils_suite, ip_representation6_str_clustered_middle) + http::ip_representation test_ip("2001:db8::ff00:42:8329"); + + LT_CHECK_EQ(test_ip.ip_version, http::http_utils::IPV6); + + LT_CHECK_EQ(test_ip.pieces[0], 32); + LT_CHECK_EQ(test_ip.pieces[1], 1); + LT_CHECK_EQ(test_ip.pieces[2], 13); + LT_CHECK_EQ(test_ip.pieces[3], 184); + LT_CHECK_EQ(test_ip.pieces[4], 0); + LT_CHECK_EQ(test_ip.pieces[5], 0); + LT_CHECK_EQ(test_ip.pieces[6], 0); + LT_CHECK_EQ(test_ip.pieces[7], 0); + LT_CHECK_EQ(test_ip.pieces[8], 0); + LT_CHECK_EQ(test_ip.pieces[9], 0); + LT_CHECK_EQ(test_ip.pieces[10], 255); + LT_CHECK_EQ(test_ip.pieces[11], 0); + LT_CHECK_EQ(test_ip.pieces[12], 0); + LT_CHECK_EQ(test_ip.pieces[13], 66); + LT_CHECK_EQ(test_ip.pieces[14], 131); + LT_CHECK_EQ(test_ip.pieces[15], 41); + + LT_CHECK_EQ(test_ip.mask, 0xFFFF); +LT_END_AUTO_TEST(ip_representation6_str_clustered_middle) + +LT_BEGIN_AUTO_TEST(http_utils_suite, ip_representation6_str_loopback) + http::ip_representation test_ip("::1"); + + LT_CHECK_EQ(test_ip.ip_version, http::http_utils::IPV6); + + LT_CHECK_EQ(test_ip.pieces[0], 0); + LT_CHECK_EQ(test_ip.pieces[1], 0); + LT_CHECK_EQ(test_ip.pieces[2], 0); + LT_CHECK_EQ(test_ip.pieces[3], 0); + LT_CHECK_EQ(test_ip.pieces[4], 0); + LT_CHECK_EQ(test_ip.pieces[5], 0); + LT_CHECK_EQ(test_ip.pieces[6], 0); + LT_CHECK_EQ(test_ip.pieces[7], 0); + LT_CHECK_EQ(test_ip.pieces[8], 0); + LT_CHECK_EQ(test_ip.pieces[9], 0); + LT_CHECK_EQ(test_ip.pieces[10], 0); + LT_CHECK_EQ(test_ip.pieces[11], 0); + LT_CHECK_EQ(test_ip.pieces[12], 0); + LT_CHECK_EQ(test_ip.pieces[13], 0); + LT_CHECK_EQ(test_ip.pieces[14], 0); + LT_CHECK_EQ(test_ip.pieces[15], 1); + + LT_CHECK_EQ(test_ip.mask, 0xFFFF); +LT_END_AUTO_TEST(ip_representation6_str_loopback) + +LT_BEGIN_AUTO_TEST(http_utils_suite, ip_representation6_str_invalid) + LT_CHECK_THROW(http::ip_representation("2001:db8:8714:3a90::12:4:4:4")); +LT_END_AUTO_TEST(ip_representation6_str_invalid) + +LT_BEGIN_AUTO_TEST(http_utils_suite, ip_representation6_str_block_too_long) + LT_CHECK_THROW(http::ip_representation("2001:db8:87214:3a90::12:4:4")); +LT_END_AUTO_TEST(ip_representation6_str_block_too_long) + +LT_BEGIN_AUTO_TEST(http_utils_suite, ip_representation6_str_invalid_multiple_clusters) + LT_CHECK_THROW(http::ip_representation("2001::3a90::12:4:4")); +LT_END_AUTO_TEST(ip_representation6_str_invalid_multiple_clusters) + +LT_BEGIN_AUTO_TEST(http_utils_suite, ip_representation6_str_invalid_too_long_before_nested) + LT_CHECK_THROW(http::ip_representation("2001:db8:8714:3a90:13:12:13:192.0.2.128")); +LT_END_AUTO_TEST(ip_representation6_str_invalid_too_long_before_nested) + +LT_BEGIN_AUTO_TEST(http_utils_suite, ip_representation6_str_invalid_nested_beyond255) + LT_CHECK_THROW(http::ip_representation("::ffff:192.0.256.128")); +LT_END_AUTO_TEST(ip_representation6_str_invalid_nested_beyond255) + +LT_BEGIN_AUTO_TEST(http_utils_suite, ip_representation6_str_invalid_nested_not_at_end) + LT_CHECK_THROW(http::ip_representation("::ffff:192.0.256.128:ffff")); +LT_END_AUTO_TEST(ip_representation6_str_invalid_nested_not_at_end) + +LT_BEGIN_AUTO_TEST(http_utils_suite, ip_representation6_str_invalid_nested_starting_non_zero) + LT_CHECK_THROW(http::ip_representation("0:0:1::ffff:192.0.5.128")); +LT_END_AUTO_TEST(ip_representation6_str_invalid_nested_starting_non_zero) + +LT_BEGIN_AUTO_TEST(http_utils_suite, ip_representation6_str_invalid_nested_starting_wrong_prefix) + LT_CHECK_THROW(http::ip_representation("::ffcc:192.0.5.128")); + LT_CHECK_THROW(http::ip_representation("::ccff:192.0.5.128")); +LT_END_AUTO_TEST(ip_representation6_str_invalid_nested_starting_wrong_prefix) + LT_BEGIN_AUTO_TEST_ENV() AUTORUN_TESTS() LT_END_AUTO_TEST_ENV() From 4f8fd38ea3ea2b812e9d1024535d2b998178933f Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Mon, 24 Dec 2018 00:36:23 +0000 Subject: [PATCH 294/623] Added test to prevent nested IPV4 with more than 4 octets --- test/unit/http_utils_test.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/unit/http_utils_test.cpp b/test/unit/http_utils_test.cpp index b5edc4d6..67169bb5 100644 --- a/test/unit/http_utils_test.cpp +++ b/test/unit/http_utils_test.cpp @@ -386,6 +386,10 @@ LT_BEGIN_AUTO_TEST(http_utils_suite, ip_representation6_str_invalid_nested_beyon LT_CHECK_THROW(http::ip_representation("::ffff:192.0.256.128")); LT_END_AUTO_TEST(ip_representation6_str_invalid_nested_beyond255) +LT_BEGIN_AUTO_TEST(http_utils_suite, ip_representation6_str_invalid_nested_more_than_4_parts) + LT_CHECK_THROW(http::ip_representation("::ffff:192.0.5.128.128")); +LT_END_AUTO_TEST(ip_representation6_str_invalid_nested_more_than_4_parts) + LT_BEGIN_AUTO_TEST(http_utils_suite, ip_representation6_str_invalid_nested_not_at_end) LT_CHECK_THROW(http::ip_representation("::ffff:192.0.256.128:ffff")); LT_END_AUTO_TEST(ip_representation6_str_invalid_nested_not_at_end) From 307113d10bc0307d7ffd1094ee08d5d3fd481754 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Mon, 24 Dec 2018 00:58:54 +0000 Subject: [PATCH 295/623] Fixed computation of ip mask --- src/http_utils.cpp | 3 ++- src/httpserver/http_utils.hpp | 2 +- test/unit/http_utils_test.cpp | 13 ++++++++++++- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/http_utils.cpp b/src/http_utils.cpp index 7b59c35d..f1aae3a7 100644 --- a/src/http_utils.cpp +++ b/src/http_utils.cpp @@ -475,7 +475,8 @@ ip_representation::ip_representation(const std::string& ip) else { CLEAR_BIT(mask, y); - y++; + CLEAR_BIT(mask, y+1); + y+=2; } } } diff --git a/src/httpserver/http_utils.hpp b/src/httpserver/http_utils.hpp index d8483f6f..ba0dd357 100644 --- a/src/httpserver/http_utils.hpp +++ b/src/httpserver/http_utils.hpp @@ -302,7 +302,7 @@ struct ip_representation int weight() const { //variable-precision SWAR algorithm - unsigned int x = mask; + unsigned short x = mask; x = x - ((x >> 1) & 0x55555555); x = (x & 0x33333333) + ((x >> 2) & 0x33333333); return (((x + (x >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24; diff --git a/test/unit/http_utils_test.cpp b/test/unit/http_utils_test.cpp index 67169bb5..3c7427d6 100644 --- a/test/unit/http_utils_test.cpp +++ b/test/unit/http_utils_test.cpp @@ -237,7 +237,7 @@ LT_BEGIN_AUTO_TEST(http_utils_suite, ip_representation6_str_mask) LT_CHECK_EQ(test_ip.pieces[14], 0); LT_CHECK_EQ(test_ip.pieces[15], 0); - LT_CHECK_EQ(test_ip.mask, 0xFCFF); + LT_CHECK_EQ(test_ip.mask, 0xF0FF); LT_END_AUTO_TEST(ip_representation6_str_mask) LT_BEGIN_AUTO_TEST(http_utils_suite, ip_representation6_str_nested) @@ -366,6 +366,17 @@ LT_BEGIN_AUTO_TEST(http_utils_suite, ip_representation6_str_loopback) LT_CHECK_EQ(test_ip.mask, 0xFFFF); LT_END_AUTO_TEST(ip_representation6_str_loopback) +LT_BEGIN_AUTO_TEST(http_utils_suite, ip_representation_weight) + LT_CHECK_EQ(http::ip_representation("::1").weight(), 16); + LT_CHECK_EQ(http::ip_representation("192.168.0.1").weight(), 16); + LT_CHECK_EQ(http::ip_representation("192.168.*.*").weight(), 14); + LT_CHECK_EQ(http::ip_representation("::ffff:192.0.*.*").weight(), 14); + LT_CHECK_EQ(http::ip_representation("2001:db8:8714:3a90:*:*").weight(), 12); + LT_CHECK_EQ(http::ip_representation("2001:db8:8714:3a90:8714:2001:db8:3a90").weight(), 16); + LT_CHECK_EQ(http::ip_representation("2001:db8:8714:3a90:8714:2001:*:*").weight(), 12); + LT_CHECK_EQ(http::ip_representation("*:*:*:*:*:*:*:*").weight(), 0); +LT_END_AUTO_TEST(ip_representation_weight) + LT_BEGIN_AUTO_TEST(http_utils_suite, ip_representation6_str_invalid) LT_CHECK_THROW(http::ip_representation("2001:db8:8714:3a90::12:4:4:4")); LT_END_AUTO_TEST(ip_representation6_str_invalid) From 754fdc695016f2656472e7e4b1d52ee3fff848b8 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Mon, 24 Dec 2018 06:40:36 +0000 Subject: [PATCH 296/623] Fix building ip_representation from sockaddr --- src/http_utils.cpp | 6 ++-- test/unit/http_utils_test.cpp | 54 +++++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 4 deletions(-) diff --git a/src/http_utils.cpp b/src/http_utils.cpp index f1aae3a7..3e7aa6af 100644 --- a/src/http_utils.cpp +++ b/src/http_utils.cpp @@ -333,11 +333,9 @@ ip_representation::ip_representation(const struct sockaddr* ip) else { ip_version = http_utils::IPV6; - for(int i=0;i<32;i+=2) + for (int i = 0; i < 16; i++) { - pieces[i/2] = - ((u_char*)&(((struct sockaddr_in6 *)ip)->sin6_addr))[i] + - 16 * ((u_char*)&(((struct sockaddr_in6 *)ip)->sin6_addr))[i+1]; + pieces[i] = ((u_char*)&(((struct sockaddr_in6 *)ip)->sin6_addr))[i]; } } mask = DEFAULT_MASK_VALUE; diff --git a/test/unit/http_utils_test.cpp b/test/unit/http_utils_test.cpp index 3c7427d6..32e426e8 100644 --- a/test/unit/http_utils_test.cpp +++ b/test/unit/http_utils_test.cpp @@ -414,6 +414,60 @@ LT_BEGIN_AUTO_TEST(http_utils_suite, ip_representation6_str_invalid_nested_start LT_CHECK_THROW(http::ip_representation("::ccff:192.0.5.128")); LT_END_AUTO_TEST(ip_representation6_str_invalid_nested_starting_wrong_prefix) +LT_BEGIN_AUTO_TEST(http_utils_suite, ip_representation4_sockaddr) + struct sockaddr_in ip4addr; + + ip4addr.sin_family = AF_INET; + ip4addr.sin_port = htons(3490); + ip4addr.sin_addr.s_addr = inet_addr("127.0.0.1"); + + http::ip_representation test_ip((sockaddr*) &ip4addr); + + LT_CHECK_EQ(test_ip.ip_version, http::http_utils::IPV4); + + for (int i = 0; i < 12; i++) { + LT_CHECK_EQ(test_ip.pieces[i], 0); + } + + LT_CHECK_EQ(test_ip.pieces[12], 127); + LT_CHECK_EQ(test_ip.pieces[13], 0); + LT_CHECK_EQ(test_ip.pieces[14], 0); + LT_CHECK_EQ(test_ip.pieces[15], 1); + + LT_CHECK_EQ(test_ip.mask, 0xFFFF); +LT_END_AUTO_TEST(ip_representation4_sockaddr) + +LT_BEGIN_AUTO_TEST(http_utils_suite, ip_representation6_sockaddr) + struct sockaddr_in6 ip6addr; + + ip6addr.sin6_family = AF_INET6; + ip6addr.sin6_port = htons(3490); + inet_pton(AF_INET6, "2001:db8:8714:3a90::12", &(ip6addr.sin6_addr)); + + http::ip_representation test_ip((sockaddr*) &ip6addr); + + LT_CHECK_EQ(test_ip.ip_version, http::http_utils::IPV6); + + LT_CHECK_EQ(test_ip.pieces[0], 32); + LT_CHECK_EQ(test_ip.pieces[1], 1); + LT_CHECK_EQ(test_ip.pieces[2], 13); + LT_CHECK_EQ(test_ip.pieces[3], 184); + LT_CHECK_EQ(test_ip.pieces[4], 135); + LT_CHECK_EQ(test_ip.pieces[5], 20); + LT_CHECK_EQ(test_ip.pieces[6], 58); + LT_CHECK_EQ(test_ip.pieces[7], 144); + LT_CHECK_EQ(test_ip.pieces[8], 0); + LT_CHECK_EQ(test_ip.pieces[9], 0); + LT_CHECK_EQ(test_ip.pieces[10], 0); + LT_CHECK_EQ(test_ip.pieces[11], 0); + LT_CHECK_EQ(test_ip.pieces[12], 0); + LT_CHECK_EQ(test_ip.pieces[13], 0); + LT_CHECK_EQ(test_ip.pieces[14], 0); + LT_CHECK_EQ(test_ip.pieces[15], 18); + + LT_CHECK_EQ(test_ip.mask, 0xFFFF); +LT_END_AUTO_TEST(ip_representation6_sockaddr) + LT_BEGIN_AUTO_TEST_ENV() AUTORUN_TESTS() LT_END_AUTO_TEST_ENV() From 5568e5a9ed7b6fb9ff4cee0771f9b10f7b0645c2 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Mon, 24 Dec 2018 07:57:19 +0000 Subject: [PATCH 297/623] Fixed load_file method (considers EOF) --- configure.ac | 2 +- src/http_utils.cpp | 13 ++++++++----- src/httpserver/create_webserver.hpp | 6 +++--- test/run_tests | 5 ----- test/test_content | 1 + test/unit/http_utils_test.cpp | 8 ++++++++ 6 files changed, 21 insertions(+), 14 deletions(-) delete mode 100755 test/run_tests create mode 100755 test/test_content diff --git a/configure.ac b/configure.ac index e2db99e7..63f56ab1 100644 --- a/configure.ac +++ b/configure.ac @@ -308,7 +308,7 @@ AC_SUBST(LDFLAGS) AC_SUBST(EXT_LIB_PATH) AC_SUBST(EXT_LIBS) -AC_CONFIG_LINKS([test/run_tests:test/run_tests]) +AC_CONFIG_LINKS([test/test_content:test/test_content]) AC_OUTPUT( libhttpserver.pc diff --git a/src/http_utils.cpp b/src/http_utils.cpp index 3e7aa6af..eab3fadd 100644 --- a/src/http_utils.cpp +++ b/src/http_utils.cpp @@ -524,21 +524,24 @@ bool ip_representation::operator <(const ip_representation& b) const char* load_file (const char *filename) { - char* content = NULL; - ifstream fp(filename, ios::in | ios::binary | ios::ate); if(fp.is_open()) { + fp.seekg(0, fp.end); int size = fp.tellg(); - content = (char*) malloc(size * sizeof(char)); - fp.seekg(0, ios::beg); + fp.seekg(0, fp.beg); + + char* content = new char[size]; fp.read(content, size); fp.close(); + + content[size - 1] = 0; return content; } else + { throw std::invalid_argument("Unable to open file"); - return content; + } } void dump_header_map(std::ostream &os, const std::string &prefix, diff --git a/src/httpserver/create_webserver.hpp b/src/httpserver/create_webserver.hpp index 5476a95d..d3fc23b0 100644 --- a/src/httpserver/create_webserver.hpp +++ b/src/httpserver/create_webserver.hpp @@ -201,21 +201,21 @@ class create_webserver { char* _https_mem_key_pt = http::load_file(https_mem_key.c_str()); _https_mem_key = _https_mem_key_pt; - free(_https_mem_key_pt); + delete _https_mem_key_pt; return *this; } create_webserver& https_mem_cert(const std::string& https_mem_cert) { char* _https_mem_cert_pt = http::load_file(https_mem_cert.c_str()); _https_mem_cert = _https_mem_cert_pt; - free(_https_mem_cert_pt); + delete _https_mem_cert_pt; return *this; } create_webserver& https_mem_trust(const std::string& https_mem_trust) { char* _https_mem_trust_pt = http::load_file(https_mem_trust.c_str()); _https_mem_trust = _https_mem_trust_pt; - free(_https_mem_trust_pt); + delete _https_mem_trust_pt; return *this; } create_webserver& raw_https_mem_key(const std::string& https_mem_key) diff --git a/test/run_tests b/test/run_tests deleted file mode 100755 index 46870932..00000000 --- a/test/run_tests +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/sh - -./basic -./http_utils -./threaded diff --git a/test/test_content b/test/test_content new file mode 100755 index 00000000..5f643138 --- /dev/null +++ b/test/test_content @@ -0,0 +1 @@ +test content of file diff --git a/test/unit/http_utils_test.cpp b/test/unit/http_utils_test.cpp index 32e426e8..6967e570 100644 --- a/test/unit/http_utils_test.cpp +++ b/test/unit/http_utils_test.cpp @@ -468,6 +468,14 @@ LT_BEGIN_AUTO_TEST(http_utils_suite, ip_representation6_sockaddr) LT_CHECK_EQ(test_ip.mask, 0xFFFF); LT_END_AUTO_TEST(ip_representation6_sockaddr) +LT_BEGIN_AUTO_TEST(http_utils_suite, load_file) + LT_CHECK_EQ(std::string(http::load_file("test_content")), "test content of file"); +LT_END_AUTO_TEST(load_file) + +LT_BEGIN_AUTO_TEST(http_utils_suite, load_file_invalid) + LT_CHECK_THROW(http::load_file("test_content_invalid")); +LT_END_AUTO_TEST(load_file_invalid) + LT_BEGIN_AUTO_TEST_ENV() AUTORUN_TESTS() LT_END_AUTO_TEST_ENV() From a36f803377fc3d5bde6c4a26bb98be3709dcd742 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Tue, 25 Dec 2018 01:38:27 +0000 Subject: [PATCH 298/623] Fixed ip_representation comparison method --- src/http_utils.cpp | 37 +++++++++++++++++++++++--------- test/unit/http_utils_test.cpp | 40 +++++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+), 10 deletions(-) diff --git a/src/http_utils.cpp b/src/http_utils.cpp index eab3fadd..36dd2c0f 100644 --- a/src/http_utils.cpp +++ b/src/http_utils.cpp @@ -506,20 +506,37 @@ ip_representation::ip_representation(const std::string& ip) bool ip_representation::operator <(const ip_representation& b) const { - int VAL = 16; - if(this->ip_version == http_utils::IPV4 && this->ip_version == b.ip_version) + long this_score = 0; + long b_score = 0; + for (int i = 0; i < 16; i++) { - VAL = this->ip_version; + if (i == 10 || i == 11) continue; + + if (CHECK_BIT(this->mask, i) && CHECK_BIT(b.mask, i)) + { + this_score += (16 - i) * this->pieces[i]; + b_score += (16 - i) * b.pieces[i]; + } + } + + if (this_score == b_score && + ((this->pieces[10] == 0x00 || this->pieces[10] == 0xFF) && (b.pieces[10] == 0x00 || b.pieces[10] == 0xFF)) && + ((this->pieces[11] == 0x00 || this->pieces[11] == 0xFF) && (b.pieces[11] == 0x00 || b.pieces[11] == 0xFF)) + ) + { + return false; } - for(int i = 16 - VAL; i < 16; i++) + + for (int i = 10; i < 12; i++) { - if(CHECK_BIT(this->mask,i) && - CHECK_BIT(b.mask,i) && - this->pieces[i] < b.pieces[i] - ) - return true; + if (CHECK_BIT(this->mask, i) && CHECK_BIT(b.mask, i)) + { + this_score += (16 - i) * this->pieces[i]; + b_score += (16 - i) * b.pieces[i]; + } } - return false; + + return this_score < b_score; } char* load_file (const char *filename) diff --git a/test/unit/http_utils_test.cpp b/test/unit/http_utils_test.cpp index 6967e570..51c9ec71 100644 --- a/test/unit/http_utils_test.cpp +++ b/test/unit/http_utils_test.cpp @@ -476,6 +476,46 @@ LT_BEGIN_AUTO_TEST(http_utils_suite, load_file_invalid) LT_CHECK_THROW(http::load_file("test_content_invalid")); LT_END_AUTO_TEST(load_file_invalid) +LT_BEGIN_AUTO_TEST(http_utils_suite, ip_representation_less_than) + LT_CHECK_EQ(http::ip_representation("127.0.0.1") < http::ip_representation("127.0.0.2"), true); + LT_CHECK_EQ(http::ip_representation("128.0.0.1") < http::ip_representation("127.0.0.2"), false); + LT_CHECK_EQ(http::ip_representation("127.0.0.2") < http::ip_representation("127.0.0.1"), false); + LT_CHECK_EQ(http::ip_representation("127.0.0.1") < http::ip_representation("127.0.0.1"), false); + LT_CHECK_EQ(http::ip_representation("127.0.0.1") < http::ip_representation("127.0.0.1"), false); + + LT_CHECK_EQ(http::ip_representation("2001:db8::ff00:42:8329") < http::ip_representation("2001:db8::ff00:42:8329"), false); + LT_CHECK_EQ(http::ip_representation("2001:db8::ff00:42:8330") < http::ip_representation("2001:db8::ff00:42:8329"), false); + LT_CHECK_EQ(http::ip_representation("2001:db8::ff00:42:8329") < http::ip_representation("2001:db8::ff00:42:8330"), true); + LT_CHECK_EQ(http::ip_representation("2002:db8::ff00:42:8329") < http::ip_representation("2001:db8::ff00:42:8330"), false); + + LT_CHECK_EQ(http::ip_representation("::192.0.2.128") < http::ip_representation("::192.0.2.128"), false); + LT_CHECK_EQ(http::ip_representation("::192.0.2.129") < http::ip_representation("::192.0.2.128"), false); + LT_CHECK_EQ(http::ip_representation("::192.0.2.128") < http::ip_representation("::192.0.2.129"), true); + + LT_CHECK_EQ(http::ip_representation("::ffff:192.0.2.128") < http::ip_representation("::ffff:192.0.2.128"), false); + LT_CHECK_EQ(http::ip_representation("::ffff:192.0.2.129") < http::ip_representation("::ffff:192.0.2.128"), false); + LT_CHECK_EQ(http::ip_representation("::ffff:192.0.2.128") < http::ip_representation("::ffff:192.0.2.129"), true); + + LT_CHECK_EQ(http::ip_representation("::ffff:192.0.2.128") < http::ip_representation("::192.0.2.128"), false); + LT_CHECK_EQ(http::ip_representation("::ffff:192.0.2.128") < http::ip_representation("::192.0.2.128"), false); + LT_CHECK_EQ(http::ip_representation("::192.0.2.128") < http::ip_representation("::ffff:192.0.2.129"), true); +LT_END_AUTO_TEST(ip_representation_less_than) + +LT_BEGIN_AUTO_TEST(http_utils_suite, ip_representation_less_than_with_masks) + LT_CHECK_EQ(http::ip_representation("127.0.*.*") < http::ip_representation("127.0.0.1"), false); + LT_CHECK_EQ(http::ip_representation("127.0.0.1") < http::ip_representation("127.0.*.*"), false); + LT_CHECK_EQ(http::ip_representation("127.0.0.*") < http::ip_representation("127.0.*.*"), false); + LT_CHECK_EQ(http::ip_representation("127.0.*.1") < http::ip_representation("127.0.0.1"), false); + LT_CHECK_EQ(http::ip_representation("127.0.0.1") < http::ip_representation("127.0.*.1"), false); + LT_CHECK_EQ(http::ip_representation("127.1.0.1") < http::ip_representation("127.0.*.1"), false); + LT_CHECK_EQ(http::ip_representation("127.0.*.1") < http::ip_representation("127.1.0.1"), true); + LT_CHECK_EQ(http::ip_representation("127.1.*.1") < http::ip_representation("127.0.*.1"), false); + LT_CHECK_EQ(http::ip_representation("127.0.*.1") < http::ip_representation("127.1.*.1"), true); + + LT_CHECK_EQ(http::ip_representation("2001:db8::ff00:42:*") < http::ip_representation("2001:db8::ff00:42:8329"), false); + LT_CHECK_EQ(http::ip_representation("2001:db8::ff00:42:8329") < http::ip_representation("2001:db8::ff00:42:*"), false); +LT_END_AUTO_TEST(ip_representation_less_than_with_masks) + LT_BEGIN_AUTO_TEST_ENV() AUTORUN_TESTS() LT_END_AUTO_TEST_ENV() From fa7e247a14ea78facbc876d0185ea94dc50d8188 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Tue, 25 Dec 2018 01:59:18 +0000 Subject: [PATCH 299/623] Added tests for dumping methods --- test/unit/http_utils_test.cpp | 58 ++++++++++++++++++++++++++++++----- 1 file changed, 51 insertions(+), 7 deletions(-) diff --git a/test/unit/http_utils_test.cpp b/test/unit/http_utils_test.cpp index 51c9ec71..9c079394 100644 --- a/test/unit/http_utils_test.cpp +++ b/test/unit/http_utils_test.cpp @@ -150,7 +150,7 @@ LT_END_AUTO_TEST(get_port_null) LT_BEGIN_AUTO_TEST(http_utils_suite, ip_representation4_str) http::ip_representation test_ip("192.168.5.5"); - + LT_CHECK_EQ(test_ip.ip_version, http::http_utils::IPV4); for (int i = 0; i < 12; i++) { @@ -167,7 +167,7 @@ LT_END_AUTO_TEST(ip_representation4_str) LT_BEGIN_AUTO_TEST(http_utils_suite, ip_representation4_str_mask) http::ip_representation test_ip("192.168.*.*"); - + LT_CHECK_EQ(test_ip.ip_version, http::http_utils::IPV4); for (int i = 0; i < 12; i++) { @@ -192,7 +192,7 @@ LT_END_AUTO_TEST(ip_representation4_str_beyond255) LT_BEGIN_AUTO_TEST(http_utils_suite, ip_representation6_str) http::ip_representation test_ip("2001:db8:8714:3a90::12"); - + LT_CHECK_EQ(test_ip.ip_version, http::http_utils::IPV6); LT_CHECK_EQ(test_ip.pieces[0], 32); @@ -217,7 +217,7 @@ LT_END_AUTO_TEST(ip_representation6_str) LT_BEGIN_AUTO_TEST(http_utils_suite, ip_representation6_str_mask) http::ip_representation test_ip("2001:db8:8714:3a90:*:*"); - + LT_CHECK_EQ(test_ip.ip_version, http::http_utils::IPV6); LT_CHECK_EQ(test_ip.pieces[0], 32); @@ -422,7 +422,7 @@ LT_BEGIN_AUTO_TEST(http_utils_suite, ip_representation4_sockaddr) ip4addr.sin_addr.s_addr = inet_addr("127.0.0.1"); http::ip_representation test_ip((sockaddr*) &ip4addr); - + LT_CHECK_EQ(test_ip.ip_version, http::http_utils::IPV4); for (int i = 0; i < 12; i++) { @@ -434,7 +434,7 @@ LT_BEGIN_AUTO_TEST(http_utils_suite, ip_representation4_sockaddr) LT_CHECK_EQ(test_ip.pieces[14], 0); LT_CHECK_EQ(test_ip.pieces[15], 1); - LT_CHECK_EQ(test_ip.mask, 0xFFFF); + LT_CHECK_EQ(test_ip.mask, 0xFFFF); LT_END_AUTO_TEST(ip_representation4_sockaddr) LT_BEGIN_AUTO_TEST(http_utils_suite, ip_representation6_sockaddr) @@ -445,7 +445,7 @@ LT_BEGIN_AUTO_TEST(http_utils_suite, ip_representation6_sockaddr) inet_pton(AF_INET6, "2001:db8:8714:3a90::12", &(ip6addr.sin6_addr)); http::ip_representation test_ip((sockaddr*) &ip6addr); - + LT_CHECK_EQ(test_ip.ip_version, http::http_utils::IPV6); LT_CHECK_EQ(test_ip.pieces[0], 32); @@ -516,6 +516,50 @@ LT_BEGIN_AUTO_TEST(http_utils_suite, ip_representation_less_than_with_masks) LT_CHECK_EQ(http::ip_representation("2001:db8::ff00:42:8329") < http::ip_representation("2001:db8::ff00:42:*"), false); LT_END_AUTO_TEST(ip_representation_less_than_with_masks) +LT_BEGIN_AUTO_TEST(http_utils_suite, dump_header_map) + std::map header_map; + header_map["HEADER_ONE"] = "VALUE_ONE"; + header_map["HEADER_TWO"] = "VALUE_TWO"; + header_map["HEADER_THREE"] = "VALUE_THREE"; + + std::stringstream ss; + http::dump_header_map(ss, "prefix", header_map); + LT_CHECK_EQ(ss.str(), " prefix [HEADER_ONE:\"VALUE_ONE\" HEADER_TWO:\"VALUE_TWO\" HEADER_THREE:\"VALUE_THREE\" ]\n"); +LT_END_AUTO_TEST(dump_header_map) + +LT_BEGIN_AUTO_TEST(http_utils_suite, dump_header_map_no_prefix) + std::map header_map; + header_map["HEADER_ONE"] = "VALUE_ONE"; + header_map["HEADER_TWO"] = "VALUE_TWO"; + header_map["HEADER_THREE"] = "VALUE_THREE"; + + std::stringstream ss; + http::dump_header_map(ss, "", header_map); + LT_CHECK_EQ(ss.str(), " [HEADER_ONE:\"VALUE_ONE\" HEADER_TWO:\"VALUE_TWO\" HEADER_THREE:\"VALUE_THREE\" ]\n"); +LT_END_AUTO_TEST(dump_header_map_no_prefix) + +LT_BEGIN_AUTO_TEST(http_utils_suite, dump_arg_map) + std::map arg_map; + arg_map["ARG_ONE"] = "VALUE_ONE"; + arg_map["ARG_TWO"] = "VALUE_TWO"; + arg_map["ARG_THREE"] = "VALUE_THREE"; + + std::stringstream ss; + http::dump_arg_map(ss, "prefix", arg_map); + LT_CHECK_EQ(ss.str(), " prefix [ARG_ONE:\"VALUE_ONE\" ARG_TWO:\"VALUE_TWO\" ARG_THREE:\"VALUE_THREE\" ]\n"); +LT_END_AUTO_TEST(dump_arg_map) + +LT_BEGIN_AUTO_TEST(http_utils_suite, dump_arg_map_no_prefix) + std::map arg_map; + arg_map["ARG_ONE"] = "VALUE_ONE"; + arg_map["ARG_TWO"] = "VALUE_TWO"; + arg_map["ARG_THREE"] = "VALUE_THREE"; + + std::stringstream ss; + http::dump_arg_map(ss, "", arg_map); + LT_CHECK_EQ(ss.str(), " [ARG_ONE:\"VALUE_ONE\" ARG_TWO:\"VALUE_TWO\" ARG_THREE:\"VALUE_THREE\" ]\n"); +LT_END_AUTO_TEST(dump_arg_map_no_prefix) + LT_BEGIN_AUTO_TEST_ENV() AUTORUN_TESTS() LT_END_AUTO_TEST_ENV() From bf90c0184f5d75a9b047649501fdd0cbbf658e39 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Tue, 25 Dec 2018 02:09:23 +0000 Subject: [PATCH 300/623] Added tests for url tokenization --- test/unit/http_utils_test.cpp | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/test/unit/http_utils_test.cpp b/test/unit/http_utils_test.cpp index 9c079394..5c5ceebd 100644 --- a/test/unit/http_utils_test.cpp +++ b/test/unit/http_utils_test.cpp @@ -70,6 +70,33 @@ LT_BEGIN_AUTO_TEST(http_utils_suite, unescape_plus) LT_CHECK_EQ(expected_size, 3); LT_END_AUTO_TEST(unescape_plus) +LT_BEGIN_AUTO_TEST(http_utils_suite, tokenize_url) + string value = "test/this/url/here"; + string expected_arr[] = { "test", "this", "url", "here" }; + vector expected(expected_arr, expected_arr + sizeof(expected_arr) / sizeof(expected_arr[0])); + vector actual = http::http_utils::tokenize_url(value, '/'); + + LT_CHECK_COLLECTIONS_EQ(expected.begin(), expected.end(), actual.begin()); +LT_END_AUTO_TEST(tokenize_url) + +LT_BEGIN_AUTO_TEST(http_utils_suite, tokenize_url_multiple_spaces) + string value = "test//this//url//here"; + string expected_arr[] = { "test", "this", "url", "here" }; + vector expected(expected_arr, expected_arr + sizeof(expected_arr) / sizeof(expected_arr[0])); + vector actual = http::http_utils::tokenize_url(value, '/'); + + LT_CHECK_COLLECTIONS_EQ(expected.begin(), expected.end(), actual.begin()); +LT_END_AUTO_TEST(tokenize_url_multiple_spaces) + +LT_BEGIN_AUTO_TEST(http_utils_suite, tokenize_url_end_slash) + string value = "test/this/url/here/"; + string expected_arr[] = { "test", "this", "url", "here" }; + vector expected(expected_arr, expected_arr + sizeof(expected_arr) / sizeof(expected_arr[0])); + vector actual = http::http_utils::tokenize_url(value, '/'); + + LT_CHECK_COLLECTIONS_EQ(expected.begin(), expected.end(), actual.begin()); +LT_END_AUTO_TEST(tokenize_url_end_slash) + LT_BEGIN_AUTO_TEST(http_utils_suite, standardize_url) string url = "/", result; result = http::http_utils::standardize_url(url); From 75010ba47ab7207fa5f8fd590a2ea681c80cb60e Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Wed, 26 Dec 2018 02:21:11 +0000 Subject: [PATCH 301/623] Added tests to http_endpoint. Moved http_endpoint to details subpackage to allow for tests --- src/Makefile.am | 4 +- src/{ => details}/http_endpoint.cpp | 50 ++-- src/httpserver.hpp | 1 - .../{ => details}/http_endpoint.hpp | 32 ++- src/httpserver/webserver.hpp | 6 +- src/webserver.cpp | 18 +- test/Makefile.am | 3 +- test/unit/http_endpoint_test.cpp | 224 ++++++++++++++++++ 8 files changed, 292 insertions(+), 46 deletions(-) rename src/{ => details}/http_endpoint.cpp (70%) rename src/httpserver/{ => details}/http_endpoint.hpp (93%) create mode 100644 test/unit/http_endpoint_test.cpp diff --git a/src/Makefile.am b/src/Makefile.am index 8b0a0893..11aff7ed 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -19,9 +19,9 @@ AM_CPPFLAGS = -I../ -I$(srcdir)/httpserver/ METASOURCES = AUTO lib_LTLIBRARIES = libhttpserver.la -libhttpserver_la_SOURCES = string_utilities.cpp webserver.cpp http_utils.cpp http_request.cpp http_response.cpp http_resource.cpp details/comet_manager.cpp http_endpoint.cpp +libhttpserver_la_SOURCES = string_utilities.cpp webserver.cpp http_utils.cpp http_request.cpp http_response.cpp http_resource.cpp details/comet_manager.cpp details/http_endpoint.cpp noinst_HEADERS = httpserver/string_utilities.hpp httpserver/details/modded_request.hpp httpserver/details/http_response_ptr.hpp httpserver/details/cache_entry.hpp httpserver/details/comet_manager.hpp gettext.h -nobase_include_HEADERS = httpserver.hpp httpserver/create_webserver.hpp httpserver/webserver.hpp httpserver/http_utils.hpp httpserver/http_endpoint.hpp httpserver/http_request.hpp httpserver/http_response.hpp httpserver/http_resource.hpp httpserver/binders.hpp httpserver/http_response_builder.hpp +nobase_include_HEADERS = httpserver.hpp httpserver/create_webserver.hpp httpserver/webserver.hpp httpserver/http_utils.hpp httpserver/details/http_endpoint.hpp httpserver/http_request.hpp httpserver/http_response.hpp httpserver/http_resource.hpp httpserver/binders.hpp httpserver/http_response_builder.hpp AM_CXXFLAGS += -fPIC -Wall diff --git a/src/http_endpoint.cpp b/src/details/http_endpoint.cpp similarity index 70% rename from src/http_endpoint.cpp rename to src/details/http_endpoint.cpp index a041bee1..9a4f38f6 100644 --- a/src/http_endpoint.cpp +++ b/src/details/http_endpoint.cpp @@ -18,7 +18,7 @@ USA */ -#include "http_endpoint.hpp" +#include "details/http_endpoint.hpp" #include "http_utils.hpp" #include "string_utilities.hpp" @@ -27,17 +27,20 @@ using namespace std; namespace httpserver { +namespace details +{ + using namespace http; -webserver::http_endpoint::~http_endpoint() +http_endpoint::~http_endpoint() { if(reg_compiled) { - regfree(&(this->re_url_modded)); + regfree(&(this->re_url_normalized)); } } -webserver::http_endpoint::http_endpoint +http_endpoint::http_endpoint ( const string& url, bool family, @@ -47,7 +50,7 @@ webserver::http_endpoint::http_endpoint family_url(family), reg_compiled(false) { - this->url_modded = use_regex ? "^/" : "/"; + this->url_normalized = use_regex ? "^/" : "/"; vector parts; #ifdef CASE_INSENSITIVE @@ -67,7 +70,7 @@ webserver::http_endpoint::http_endpoint { if(!registration) { - this->url_modded += (first ? "" : "/") + parts[i]; + this->url_normalized += (first ? "" : "/") + parts[i]; first = false; this->url_pieces.push_back(parts[i]); @@ -79,12 +82,12 @@ webserver::http_endpoint::http_endpoint { if(first) { - this->url_modded = (parts[i][0] == '^' ? "" : this->url_modded) + parts[i]; + this->url_normalized = (parts[i][0] == '^' ? "" : this->url_normalized) + parts[i]; first = false; } else { - this->url_modded += "/" + parts[i]; + this->url_normalized += "/" + parts[i]; } this->url_pieces.push_back(parts[i]); @@ -96,7 +99,7 @@ webserver::http_endpoint::http_endpoint std::string::size_type bar = parts[i].find_first_of('|'); this->url_pars.push_back(parts[i].substr(1, bar != string::npos ? bar - 1 : parts[i].size() - 2)); - this->url_modded += (first ? "" : "/") + (bar != string::npos ? parts[i].substr(bar + 1, parts[i].size() - bar - 2) : "([^\\/]+)"); + this->url_normalized += (first ? "" : "/") + (bar != string::npos ? parts[i].substr(bar + 1, parts[i].size() - bar - 2) : "([^\\/]+)"); first = false; @@ -107,17 +110,17 @@ webserver::http_endpoint::http_endpoint if(use_regex) { - this->url_modded += "$"; - regcomp(&(this->re_url_modded), url_modded.c_str(), + this->url_normalized += "$"; + regcomp(&(this->re_url_normalized), url_normalized.c_str(), REG_EXTENDED|REG_ICASE|REG_NOSUB ); this->reg_compiled = true; } } -webserver::http_endpoint::http_endpoint(const webserver::http_endpoint& h): +http_endpoint::http_endpoint(const http_endpoint& h): url_complete(h.url_complete), - url_modded(h.url_modded), + url_normalized(h.url_normalized), url_pars(h.url_pars), url_pieces(h.url_pieces), chunk_positions(h.chunk_positions), @@ -125,19 +128,19 @@ webserver::http_endpoint::http_endpoint(const webserver::http_endpoint& h): reg_compiled(h.reg_compiled) { if(this->reg_compiled) - regcomp(&(this->re_url_modded), url_modded.c_str(), + regcomp(&(this->re_url_normalized), url_normalized.c_str(), REG_EXTENDED|REG_ICASE|REG_NOSUB ); } -webserver::http_endpoint& webserver::http_endpoint::operator =(const webserver::http_endpoint& h) +http_endpoint& http_endpoint::operator =(const http_endpoint& h) { this->url_complete = h.url_complete; - this->url_modded = h.url_modded; + this->url_normalized = h.url_normalized; this->family_url = h.family_url; this->reg_compiled = h.reg_compiled; if(this->reg_compiled) - regcomp(&(this->re_url_modded), url_modded.c_str(), + regcomp(&(this->re_url_normalized), url_normalized.c_str(), REG_EXTENDED|REG_ICASE|REG_NOSUB ); this->url_pars = h.url_pars; @@ -146,16 +149,16 @@ webserver::http_endpoint& webserver::http_endpoint::operator =(const webserver:: return *this; } -bool webserver::http_endpoint::operator <(const webserver::http_endpoint& b) const +bool http_endpoint::operator <(const http_endpoint& b) const { - COMPARATOR(this->url_modded, b.url_modded, std::toupper); + COMPARATOR(this->url_normalized, b.url_normalized, std::toupper); } -bool webserver::http_endpoint::match(const webserver::http_endpoint& url) const +bool http_endpoint::match(const http_endpoint& url) const { - if(!this->family_url || url.url_pieces.size() < this->url_pieces.size()) - return regexec(&(this->re_url_modded), url.url_complete.c_str(), 0, NULL, 0) == 0; + if(!this->family_url || url.url_pieces.size() < this->url_pieces.size()) + return regexec(&(this->re_url_normalized), url.url_complete.c_str(), 0, NULL, 0) == 0; string nn = "/"; bool first = true; @@ -164,8 +167,9 @@ bool webserver::http_endpoint::match(const webserver::http_endpoint& url) const nn += (first ? "" : "/") + url.url_pieces[i]; first = false; } - return regexec(&(this->re_url_modded), nn.c_str(), 0, NULL, 0) == 0; + return regexec(&(this->re_url_normalized), nn.c_str(), 0, NULL, 0) == 0; } }; +}; diff --git a/src/httpserver.hpp b/src/httpserver.hpp index 2174240f..2cfbb6cb 100644 --- a/src/httpserver.hpp +++ b/src/httpserver.hpp @@ -24,7 +24,6 @@ #define _HTTPSERVER_HPP_INSIDE_ #include "httpserver/http_utils.hpp" -#include "httpserver/http_endpoint.hpp" #include "httpserver/http_resource.hpp" #include "httpserver/http_response.hpp" #include "httpserver/http_response_builder.hpp" diff --git a/src/httpserver/http_endpoint.hpp b/src/httpserver/details/http_endpoint.hpp similarity index 93% rename from src/httpserver/http_endpoint.hpp rename to src/httpserver/details/http_endpoint.hpp index 1efa99d3..7acf1b29 100644 --- a/src/httpserver/http_endpoint.hpp +++ b/src/httpserver/details/http_endpoint.hpp @@ -31,18 +31,18 @@ #include #include -#include "webserver.hpp" - namespace httpserver { -class webserver; +namespace details +{ + class http_resource; /** * Class representing an Http Endpoint. It is an abstraction used by the APIs. **/ -class webserver::http_endpoint +class http_endpoint { public: /** @@ -88,6 +88,11 @@ class webserver::http_endpoint return this->url_complete; } + const std::string& get_url_normalized() const + { + return this->url_normalized; + } + /** * Method used to get all pars defined inside an url. * @return a vector of strings representing all found pars. @@ -115,6 +120,16 @@ class webserver::http_endpoint return this->chunk_positions; } + const bool is_family_url() const + { + return this->family_url; + } + + const bool is_regex_compiled() const + { + return this->reg_compiled; + } + /** * Default constructor of the class. * @param family boolean that indicates if the endpoint is a family endpoint. @@ -124,7 +139,7 @@ class webserver::http_endpoint **/ http_endpoint(bool family = false): url_complete("/"), - url_modded("/"), + url_normalized("/"), family_url(family), reg_compiled(false) { @@ -147,6 +162,7 @@ class webserver::http_endpoint bool use_regex = true ); + private: /** * The complete url extracted **/ @@ -155,7 +171,7 @@ class webserver::http_endpoint /** * The url standardized in order to use standard comparisons or regexes **/ - std::string url_modded; + std::string url_normalized; /** * Vector containing parameters extracted from url @@ -175,7 +191,7 @@ class webserver::http_endpoint /** * Regex used in comparisons **/ - regex_t re_url_modded; + regex_t re_url_normalized; /** * Boolean indicating wheter the endpoint represents a family @@ -188,5 +204,7 @@ class webserver::http_endpoint bool reg_compiled; }; +}; + }; #endif diff --git a/src/httpserver/webserver.hpp b/src/httpserver/webserver.hpp index 5fc45017..ac348019 100644 --- a/src/httpserver/webserver.hpp +++ b/src/httpserver/webserver.hpp @@ -46,6 +46,8 @@ #include "httpserver/create_webserver.hpp" #include "httpserver/http_response.hpp" +#include "details/http_endpoint.hpp" + namespace httpserver { class http_resource; @@ -173,8 +175,6 @@ class webserver webserver& operator=(const webserver& other); private: - class http_endpoint; - const uint16_t port; http::http_utils::start_method_T start_method; const int max_threads; @@ -219,7 +219,7 @@ class webserver render_ptr method_not_allowed_resource; render_ptr method_not_acceptable_resource; render_ptr internal_error_resource; - std::map registered_resources; + std::map registered_resources; std::map registered_resources_str; std::map response_cache; diff --git a/src/webserver.cpp b/src/webserver.cpp index 3f8af704..cf120d48 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -49,7 +49,7 @@ #include "http_response.hpp" #include "http_request.hpp" #include "http_response_builder.hpp" -#include "http_endpoint.hpp" +#include "details/http_endpoint.hpp" #include "string_utilities.hpp" #include "create_webserver.hpp" #include "details/comet_manager.hpp" @@ -225,10 +225,10 @@ void webserver::request_completed ( bool webserver::register_resource(const std::string& resource, http_resource* hrm, bool family) { - http_endpoint idx(resource, family, true, regex_checking); + details::http_endpoint idx(resource, family, true, regex_checking); - pair::iterator, bool> result = registered_resources.insert( - map::value_type(idx, hrm) + pair::iterator, bool> result = registered_resources.insert( + map::value_type(idx, hrm) ); if(result.second) @@ -436,9 +436,9 @@ bool webserver::stop() void webserver::unregister_resource(const string& resource) { - http_endpoint he(resource); + details::http_endpoint he(resource); this->registered_resources.erase(he); - this->registered_resources.erase(he.url_complete); + this->registered_resources.erase(he.get_url_complete()); } void webserver::ban_ip(const string& ip) @@ -813,11 +813,11 @@ int webserver::finalize_answer( if(regex_checking) { - map::iterator found_endpoint; + map::iterator found_endpoint; - http_endpoint endpoint(st_url, false, false, regex_checking); + details::http_endpoint endpoint(st_url, false, false, regex_checking); - map::iterator it; + map::iterator it; size_t len = 0; size_t tot_len = 0; diff --git a/test/Makefile.am b/test/Makefile.am index cef3ed09..5acd1456 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -19,7 +19,7 @@ LDADD = $(top_builddir)/src/libhttpserver.la AM_CPPFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/src/httpserver/ METASOURCES = AUTO -check_PROGRAMS = basic http_utils threaded string_utilities +check_PROGRAMS = basic http_utils threaded string_utilities http_endpoint MOSTLYCLEANFILES = *.gcda *.gcno *.gcov @@ -27,6 +27,7 @@ basic_SOURCES = integ/basic.cpp threaded_SOURCES = integ/threaded.cpp http_utils_SOURCES = unit/http_utils_test.cpp string_utilities_SOURCES = unit/string_utilities_test.cpp +http_endpoint_SOURCES = unit/http_endpoint_test.cpp noinst_HEADERS = littletest.hpp AM_CXXFLAGS += -lcurl -Wall -fPIC diff --git a/test/unit/http_endpoint_test.cpp b/test/unit/http_endpoint_test.cpp new file mode 100644 index 00000000..22b787e0 --- /dev/null +++ b/test/unit/http_endpoint_test.cpp @@ -0,0 +1,224 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011-2019 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#include "littletest.hpp" +#include "details/http_endpoint.hpp" + +using namespace httpserver; +using namespace std; +using namespace details; + +LT_BEGIN_SUITE(http_endpoint_suite) + void set_up() + { + } + + void tear_down() + { + } +LT_END_SUITE(http_endpoint_suite) + +LT_BEGIN_AUTO_TEST(http_endpoint_suite, http_endpoint_default) + http_endpoint test_endpoint; + + LT_CHECK_EQ(test_endpoint.get_url_complete(), "/"); + LT_CHECK_EQ(test_endpoint.get_url_normalized(), "/"); + + LT_CHECK_EQ(test_endpoint.get_url_pars().size(), 0); + LT_CHECK_EQ(test_endpoint.get_url_pieces().size(), 0); + LT_CHECK_EQ(test_endpoint.get_chunk_positions().size(), 0); + + LT_CHECK_EQ(test_endpoint.is_family_url(), false); + LT_CHECK_EQ(test_endpoint.is_regex_compiled(), false); +LT_END_AUTO_TEST(http_endpoint_default) + +LT_BEGIN_AUTO_TEST(http_endpoint_suite, http_endpoint_default_family) + http_endpoint test_endpoint(true); + + LT_CHECK_EQ(test_endpoint.get_url_complete(), "/"); + LT_CHECK_EQ(test_endpoint.get_url_normalized(), "/"); + + LT_CHECK_EQ(test_endpoint.get_url_pars().size(), 0); + LT_CHECK_EQ(test_endpoint.get_url_pieces().size(), 0); + LT_CHECK_EQ(test_endpoint.get_chunk_positions().size(), 0); + + LT_CHECK_EQ(test_endpoint.is_family_url(), true); + LT_CHECK_EQ(test_endpoint.is_regex_compiled(), false); +LT_END_AUTO_TEST(http_endpoint_default_family) + +LT_BEGIN_AUTO_TEST(http_endpoint_suite, http_endpoint_from_string_default) + http_endpoint test_endpoint(std::string("/path/to/resource")); + + LT_CHECK_EQ(test_endpoint.get_url_complete(), "/path/to/resource"); + LT_CHECK_EQ(test_endpoint.get_url_normalized(), "^/path/to/resource$"); + + LT_CHECK_EQ(test_endpoint.get_url_pars().size(), 0); + + string expected_arr[] = { "path", "to", "resource" }; + vector expected_pieces(expected_arr, expected_arr + sizeof(expected_arr) / sizeof(expected_arr[0])); + LT_CHECK_COLLECTIONS_EQ(test_endpoint.get_url_pieces().begin(), test_endpoint.get_url_pieces().end(), expected_pieces.begin()); + + LT_CHECK_EQ(test_endpoint.get_chunk_positions().size(), 0); + + LT_CHECK_EQ(test_endpoint.is_family_url(), false); + LT_CHECK_EQ(test_endpoint.is_regex_compiled(), true); +LT_END_AUTO_TEST(http_endpoint_from_string_default) + +LT_BEGIN_AUTO_TEST(http_endpoint_suite, http_endpoint_from_string_not_beginning_with_slash) + http_endpoint test_endpoint(std::string("path/to/resource")); + + LT_CHECK_EQ(test_endpoint.get_url_complete(), "/path/to/resource"); + LT_CHECK_EQ(test_endpoint.get_url_normalized(), "^/path/to/resource$"); + + LT_CHECK_EQ(test_endpoint.get_url_pars().size(), 0); + + string expected_arr[] = { "path", "to", "resource" }; + vector expected_pieces(expected_arr, expected_arr + sizeof(expected_arr) / sizeof(expected_arr[0])); + LT_CHECK_COLLECTIONS_EQ(test_endpoint.get_url_pieces().begin(), test_endpoint.get_url_pieces().end(), expected_pieces.begin()); + + LT_CHECK_EQ(test_endpoint.get_chunk_positions().size(), 0); + + LT_CHECK_EQ(test_endpoint.is_family_url(), false); + LT_CHECK_EQ(test_endpoint.is_regex_compiled(), true); +LT_END_AUTO_TEST(http_endpoint_from_string_not_beginning_with_slash) + +LT_BEGIN_AUTO_TEST(http_endpoint_suite, http_endpoint_from_string_family) + http_endpoint test_endpoint(std::string("/path/to/resource"), true); + + LT_CHECK_EQ(test_endpoint.get_url_complete(), "/path/to/resource"); + LT_CHECK_EQ(test_endpoint.get_url_normalized(), "^/path/to/resource$"); + + LT_CHECK_EQ(test_endpoint.get_url_pars().size(), 0); + + string expected_arr[] = { "path", "to", "resource" }; + vector expected_pieces(expected_arr, expected_arr + sizeof(expected_arr) / sizeof(expected_arr[0])); + LT_CHECK_COLLECTIONS_EQ(test_endpoint.get_url_pieces().begin(), test_endpoint.get_url_pieces().end(), expected_pieces.begin()); + + LT_CHECK_EQ(test_endpoint.get_chunk_positions().size(), 0); + + LT_CHECK_EQ(test_endpoint.is_family_url(), true); + LT_CHECK_EQ(test_endpoint.is_regex_compiled(), true); +LT_END_AUTO_TEST(http_endpoint_from_string_family) + +LT_BEGIN_AUTO_TEST(http_endpoint_suite, http_endpoint_from_string_no_regex) + http_endpoint test_endpoint(std::string("/path/to/resource"), false, false, false); + + LT_CHECK_EQ(test_endpoint.get_url_complete(), "/path/to/resource"); + LT_CHECK_EQ(test_endpoint.get_url_normalized(), "/path/to/resource"); + + LT_CHECK_EQ(test_endpoint.get_url_pars().size(), 0); + + string expected_arr[] = { "path", "to", "resource" }; + vector expected_pieces(expected_arr, expected_arr + sizeof(expected_arr) / sizeof(expected_arr[0])); + LT_CHECK_COLLECTIONS_EQ(test_endpoint.get_url_pieces().begin(), test_endpoint.get_url_pieces().end(), expected_pieces.begin()); + + LT_CHECK_EQ(test_endpoint.get_chunk_positions().size(), 0); + + LT_CHECK_EQ(test_endpoint.is_family_url(), false); + LT_CHECK_EQ(test_endpoint.is_regex_compiled(), false); +LT_END_AUTO_TEST(http_endpoint_from_string_no_regex) + +LT_BEGIN_AUTO_TEST(http_endpoint_suite, http_endpoint_registration) + http_endpoint test_endpoint(std::string("/path/to/resource"), false, true); + + LT_CHECK_EQ(test_endpoint.get_url_complete(), "/path/to/resource"); + LT_CHECK_EQ(test_endpoint.get_url_normalized(), "^/path/to/resource$"); + + LT_CHECK_EQ(test_endpoint.get_url_pars().size(), 0); + + string expected_arr[] = { "path", "to", "resource" }; + vector expected_pieces(expected_arr, expected_arr + sizeof(expected_arr) / sizeof(expected_arr[0])); + LT_CHECK_COLLECTIONS_EQ(test_endpoint.get_url_pieces().begin(), test_endpoint.get_url_pieces().end(), expected_pieces.begin()); + + LT_CHECK_EQ(test_endpoint.get_chunk_positions().size(), 0); + + LT_CHECK_EQ(test_endpoint.is_family_url(), false); + LT_CHECK_EQ(test_endpoint.is_regex_compiled(), true); +LT_END_AUTO_TEST(http_endpoint_registration) + +LT_BEGIN_AUTO_TEST(http_endpoint_suite, http_endpoint_registration_nested_regex) + http_endpoint test_endpoint(std::string("/path/to/resource/with/[0-9]+/to/fetch"), false, true); + + LT_CHECK_EQ(test_endpoint.get_url_complete(), "/path/to/resource/with/[0-9]+/to/fetch"); + LT_CHECK_EQ(test_endpoint.get_url_normalized(), "^/path/to/resource/with/[0-9]+/to/fetch$"); + + LT_CHECK_EQ(test_endpoint.get_url_pars().size(), 0); + + string expected_arr[] = { "path", "to", "resource", "with", "[0-9]+", "to", "fetch" }; + vector expected_pieces(expected_arr, expected_arr + sizeof(expected_arr) / sizeof(expected_arr[0])); + LT_CHECK_COLLECTIONS_EQ(test_endpoint.get_url_pieces().begin(), test_endpoint.get_url_pieces().end(), expected_pieces.begin()); + + LT_CHECK_EQ(test_endpoint.get_chunk_positions().size(), 0); + + LT_CHECK_EQ(test_endpoint.is_family_url(), false); + LT_CHECK_EQ(test_endpoint.is_regex_compiled(), true); +LT_END_AUTO_TEST(http_endpoint_registration_nested_regex) + +LT_BEGIN_AUTO_TEST(http_endpoint_suite, http_endpoint_registration_arg) + http_endpoint test_endpoint(std::string("/path/to/resource/with/{arg}/to/fetch"), false, true); + + LT_CHECK_EQ(test_endpoint.get_url_complete(), "/path/to/resource/with/{arg}/to/fetch"); + LT_CHECK_EQ(test_endpoint.get_url_normalized(), "^/path/to/resource/with/([^\\/]+)/to/fetch$"); + + string expected_pars_arr[] = { "arg" }; + vector expected_pars(expected_pars_arr, expected_pars_arr + sizeof(expected_pars_arr) / sizeof(expected_pars_arr[0])); + LT_CHECK_COLLECTIONS_EQ(test_endpoint.get_url_pars().begin(), test_endpoint.get_url_pars().end(), expected_pars.begin()); + + string expected_arr[] = { "path", "to", "resource", "with", "{arg}", "to", "fetch" }; + vector expected_pieces(expected_arr, expected_arr + sizeof(expected_arr) / sizeof(expected_arr[0])); + LT_CHECK_COLLECTIONS_EQ(test_endpoint.get_url_pieces().begin(), test_endpoint.get_url_pieces().end(), expected_pieces.begin()); + + int expected_chunk_positions_arr[] = { 4 }; + vector expected_chunk_positions(expected_chunk_positions_arr, expected_chunk_positions_arr + sizeof(expected_chunk_positions_arr) / sizeof(expected_chunk_positions_arr[0])); + LT_CHECK_COLLECTIONS_EQ(test_endpoint.get_chunk_positions().begin(), test_endpoint.get_chunk_positions().end(), expected_chunk_positions.begin()); + + LT_CHECK_EQ(test_endpoint.is_family_url(), false); + LT_CHECK_EQ(test_endpoint.is_regex_compiled(), true); +LT_END_AUTO_TEST(http_endpoint_registration_arg) + +LT_BEGIN_AUTO_TEST(http_endpoint_suite, http_endpoint_registration_arg_custom_regex) + http_endpoint test_endpoint(std::string("/path/to/resource/with/{arg|([0-9]+)}/to/fetch"), false, true); + + LT_CHECK_EQ(test_endpoint.get_url_complete(), "/path/to/resource/with/{arg|([0-9]+)}/to/fetch"); + LT_CHECK_EQ(test_endpoint.get_url_normalized(), "^/path/to/resource/with/([0-9]+)/to/fetch$"); + + string expected_pars_arr[] = { "arg" }; + vector expected_pars(expected_pars_arr, expected_pars_arr + sizeof(expected_pars_arr) / sizeof(expected_pars_arr[0])); + LT_CHECK_COLLECTIONS_EQ(test_endpoint.get_url_pars().begin(), test_endpoint.get_url_pars().end(), expected_pars.begin()); + + string expected_arr[] = { "path", "to", "resource", "with", "{arg|([0-9]+)}", "to", "fetch" }; + vector expected_pieces(expected_arr, expected_arr + sizeof(expected_arr) / sizeof(expected_arr[0])); + LT_CHECK_COLLECTIONS_EQ(test_endpoint.get_url_pieces().begin(), test_endpoint.get_url_pieces().end(), expected_pieces.begin()); + + int expected_chunk_positions_arr[] = { 4 }; + vector expected_chunk_positions(expected_chunk_positions_arr, expected_chunk_positions_arr + sizeof(expected_chunk_positions_arr) / sizeof(expected_chunk_positions_arr[0])); + LT_CHECK_COLLECTIONS_EQ(test_endpoint.get_chunk_positions().begin(), test_endpoint.get_chunk_positions().end(), expected_chunk_positions.begin()); + + LT_CHECK_EQ(test_endpoint.is_family_url(), false); + LT_CHECK_EQ(test_endpoint.is_regex_compiled(), true); +LT_END_AUTO_TEST(http_endpoint_registration_arg_custom_regex) + +LT_BEGIN_AUTO_TEST(http_endpoint_suite, http_endpoint_registration_invalid_arg) + LT_CHECK_THROW(http_endpoint(std::string("/path/to/resource/with/{}/to/fetch"), false, true)); +LT_END_AUTO_TEST(http_endpoint_registration_invalid_arg) + +LT_BEGIN_AUTO_TEST_ENV() + AUTORUN_TESTS() +LT_END_AUTO_TEST_ENV() From 0a94afd10aa4fe9b34bf01a96a67f2d5b8b76011 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Wed, 26 Dec 2018 02:32:48 +0000 Subject: [PATCH 302/623] Prevent warnings --- src/http_utils.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/http_utils.cpp b/src/http_utils.cpp index 36dd2c0f..14bdb73d 100644 --- a/src/http_utils.cpp +++ b/src/http_utils.cpp @@ -90,12 +90,12 @@ const int http_utils::http_gone = MHD_HTTP_GONE; const int http_utils::http_length_required = MHD_HTTP_LENGTH_REQUIRED; const int http_utils::http_precondition_failed = MHD_HTTP_PRECONDITION_FAILED; const int http_utils::http_request_entity_too_large = - MHD_HTTP_REQUEST_ENTITY_TOO_LARGE; -const int http_utils::http_request_uri_too_long = MHD_HTTP_REQUEST_URI_TOO_LONG; + MHD_HTTP_PAYLOAD_TOO_LARGE; +const int http_utils::http_request_uri_too_long = MHD_HTTP_URI_TOO_LONG; const int http_utils::http_unsupported_media_type = MHD_HTTP_UNSUPPORTED_MEDIA_TYPE; const int http_utils::http_requested_range_not_satisfiable = - MHD_HTTP_REQUESTED_RANGE_NOT_SATISFIABLE; + MHD_HTTP_RANGE_NOT_SATISFIABLE; const int http_utils::http_expectation_failed = MHD_HTTP_EXPECTATION_FAILED; const int http_utils::http_unprocessable_entity = MHD_HTTP_UNPROCESSABLE_ENTITY; const int http_utils::http_locked = MHD_HTTP_LOCKED; @@ -355,7 +355,7 @@ ip_representation::ip_representation(const std::string& ip) throw std::invalid_argument("IP is badly formatted. Max 8 parts in IPV6."); } - int omitted = 8 - (parts.size() - 1); + unsigned int omitted = 8 - (parts.size() - 1); if (omitted != 0) { int empty_count = 0; @@ -416,7 +416,7 @@ ip_representation::ip_representation(const std::string& ip) y += 2; } else - { + { if(parts[i].find('.') != std::string::npos) { if(y != 12) From 36cef5bce3337caa858bc21aa0ca48c33d236b17 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Wed, 26 Dec 2018 18:01:25 +0000 Subject: [PATCH 303/623] Update version --- ChangeLog | 4 ++++ configure.ac | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index a01584b5..38eb9bd9 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +Thu Dec 26 10:00:30 2018 -0800 + Fixed IPV6 parsing logic. + Added tests to support IP parsing, URL parsing and utilities + Thu Nov 22 20:58:00 2018 -0800 Solved problem with the server not being able to start on mac os diff --git a/configure.ac b/configure.ac index 63f56ab1..82d2b31a 100644 --- a/configure.ac +++ b/configure.ac @@ -21,7 +21,7 @@ AC_PREREQ(2.57) m4_define([libhttpserver_MAJOR_VERSION],[0])dnl -m4_define([libhttpserver_MINOR_VERSION],[15])dnl +m4_define([libhttpserver_MINOR_VERSION],[16])dnl m4_define([libhttpserver_REVISION],[0])dnl m4_define([libhttpserver_PKG_VERSION],[libhttpserver_MAJOR_VERSION.libhttpserver_MINOR_VERSION.libhttpserver_REVISION])dnl m4_define([libhttpserver_LDF_VERSION],[libhttpserver_MAJOR_VERSION:libhttpserver_MINOR_VERSION:libhttpserver_REVISION])dnl From 8e7e234bf8dd44c073b30e283cc356d2b0632fb7 Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Sun, 30 Dec 2018 03:36:36 +0000 Subject: [PATCH 304/623] Fixed matching for endpoint ending with '/'. Added unit tests on http_endpoint to verify the matching logic --- src/details/http_endpoint.cpp | 10 +- src/httpserver/details/http_endpoint.hpp | 9 +- test/unit/http_endpoint_test.cpp | 142 +++++++++++++++++++---- 3 files changed, 132 insertions(+), 29 deletions(-) diff --git a/src/details/http_endpoint.cpp b/src/details/http_endpoint.cpp index 9a4f38f6..1b70f0be 100644 --- a/src/details/http_endpoint.cpp +++ b/src/details/http_endpoint.cpp @@ -59,8 +59,15 @@ http_endpoint::http_endpoint url_complete = url; #endif - if(url_complete[0] != '/') + if (url_complete[url_complete.size() - 1] == '/') + { + url_complete = url_complete.substr(0, url_complete.size() - 1); + } + + if (url_complete[0] != '/') + { url_complete = "/" + url_complete; + } parts = http_utils::tokenize_url(url); string buffered; @@ -156,6 +163,7 @@ bool http_endpoint::operator <(const http_endpoint& b) const bool http_endpoint::match(const http_endpoint& url) const { + if (!this->reg_compiled) throw std::invalid_argument("Cannot run match. Regex suppressed."); if(!this->family_url || url.url_pieces.size() < this->url_pieces.size()) return regexec(&(this->re_url_normalized), url.url_complete.c_str(), 0, NULL, 0) == 0; diff --git a/src/httpserver/details/http_endpoint.hpp b/src/httpserver/details/http_endpoint.hpp index 7acf1b29..2527f66c 100644 --- a/src/httpserver/details/http_endpoint.hpp +++ b/src/httpserver/details/http_endpoint.hpp @@ -132,15 +132,11 @@ class http_endpoint /** * Default constructor of the class. - * @param family boolean that indicates if the endpoint is a family endpoint. - * A family endpoint is an endpoint that identifies a root and all its child like the same resource. - * For example, if I identify "/path/" like a family endpoint and I associate to it the resource "A", also - * "/path/to/res/" is automatically associated to resource "A". **/ - http_endpoint(bool family = false): + http_endpoint(): url_complete("/"), url_normalized("/"), - family_url(family), + family_url(false), reg_compiled(false) { } @@ -161,7 +157,6 @@ class http_endpoint bool registration = false, bool use_regex = true ); - private: /** * The complete url extracted diff --git a/test/unit/http_endpoint_test.cpp b/test/unit/http_endpoint_test.cpp index 22b787e0..ed461792 100644 --- a/test/unit/http_endpoint_test.cpp +++ b/test/unit/http_endpoint_test.cpp @@ -49,22 +49,26 @@ LT_BEGIN_AUTO_TEST(http_endpoint_suite, http_endpoint_default) LT_CHECK_EQ(test_endpoint.is_regex_compiled(), false); LT_END_AUTO_TEST(http_endpoint_default) -LT_BEGIN_AUTO_TEST(http_endpoint_suite, http_endpoint_default_family) - http_endpoint test_endpoint(true); +LT_BEGIN_AUTO_TEST(http_endpoint_suite, http_endpoint_from_string_default) + http_endpoint test_endpoint("/path/to/resource"); - LT_CHECK_EQ(test_endpoint.get_url_complete(), "/"); - LT_CHECK_EQ(test_endpoint.get_url_normalized(), "/"); + LT_CHECK_EQ(test_endpoint.get_url_complete(), "/path/to/resource"); + LT_CHECK_EQ(test_endpoint.get_url_normalized(), "^/path/to/resource$"); LT_CHECK_EQ(test_endpoint.get_url_pars().size(), 0); - LT_CHECK_EQ(test_endpoint.get_url_pieces().size(), 0); + + string expected_arr[] = { "path", "to", "resource" }; + vector expected_pieces(expected_arr, expected_arr + sizeof(expected_arr) / sizeof(expected_arr[0])); + LT_CHECK_COLLECTIONS_EQ(test_endpoint.get_url_pieces().begin(), test_endpoint.get_url_pieces().end(), expected_pieces.begin()); + LT_CHECK_EQ(test_endpoint.get_chunk_positions().size(), 0); - LT_CHECK_EQ(test_endpoint.is_family_url(), true); - LT_CHECK_EQ(test_endpoint.is_regex_compiled(), false); -LT_END_AUTO_TEST(http_endpoint_default_family) + LT_CHECK_EQ(test_endpoint.is_family_url(), false); + LT_CHECK_EQ(test_endpoint.is_regex_compiled(), true); +LT_END_AUTO_TEST(http_endpoint_from_string_default) -LT_BEGIN_AUTO_TEST(http_endpoint_suite, http_endpoint_from_string_default) - http_endpoint test_endpoint(std::string("/path/to/resource")); +LT_BEGIN_AUTO_TEST(http_endpoint_suite, http_endpoint_from_string_not_beginning_with_slash) + http_endpoint test_endpoint("path/to/resource"); LT_CHECK_EQ(test_endpoint.get_url_complete(), "/path/to/resource"); LT_CHECK_EQ(test_endpoint.get_url_normalized(), "^/path/to/resource$"); @@ -79,10 +83,10 @@ LT_BEGIN_AUTO_TEST(http_endpoint_suite, http_endpoint_from_string_default) LT_CHECK_EQ(test_endpoint.is_family_url(), false); LT_CHECK_EQ(test_endpoint.is_regex_compiled(), true); -LT_END_AUTO_TEST(http_endpoint_from_string_default) +LT_END_AUTO_TEST(http_endpoint_from_string_not_beginning_with_slash) -LT_BEGIN_AUTO_TEST(http_endpoint_suite, http_endpoint_from_string_not_beginning_with_slash) - http_endpoint test_endpoint(std::string("path/to/resource")); +LT_BEGIN_AUTO_TEST(http_endpoint_suite, http_endpoint_from_string_ending_with_slash) + http_endpoint test_endpoint("path/to/resource/"); LT_CHECK_EQ(test_endpoint.get_url_complete(), "/path/to/resource"); LT_CHECK_EQ(test_endpoint.get_url_normalized(), "^/path/to/resource$"); @@ -97,10 +101,10 @@ LT_BEGIN_AUTO_TEST(http_endpoint_suite, http_endpoint_from_string_not_beginning_ LT_CHECK_EQ(test_endpoint.is_family_url(), false); LT_CHECK_EQ(test_endpoint.is_regex_compiled(), true); -LT_END_AUTO_TEST(http_endpoint_from_string_not_beginning_with_slash) +LT_END_AUTO_TEST(http_endpoint_from_string_ending_with_slash) LT_BEGIN_AUTO_TEST(http_endpoint_suite, http_endpoint_from_string_family) - http_endpoint test_endpoint(std::string("/path/to/resource"), true); + http_endpoint test_endpoint("/path/to/resource", true); LT_CHECK_EQ(test_endpoint.get_url_complete(), "/path/to/resource"); LT_CHECK_EQ(test_endpoint.get_url_normalized(), "^/path/to/resource$"); @@ -118,7 +122,7 @@ LT_BEGIN_AUTO_TEST(http_endpoint_suite, http_endpoint_from_string_family) LT_END_AUTO_TEST(http_endpoint_from_string_family) LT_BEGIN_AUTO_TEST(http_endpoint_suite, http_endpoint_from_string_no_regex) - http_endpoint test_endpoint(std::string("/path/to/resource"), false, false, false); + http_endpoint test_endpoint("/path/to/resource", false, false, false); LT_CHECK_EQ(test_endpoint.get_url_complete(), "/path/to/resource"); LT_CHECK_EQ(test_endpoint.get_url_normalized(), "/path/to/resource"); @@ -136,7 +140,7 @@ LT_BEGIN_AUTO_TEST(http_endpoint_suite, http_endpoint_from_string_no_regex) LT_END_AUTO_TEST(http_endpoint_from_string_no_regex) LT_BEGIN_AUTO_TEST(http_endpoint_suite, http_endpoint_registration) - http_endpoint test_endpoint(std::string("/path/to/resource"), false, true); + http_endpoint test_endpoint("/path/to/resource", false, true); LT_CHECK_EQ(test_endpoint.get_url_complete(), "/path/to/resource"); LT_CHECK_EQ(test_endpoint.get_url_normalized(), "^/path/to/resource$"); @@ -154,7 +158,7 @@ LT_BEGIN_AUTO_TEST(http_endpoint_suite, http_endpoint_registration) LT_END_AUTO_TEST(http_endpoint_registration) LT_BEGIN_AUTO_TEST(http_endpoint_suite, http_endpoint_registration_nested_regex) - http_endpoint test_endpoint(std::string("/path/to/resource/with/[0-9]+/to/fetch"), false, true); + http_endpoint test_endpoint("/path/to/resource/with/[0-9]+/to/fetch", false, true); LT_CHECK_EQ(test_endpoint.get_url_complete(), "/path/to/resource/with/[0-9]+/to/fetch"); LT_CHECK_EQ(test_endpoint.get_url_normalized(), "^/path/to/resource/with/[0-9]+/to/fetch$"); @@ -172,7 +176,7 @@ LT_BEGIN_AUTO_TEST(http_endpoint_suite, http_endpoint_registration_nested_regex) LT_END_AUTO_TEST(http_endpoint_registration_nested_regex) LT_BEGIN_AUTO_TEST(http_endpoint_suite, http_endpoint_registration_arg) - http_endpoint test_endpoint(std::string("/path/to/resource/with/{arg}/to/fetch"), false, true); + http_endpoint test_endpoint("/path/to/resource/with/{arg}/to/fetch", false, true); LT_CHECK_EQ(test_endpoint.get_url_complete(), "/path/to/resource/with/{arg}/to/fetch"); LT_CHECK_EQ(test_endpoint.get_url_normalized(), "^/path/to/resource/with/([^\\/]+)/to/fetch$"); @@ -194,7 +198,7 @@ LT_BEGIN_AUTO_TEST(http_endpoint_suite, http_endpoint_registration_arg) LT_END_AUTO_TEST(http_endpoint_registration_arg) LT_BEGIN_AUTO_TEST(http_endpoint_suite, http_endpoint_registration_arg_custom_regex) - http_endpoint test_endpoint(std::string("/path/to/resource/with/{arg|([0-9]+)}/to/fetch"), false, true); + http_endpoint test_endpoint("/path/to/resource/with/{arg|([0-9]+)}/to/fetch", false, true); LT_CHECK_EQ(test_endpoint.get_url_complete(), "/path/to/resource/with/{arg|([0-9]+)}/to/fetch"); LT_CHECK_EQ(test_endpoint.get_url_normalized(), "^/path/to/resource/with/([0-9]+)/to/fetch$"); @@ -216,9 +220,105 @@ LT_BEGIN_AUTO_TEST(http_endpoint_suite, http_endpoint_registration_arg_custom_re LT_END_AUTO_TEST(http_endpoint_registration_arg_custom_regex) LT_BEGIN_AUTO_TEST(http_endpoint_suite, http_endpoint_registration_invalid_arg) - LT_CHECK_THROW(http_endpoint(std::string("/path/to/resource/with/{}/to/fetch"), false, true)); + LT_CHECK_THROW(http_endpoint("/path/to/resource/with/{}/to/fetch", false, true)); LT_END_AUTO_TEST(http_endpoint_registration_invalid_arg) +LT_BEGIN_AUTO_TEST(http_endpoint_suite, http_endpoint_copy_constructor) + http_endpoint a("/path/to/resource/with/{arg|([0-9]+)}/to/fetch", false, true, true); + http_endpoint b(a); + + LT_CHECK_EQ(a.get_url_complete(), b.get_url_complete()); + LT_CHECK_EQ(a.get_url_normalized(), b.get_url_normalized()); + LT_CHECK_COLLECTIONS_EQ(a.get_url_pars().begin(), a.get_url_pars().end(), b.get_url_pars().begin()); + LT_CHECK_COLLECTIONS_EQ(a.get_url_pieces().begin(), a.get_url_pieces().end(), b.get_url_pieces().begin()); + LT_CHECK_COLLECTIONS_EQ(a.get_chunk_positions().begin(), a.get_chunk_positions().end(), b.get_chunk_positions().begin()); + LT_CHECK_EQ(a.is_family_url(), b.is_family_url()); + LT_CHECK_EQ(a.is_regex_compiled(), b.is_regex_compiled()); + + LT_CHECK_EQ(a.match(http_endpoint("/path/to/resource/with/10/to/fetch")), true); + LT_CHECK_EQ(b.match(http_endpoint("/path/to/resource/with/10/to/fetch")), true); +LT_END_AUTO_TEST(http_endpoint_copy_constructor) + +LT_BEGIN_AUTO_TEST(http_endpoint_suite, http_endpoint_assignment) + http_endpoint a("/path/to/resource/with/{arg|([0-9]+)}/to/fetch", false, true, true); + http_endpoint b = a; + + LT_CHECK_EQ(a.get_url_complete(), b.get_url_complete()); + LT_CHECK_EQ(a.get_url_normalized(), b.get_url_normalized()); + LT_CHECK_COLLECTIONS_EQ(a.get_url_pars().begin(), a.get_url_pars().end(), b.get_url_pars().begin()); + LT_CHECK_COLLECTIONS_EQ(a.get_url_pieces().begin(), a.get_url_pieces().end(), b.get_url_pieces().begin()); + LT_CHECK_COLLECTIONS_EQ(a.get_chunk_positions().begin(), a.get_chunk_positions().end(), b.get_chunk_positions().begin()); + LT_CHECK_EQ(a.is_family_url(), b.is_family_url()); + LT_CHECK_EQ(a.is_regex_compiled(), b.is_regex_compiled()); + + LT_CHECK_EQ(a.match(http_endpoint("/path/to/resource/with/10/to/fetch")), true); + LT_CHECK_EQ(b.match(http_endpoint("/path/to/resource/with/10/to/fetch")), true); +LT_END_AUTO_TEST(http_endpoint_assignment) + +LT_BEGIN_AUTO_TEST(http_endpoint_suite, http_endpoint_match_regex) + http_endpoint test_endpoint("/path/to/resource/", false, true, true); + LT_CHECK_EQ(test_endpoint.match(http_endpoint("/path/to/resource")), true); + LT_CHECK_EQ(test_endpoint.match(http_endpoint("/path/to/resource/")), true); + LT_CHECK_EQ(test_endpoint.match(http_endpoint("/path/to2/resource")), false); +LT_END_AUTO_TEST(http_endpoint_match_regex) + +LT_BEGIN_AUTO_TEST(http_endpoint_suite, http_endpoint_match_regex_nested) + http_endpoint test_endpoint("/path/to/resource/with/[0-9]+/to/fetch", false, true, true); + LT_CHECK_EQ(test_endpoint.match(http_endpoint("/path/to/resource/with/0/to/fetch")), true); + LT_CHECK_EQ(test_endpoint.match(http_endpoint("/path/to/resource/with/10/to/fetch")), true); + LT_CHECK_EQ(test_endpoint.match(http_endpoint("path/to/resource/with/1/to/fetch")), true); + LT_CHECK_EQ(test_endpoint.match(http_endpoint("/path/to/resource/with/1/to/fetch/")), true); +LT_END_AUTO_TEST(http_endpoint_match_regex_nested) + +LT_BEGIN_AUTO_TEST(http_endpoint_suite, http_endpoint_match_regex_nested_capture) + http_endpoint test_endpoint("/path/to/resource/with/([0-9]+)/to/fetch", false, true, true); + LT_CHECK_EQ(test_endpoint.match(http_endpoint("/path/to/resource/with/0/to/fetch")), true); + LT_CHECK_EQ(test_endpoint.match(http_endpoint("/path/to/resource/with/10/to/fetch")), true); + LT_CHECK_EQ(test_endpoint.match(http_endpoint("path/to/resource/with/1/to/fetch")), true); + LT_CHECK_EQ(test_endpoint.match(http_endpoint("/path/to/resource/with/1/to/fetch/")), true); +LT_END_AUTO_TEST(http_endpoint_match_regex_nested_capture) + +LT_BEGIN_AUTO_TEST(http_endpoint_suite, http_endpoint_match_regex_nested_arg) + http_endpoint test_endpoint("/path/to/resource/with/{arg}/to/fetch", false, true, true); + LT_CHECK_EQ(test_endpoint.match(http_endpoint("/path/to/resource/with/0/to/fetch")), true); + LT_CHECK_EQ(test_endpoint.match(http_endpoint("/path/to/resource/with/10/to/fetch")), true); + LT_CHECK_EQ(test_endpoint.match(http_endpoint("path/to/resource/with/1/to/fetch")), true); + LT_CHECK_EQ(test_endpoint.match(http_endpoint("/path/to/resource/with/1/to/fetch/")), true); +LT_END_AUTO_TEST(http_endpoint_match_regex_nested_arg) + +LT_BEGIN_AUTO_TEST(http_endpoint_suite, http_endpoint_match_regex_nested_custom_arg) + http_endpoint test_endpoint("/path/to/resource/with/{arg|([0-9]+)}/to/fetch", false, true, true); + LT_CHECK_EQ(test_endpoint.match(http_endpoint("/path/to/resource/with/0/to/fetch")), true); + LT_CHECK_EQ(test_endpoint.match(http_endpoint("/path/to/resource/with/10/to/fetch")), true); + LT_CHECK_EQ(test_endpoint.match(http_endpoint("path/to/resource/with/1/to/fetch")), true); + LT_CHECK_EQ(test_endpoint.match(http_endpoint("/path/to/resource/with/1/to/fetch/")), true); +LT_END_AUTO_TEST(http_endpoint_match_regex_nested_custom_arg) + +LT_BEGIN_AUTO_TEST(http_endpoint_suite, http_endpoint_match_regex_family) + http_endpoint test_endpoint("/path/to/resource", true, true, true); + LT_CHECK_EQ(test_endpoint.match(http_endpoint("/path/to/resource")), true); + LT_CHECK_EQ(test_endpoint.match(http_endpoint("/path/to/resource/")), true); + LT_CHECK_EQ(test_endpoint.match(http_endpoint("path/to/resource")), true); + LT_CHECK_EQ(test_endpoint.match(http_endpoint("path/to/resource/")), true); + LT_CHECK_EQ(test_endpoint.match(http_endpoint("/path/to/resource")), true); + LT_CHECK_EQ(test_endpoint.match(http_endpoint("/path/to/resource/followed/by/anything")), true); + + LT_CHECK_EQ(test_endpoint.match(http_endpoint("path/to2/resource")), false); +LT_END_AUTO_TEST(http_endpoint_match_regex_family) + +LT_BEGIN_AUTO_TEST(http_endpoint_suite, http_endpoint_match_regex_disabled) + http_endpoint test_endpoint("/path/to/resource", false, true, false); + LT_CHECK_THROW(test_endpoint.match(http_endpoint("/path/to/resource"))); +LT_END_AUTO_TEST(http_endpoint_match_regex_disabled) + +LT_BEGIN_AUTO_TEST(http_endpoint_suite, comparator) + LT_CHECK_EQ(http_endpoint("/a/b") < http_endpoint("/a/c"), true); + LT_CHECK_EQ(http_endpoint("/a/c") < http_endpoint("/a/b"), false); + + LT_CHECK_EQ(http_endpoint("/a/b") < http_endpoint("/a/b/c"), true); + LT_CHECK_EQ(http_endpoint("/a/b/c") < http_endpoint("/a/b"), false); +LT_END_AUTO_TEST(comparator) + LT_BEGIN_AUTO_TEST_ENV() AUTORUN_TESTS() LT_END_AUTO_TEST_ENV() From 852c43765f6a57790ac29102823912f5038f53fa Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Sun, 30 Dec 2018 04:00:40 +0000 Subject: [PATCH 305/623] Forcing test of assignment operator for http_endpoint --- test/unit/http_endpoint_test.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/test/unit/http_endpoint_test.cpp b/test/unit/http_endpoint_test.cpp index ed461792..8dab9e24 100644 --- a/test/unit/http_endpoint_test.cpp +++ b/test/unit/http_endpoint_test.cpp @@ -241,7 +241,11 @@ LT_END_AUTO_TEST(http_endpoint_copy_constructor) LT_BEGIN_AUTO_TEST(http_endpoint_suite, http_endpoint_assignment) http_endpoint a("/path/to/resource/with/{arg|([0-9]+)}/to/fetch", false, true, true); - http_endpoint b = a; + http_endpoint b("whatever/initial/value"); + + LT_CHECK_NEQ(a.get_url_complete(), b.get_url_complete()); + + b = a; LT_CHECK_EQ(a.get_url_complete(), b.get_url_complete()); LT_CHECK_EQ(a.get_url_normalized(), b.get_url_normalized()); From 65ce5d19bc52abe85ea821c8c5fc393d5f3e4bf6 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Mon, 31 Dec 2018 02:21:12 +0000 Subject: [PATCH 306/623] Added test for no response use case --- src/http_response.cpp | 3 +-- src/httpserver/http_response.hpp | 9 ++++++++- src/webserver.cpp | 12 ++++++++++-- test/integ/basic.cpp | 10 ++-------- 4 files changed, 21 insertions(+), 13 deletions(-) diff --git a/src/http_response.cpp b/src/http_response.cpp index 48fb238b..dc9a2125 100644 --- a/src/http_response.cpp +++ b/src/http_response.cpp @@ -69,8 +69,7 @@ http_response::http_response(const http_response_builder& builder): http_response::~http_response() { - if(ce != 0x0) - webserver::unlock_cache_entry(ce); + if(ce != 0x0) webserver::unlock_cache_entry(ce); } //RESPONSE diff --git a/src/httpserver/http_response.hpp b/src/httpserver/http_response.hpp index 63cf7ee8..ba502860 100644 --- a/src/httpserver/http_response.hpp +++ b/src/httpserver/http_response.hpp @@ -104,7 +104,14 @@ class http_response { } - http_response(): response_code(-1) + http_response(): + response_code(-1), + fp(-1), + underlying_connection(0x0), + ce(0x0), + completed(false), + ws(0x0), + connection_id(0x0) { } diff --git a/src/webserver.cpp b/src/webserver.cpp index cf120d48..183f45d0 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -646,9 +646,14 @@ const http_response webserver::method_not_allowed_page(details::modded_request* const http_response webserver::internal_error_page(details::modded_request* mr, bool force_our) const { if(internal_error_resource != 0x0 && !force_our) + { return internal_error_resource(*mr->dhr); + } else - return http_response_builder(GENERIC_ERROR, http_utils::http_internal_server_error).string_response(); + { + http_response hr = http_response_builder(GENERIC_ERROR, http_utils::http_internal_server_error, "text/plain").string_response(); + return hr; + } } int webserver::bodyless_requests_answer( @@ -872,7 +877,10 @@ int webserver::finalize_answer( if(hrm->is_allowed(method)) { mr->dhrs = NEW_OR_MOVE(http_response, ((hrm)->*(mr->callback))(*mr->dhr)); //copy in memory (move in case) - if (mr->dhrs->get_response_code() == -1) mr->dhrs = NEW_OR_MOVE(http_response, internal_error_page(mr)); + if (mr->dhrs->get_response_code() == -1) + { + mr->dhrs = NEW_OR_MOVE(http_response, internal_error_page(mr)); + } } else { diff --git a/test/integ/basic.cpp b/test/integ/basic.cpp index 80969202..5a58582a 100644 --- a/test/integ/basic.cpp +++ b/test/integ/basic.cpp @@ -157,7 +157,6 @@ LT_BEGIN_SUITE(basic_suite) LT_END_SUITE(basic_suite) LT_BEGIN_AUTO_TEST(basic_suite, two_endpoints) - ok_resource* ok = new ok_resource(); ws->register_resource("OK", ok); nok_resource* nok = new nok_resource(); @@ -413,26 +412,21 @@ LT_BEGIN_AUTO_TEST(basic_suite, empty_arg) curl_easy_cleanup(curl); LT_END_AUTO_TEST(empty_arg) -/* LT_BEGIN_AUTO_TEST(basic_suite, no_response) no_response_resource* resource = new no_response_resource(); ws->register_resource("base", resource); curl_global_init(CURL_GLOBAL_ALL); - std::string s; - CURL* curl; - CURLcode res; - curl = curl_easy_init(); + CURL* curl = curl_easy_init(); curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); - res = curl_easy_perform(curl); + CURLcode res = curl_easy_perform(curl); LT_ASSERT_EQ(res, 0); long http_code = 0; curl_easy_getinfo (curl, CURLINFO_RESPONSE_CODE, &http_code); LT_ASSERT_EQ(http_code, 500); curl_easy_cleanup(curl); LT_END_AUTO_TEST(no_response) -*/ LT_BEGIN_AUTO_TEST_ENV() AUTORUN_TESTS() From ded759998fe6b48b790fd2fa88c3a4186644b745 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Mon, 31 Dec 2018 03:04:06 +0000 Subject: [PATCH 307/623] Added tests for resource matching (regex, args, custom args) --- test/integ/basic.cpp | 82 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) diff --git a/test/integ/basic.cpp b/test/integ/basic.cpp index 5a58582a..16485f3b 100644 --- a/test/integ/basic.cpp +++ b/test/integ/basic.cpp @@ -58,6 +58,15 @@ class simple_resource : public http_resource } }; +class args_resource : public http_resource +{ + public: + const http_response render_GET(const http_request& req) + { + return http_response_builder(req.get_arg("arg"), 200, "text/plain").string_response(); + } +}; + class long_content_resource : public http_resource { public: @@ -428,6 +437,79 @@ LT_BEGIN_AUTO_TEST(basic_suite, no_response) curl_easy_cleanup(curl); LT_END_AUTO_TEST(no_response) +LT_BEGIN_AUTO_TEST(basic_suite, regex_matching) + simple_resource* resource = new simple_resource(); + ws->register_resource("regex/matching/number/[0-9]+", resource); + curl_global_init(CURL_GLOBAL_ALL); + + std::string s; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/regex/matching/number/10"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "OK"); + curl_easy_cleanup(curl); +LT_END_AUTO_TEST(regex_matching) + +LT_BEGIN_AUTO_TEST(basic_suite, regex_matching_arg) + args_resource* resource = new args_resource(); + ws->register_resource("this/captures/{arg}/passed/in/input", resource); + curl_global_init(CURL_GLOBAL_ALL); + + std::string s; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/this/captures/whatever/passed/in/input"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "whatever"); + curl_easy_cleanup(curl); +LT_END_AUTO_TEST(regex_matching_arg) + +LT_BEGIN_AUTO_TEST(basic_suite, regex_matching_arg_custom) + args_resource* resource = new args_resource(); + ws->register_resource("this/captures/numeric/{arg|([0-9]+)}/passed/in/input", resource); + curl_global_init(CURL_GLOBAL_ALL); + + { + std::string s; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/this/captures/numeric/11/passed/in/input"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "11"); + curl_easy_cleanup(curl); + } + + { + std::string s; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/this/captures/numeric/text/passed/in/input"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "Not Found"); + long http_code = 0; + curl_easy_getinfo (curl, CURLINFO_RESPONSE_CODE, &http_code); + LT_ASSERT_EQ(http_code, 404); + curl_easy_cleanup(curl); + } +LT_END_AUTO_TEST(regex_matching_arg_custom) + LT_BEGIN_AUTO_TEST_ENV() AUTORUN_TESTS() LT_END_AUTO_TEST_ENV() From 2076fbada3f2b1ee271708b02cf537bf7e221c9a Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Mon, 31 Dec 2018 03:11:40 +0000 Subject: [PATCH 308/623] Removed internal exception in favor of standard one --- src/httpserver/webserver.hpp | 9 --------- src/webserver.cpp | 7 ++----- 2 files changed, 2 insertions(+), 14 deletions(-) diff --git a/src/httpserver/webserver.hpp b/src/httpserver/webserver.hpp index ac348019..8ce410c9 100644 --- a/src/httpserver/webserver.hpp +++ b/src/httpserver/webserver.hpp @@ -65,15 +65,6 @@ namespace details { class comet_manager; } -class webserver_exception : public std::runtime_error -{ -public: - webserver_exception() - : std::runtime_error("httpserver runtime error") - { - } -}; - /** * Class representing the webserver. Main class of the apis. **/ diff --git a/src/webserver.cpp b/src/webserver.cpp index 183f45d0..65d39f7a 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -296,8 +296,7 @@ bool webserver::start(bool blocking) iov.push_back(gen(MHD_OPTION_LISTEN_SOCKET, bind_socket)); if(start_method == http_utils::THREAD_PER_CONNECTION && max_threads != 0) { - cout << "Cannot specify maximum number of threads when using a thread per connection" << endl; - throw ::httpserver::webserver_exception(); + throw std::invalid_argument("Cannot specify maximum number of threads when using a thread per connection"); } if(max_threads != 0) @@ -381,9 +380,7 @@ bool webserver::start(bool blocking) if(NULL == daemon) { - cout << gettext("Unable to connect daemon to port: ") << - this->port << endl; - throw ::httpserver::webserver_exception(); + throw std::invalid_argument("Unable to connect daemon to port: " + this->port); } details::daemon_item* di = new details::daemon_item(this, daemon); daemons.push_back(di); From d8ed644daf1957a80cd9db64d5a55091cf827d0c Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Mon, 31 Dec 2018 20:03:20 +0000 Subject: [PATCH 309/623] Testing querystring parser --- test/integ/basic.cpp | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/test/integ/basic.cpp b/test/integ/basic.cpp index 16485f3b..0619a05e 100644 --- a/test/integ/basic.cpp +++ b/test/integ/basic.cpp @@ -63,7 +63,7 @@ class args_resource : public http_resource public: const http_response render_GET(const http_request& req) { - return http_response_builder(req.get_arg("arg"), 200, "text/plain").string_response(); + return http_response_builder(req.get_arg("arg") + req.get_arg("arg2"), 200, "text/plain").string_response(); } }; @@ -510,6 +510,24 @@ LT_BEGIN_AUTO_TEST(basic_suite, regex_matching_arg_custom) } LT_END_AUTO_TEST(regex_matching_arg_custom) +LT_BEGIN_AUTO_TEST(basic_suite, querystring_processing) + args_resource* resource = new args_resource(); + ws->register_resource("this/captures/args/passed/in/the/querystring", resource); + curl_global_init(CURL_GLOBAL_ALL); + + std::string s; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/this/captures/args/passed/in/the/querystring?arg=first&arg2=second"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "firstsecond"); + curl_easy_cleanup(curl); +LT_END_AUTO_TEST(querystring_processing) + LT_BEGIN_AUTO_TEST_ENV() AUTORUN_TESTS() LT_END_AUTO_TEST_ENV() From c1785a2b7137cbe4eb391d287e987cdae55cf96d Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Mon, 31 Dec 2018 20:55:14 +0000 Subject: [PATCH 310/623] Adding tests for headers and cookie management --- test/integ/basic.cpp | 117 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 113 insertions(+), 4 deletions(-) diff --git a/test/integ/basic.cpp b/test/integ/basic.cpp index 0619a05e..e65151d8 100644 --- a/test/integ/basic.cpp +++ b/test/integ/basic.cpp @@ -22,6 +22,7 @@ #include #include #include +#include "string_utilities.hpp" #include "httpserver.hpp" using namespace httpserver; @@ -76,7 +77,7 @@ class long_content_resource : public http_resource } }; -class header_test_resource : public http_resource +class header_set_test_resource : public http_resource { public: const http_response render_GET(const http_request& req) @@ -87,6 +88,35 @@ class header_test_resource : public http_resource } }; +class cookie_set_test_resource : public http_resource +{ + public: + const http_response render_GET(const http_request& req) + { + http_response_builder hrb("OK", 200, "text/plain"); + hrb.with_cookie("MyCookie", "CookieValue"); + return hrb.string_response(); + } +}; + +class cookie_reading_resource : public http_resource +{ + public: + const http_response render_GET(const http_request& req) + { + return http_response_builder(req.get_cookie("name"), 200, "text/plain").string_response(); + } +}; + +class header_reading_resource : public http_resource +{ + public: + const http_response render_GET(const http_request& req) + { + return http_response_builder(req.get_header("MyHeader"), 200, "text/plain").string_response(); + } +}; + class complete_test_resource : public http_resource { public: @@ -235,8 +265,8 @@ LT_BEGIN_AUTO_TEST(basic_suite, read_long_body) curl_easy_cleanup(curl); LT_END_AUTO_TEST(read_long_body) -LT_BEGIN_AUTO_TEST(basic_suite, read_header) - header_test_resource* resource = new header_test_resource(); +LT_BEGIN_AUTO_TEST(basic_suite, resource_setting_header) + header_set_test_resource* resource = new header_set_test_resource(); ws->register_resource("base", resource); curl_global_init(CURL_GLOBAL_ALL); std::string s; @@ -254,7 +284,86 @@ LT_BEGIN_AUTO_TEST(basic_suite, read_header) LT_CHECK_EQ(s, "OK"); LT_CHECK_EQ(ss["KEY"], "VALUE"); curl_easy_cleanup(curl); -LT_END_AUTO_TEST(read_header) +LT_END_AUTO_TEST(resource_setting_header) + +LT_BEGIN_AUTO_TEST(basic_suite, resource_setting_cookie) + cookie_set_test_resource* resource = new cookie_set_test_resource(); + ws->register_resource("base", resource); + curl_global_init(CURL_GLOBAL_ALL); + std::string s; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_COOKIEFILE, ""); + curl_easy_setopt(curl, CURLOPT_COOKIEJAR, ""); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + res = curl_easy_perform(curl); + + struct curl_slist *cookies; + curl_easy_getinfo(curl, CURLINFO_COOKIELIST, &cookies); + + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "OK"); + std::string read_cookie = ""; + + if(!res && cookies) + { + read_cookie = cookies->data; + curl_slist_free_all(cookies); + } + else + { + LT_FAIL("No cookie being set"); + } + std::vector cookie_parts = string_utilities::string_split(read_cookie, '\t', false); + LT_CHECK_EQ(cookie_parts[5], "MyCookie"); + LT_CHECK_EQ(cookie_parts[6], "CookieValue"); + + curl_easy_cleanup(curl); +LT_END_AUTO_TEST(resource_setting_cookie) + +LT_BEGIN_AUTO_TEST(basic_suite, request_with_header) + header_reading_resource* resource = new header_reading_resource(); + ws->register_resource("base", resource); + curl_global_init(CURL_GLOBAL_ALL); + std::string s; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + + struct curl_slist *list = NULL; + list = curl_slist_append(list, "MyHeader: MyValue"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list); + + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "MyValue"); + curl_slist_free_all(list); + curl_easy_cleanup(curl); +LT_END_AUTO_TEST(request_with_header) + +LT_BEGIN_AUTO_TEST(basic_suite, request_with_cookie) + cookie_reading_resource* resource = new cookie_reading_resource(); + ws->register_resource("base", resource); + curl_global_init(CURL_GLOBAL_ALL); + std::string s; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + curl_easy_setopt(curl, CURLOPT_COOKIE, "name=myname; present=yes;"); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "myname"); + curl_easy_cleanup(curl); +LT_END_AUTO_TEST(request_with_cookie) LT_BEGIN_AUTO_TEST(basic_suite, complete) complete_test_resource* resource = new complete_test_resource(); From 507381b9d2b824ef15d1977fd7b5a50b6815486f Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Mon, 31 Dec 2018 21:33:56 +0000 Subject: [PATCH 311/623] Fix unregister_resource method. Added tests to verify behavior --- src/webserver.cpp | 1 + test/integ/basic.cpp | 56 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+) diff --git a/src/webserver.cpp b/src/webserver.cpp index 65d39f7a..71e2e255 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -436,6 +436,7 @@ void webserver::unregister_resource(const string& resource) details::http_endpoint he(resource); this->registered_resources.erase(he); this->registered_resources.erase(he.get_url_complete()); + this->registered_resources_str.erase(he.get_url_complete()); } void webserver::ban_ip(const string& ip) diff --git a/test/integ/basic.cpp b/test/integ/basic.cpp index e65151d8..bcf2f9c6 100644 --- a/test/integ/basic.cpp +++ b/test/integ/basic.cpp @@ -637,6 +637,62 @@ LT_BEGIN_AUTO_TEST(basic_suite, querystring_processing) curl_easy_cleanup(curl); LT_END_AUTO_TEST(querystring_processing) +LT_BEGIN_AUTO_TEST(basic_suite, register_unregister) + simple_resource* resource = new simple_resource(); + ws->register_resource("base", resource); + curl_global_init(CURL_GLOBAL_ALL); + + { + std::string s; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "OK"); + curl_easy_cleanup(curl); + } + + ws->unregister_resource("base"); + { + std::string s; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + + long http_code = 0; + curl_easy_getinfo (curl, CURLINFO_RESPONSE_CODE, &http_code); + LT_ASSERT_EQ(http_code, 404); + + LT_CHECK_EQ(s, "Not Found"); + + curl_easy_cleanup(curl); + } + + ws->register_resource("base", resource); + { + std::string s; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "OK"); + curl_easy_cleanup(curl); + } +LT_END_AUTO_TEST(register_unregister) + LT_BEGIN_AUTO_TEST_ENV() AUTORUN_TESTS() LT_END_AUTO_TEST_ENV() From 8e304b207c11b01ba429ef0904fba47a60866375 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Mon, 31 Dec 2018 21:37:55 +0000 Subject: [PATCH 312/623] Test checking server runs --- test/integ/basic.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/integ/basic.cpp b/test/integ/basic.cpp index bcf2f9c6..8ca0302b 100644 --- a/test/integ/basic.cpp +++ b/test/integ/basic.cpp @@ -195,6 +195,10 @@ LT_BEGIN_SUITE(basic_suite) } LT_END_SUITE(basic_suite) +LT_BEGIN_AUTO_TEST(basic_suite, server_runs) + LT_CHECK_EQ(ws->is_running(), true); +LT_END_AUTO_TEST(server_runs) + LT_BEGIN_AUTO_TEST(basic_suite, two_endpoints) ok_resource* ok = new ok_resource(); ws->register_resource("OK", ok); From 019083bac1c5229177017323d1fc435227be4412 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Wed, 2 Jan 2019 00:48:57 +0000 Subject: [PATCH 313/623] Removed caching mechanism. The design is badly though and currently not used by customers. The current design doesn't fully handle management of the cache and is hence half managed with responsibility of the users. A new design will be developed with a fully managed design (in the meantime it would be simpler for users to just manage their caching independently). --- src/Makefile.am | 2 +- src/http_response.cpp | 23 --- src/httpserver/details/cache_entry.hpp | 141 ----------------- src/httpserver/http_response.hpp | 6 - src/httpserver/http_response_builder.hpp | 25 --- src/httpserver/webserver.hpp | 23 --- src/webserver.cpp | 193 ----------------------- 7 files changed, 1 insertion(+), 412 deletions(-) delete mode 100644 src/httpserver/details/cache_entry.hpp diff --git a/src/Makefile.am b/src/Makefile.am index 11aff7ed..962d34ad 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -20,7 +20,7 @@ AM_CPPFLAGS = -I../ -I$(srcdir)/httpserver/ METASOURCES = AUTO lib_LTLIBRARIES = libhttpserver.la libhttpserver_la_SOURCES = string_utilities.cpp webserver.cpp http_utils.cpp http_request.cpp http_response.cpp http_resource.cpp details/comet_manager.cpp details/http_endpoint.cpp -noinst_HEADERS = httpserver/string_utilities.hpp httpserver/details/modded_request.hpp httpserver/details/http_response_ptr.hpp httpserver/details/cache_entry.hpp httpserver/details/comet_manager.hpp gettext.h +noinst_HEADERS = httpserver/string_utilities.hpp httpserver/details/modded_request.hpp httpserver/details/http_response_ptr.hpp httpserver/details/comet_manager.hpp gettext.h nobase_include_HEADERS = httpserver.hpp httpserver/create_webserver.hpp httpserver/webserver.hpp httpserver/http_utils.hpp httpserver/details/http_endpoint.hpp httpserver/http_request.hpp httpserver/http_response.hpp httpserver/http_resource.hpp httpserver/binders.hpp httpserver/http_response_builder.hpp AM_CXXFLAGS += -fPIC -Wall diff --git a/src/http_response.cpp b/src/http_response.cpp index dc9a2125..37893476 100644 --- a/src/http_response.cpp +++ b/src/http_response.cpp @@ -53,7 +53,6 @@ http_response::http_response(const http_response_builder& builder): keepalive_msg(builder._keepalive_msg), send_topic(builder._send_topic), underlying_connection(0x0), - ce(builder._ce), cycle_callback(builder._cycle_callback), get_raw_response(this, builder._get_raw_response), decorate_response(this, builder._decorate_response), @@ -69,7 +68,6 @@ http_response::http_response(const http_response_builder& builder): http_response::~http_response() { - if(ce != 0x0) webserver::unlock_cache_entry(ce); } //RESPONSE @@ -116,10 +114,6 @@ int http_response::enqueue_response_str( return MHD_queue_response(connection, response_code, response); } -void http_response::decorate_response_cache(MHD_Response* response) -{ -} - int http_response::enqueue_response_basic( MHD_Connection* connection, MHD_Response* response @@ -167,23 +161,6 @@ void http_response::get_raw_response_file( } } -void http_response::get_raw_response_cache( - MHD_Response** response, - webserver* ws -) -{ - bool valid; - http_response* r; - if(ce == 0x0) - r = ws->get_from_cache(content, &valid, &ce, true, false); - else - webserver::get_response(ce, &r); - r->get_raw_response(response, ws); - r->decorate_response(*response); //It is done here to avoid to search two times for the same element - - //TODO: Check if element is not in cache and throw exception -} - namespace details { diff --git a/src/httpserver/details/cache_entry.hpp b/src/httpserver/details/cache_entry.hpp deleted file mode 100644 index e7fdc03d..00000000 --- a/src/httpserver/details/cache_entry.hpp +++ /dev/null @@ -1,141 +0,0 @@ -/* - This file is part of libhttpserver - Copyright (C) 2011-2019 Sebastiano Merlino - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 - USA -*/ - -#if !defined (_HTTPSERVER_HPP_INSIDE_) && !defined (HTTPSERVER_COMPILATION) -#error "Only or can be included directly." -#endif - -#ifndef _CACHE_ENTRY_HPP_ -#define _CACHE_ENTRY_HPP_ - -#include -#include -#include "httpserver/details/http_response_ptr.hpp" - -namespace httpserver -{ -namespace details -{ - -struct pthread_t_comparator -{ - bool operator()(const pthread_t& t1, const pthread_t& t2) const - { - return pthread_equal(t1, t2); - } -}; - -struct cache_entry -{ - long ts; - int validity; - details::http_response_ptr response; - pthread_rwlock_t elem_guard; - pthread_mutex_t lock_guard; - std::set lockers; - - cache_entry(): - ts(-1), - validity(-1) - { - pthread_rwlock_init(&elem_guard, NULL); - pthread_mutex_init(&lock_guard, NULL); - } - - ~cache_entry() - { - pthread_rwlock_destroy(&elem_guard); - pthread_mutex_destroy(&lock_guard); - } - - cache_entry(const cache_entry& b): - ts(b.ts), - validity(b.validity), - response(b.response), - elem_guard(b.elem_guard), - lock_guard(b.lock_guard) - { - } - - void operator= (const cache_entry& b) - { - ts = b.ts; - validity = b.validity; - response = b.response; - pthread_rwlock_destroy(&elem_guard); - pthread_mutex_destroy(&lock_guard); - elem_guard = b.elem_guard; - } - - cache_entry( - details::http_response_ptr response, - long ts = -1, - int validity = -1 - ): - ts(ts), - validity(validity), - response(response) - { - pthread_rwlock_init(&elem_guard, NULL); - pthread_mutex_init(&lock_guard, NULL); - } - - void lock(bool write = false) - { - pthread_mutex_lock(&lock_guard); - pthread_t tid = pthread_self(); - if(!lockers.count(tid)) - { - if(write) - { - lockers.insert(tid); - pthread_mutex_unlock(&lock_guard); - pthread_rwlock_wrlock(&elem_guard); - } - else - { - lockers.insert(tid); - pthread_mutex_unlock(&lock_guard); - pthread_rwlock_rdlock(&elem_guard); - } - } - else - pthread_mutex_unlock(&lock_guard); - } - - void unlock() - { - pthread_mutex_lock(&lock_guard); - { - pthread_t tid = pthread_self(); - if(lockers.count(tid)) - { - lockers.erase(tid); - pthread_rwlock_unlock(&elem_guard); - } - } - pthread_mutex_unlock(&lock_guard); - } -}; - -} //details -} //httpserver - -#endif //_CACHE_ENTRY_HPP_ diff --git a/src/httpserver/http_response.hpp b/src/httpserver/http_response.hpp index ba502860..8f1f66cf 100644 --- a/src/httpserver/http_response.hpp +++ b/src/httpserver/http_response.hpp @@ -48,7 +48,6 @@ namespace details { struct http_response_ptr; ssize_t cb(void*, uint64_t, char*, size_t); - struct cache_entry; }; class bad_caching_attempt: public std::exception @@ -90,7 +89,6 @@ class http_response keepalive_msg(b.keepalive_msg), send_topic(b.send_topic), underlying_connection(b.underlying_connection), - ce(b.ce), cycle_callback(b.cycle_callback), get_raw_response(this, b._get_raw_response), decorate_response(this, b._decorate_response), @@ -108,7 +106,6 @@ class http_response response_code(-1), fp(-1), underlying_connection(0x0), - ce(0x0), completed(false), ws(0x0), connection_id(0x0) @@ -234,7 +231,6 @@ class http_response std::string keepalive_msg; std::string send_topic; struct MHD_Connection* underlying_connection; - details::cache_entry* ce; cycle_callback_ptr cycle_callback; const get_raw_response_t get_raw_response; @@ -254,10 +250,8 @@ class http_response webserver* ws = 0x0); void get_raw_response_lp_send(MHD_Response** res, webserver* ws = 0x0); - void get_raw_response_cache(MHD_Response** res, webserver* ws = 0x0); void get_raw_response_deferred(MHD_Response** res, webserver* ws = 0x0); void decorate_response_str(MHD_Response* res); - void decorate_response_cache(MHD_Response* res); void decorate_response_deferred(MHD_Response* res); int enqueue_response_str(MHD_Connection* connection, MHD_Response* res); diff --git a/src/httpserver/http_response_builder.hpp b/src/httpserver/http_response_builder.hpp index 4b7b8cb9..4f047a16 100644 --- a/src/httpserver/http_response_builder.hpp +++ b/src/httpserver/http_response_builder.hpp @@ -40,11 +40,6 @@ namespace http class header_comparator; }; -namespace details -{ - struct cache_entry; -}; - struct byte_string { public: @@ -91,7 +86,6 @@ class http_response_builder _keepalive_secs(-1), _keepalive_msg(""), _send_topic(""), - _ce(0x0), _get_raw_response(&http_response::get_raw_response_str), _decorate_response(&http_response::decorate_response_str), _enqueue_response(&http_response::enqueue_response_str) @@ -118,7 +112,6 @@ class http_response_builder _keepalive_secs(-1), _keepalive_msg(""), _send_topic(""), - _ce(0x0), _get_raw_response(&http_response::get_raw_response_str), _decorate_response(&http_response::decorate_response_str), _enqueue_response(&http_response::enqueue_response_str) @@ -140,7 +133,6 @@ class http_response_builder _keepalive_secs(b._keepalive_secs), _keepalive_msg(b._keepalive_msg), _send_topic(b._send_topic), - _ce(b._ce), _get_raw_response(b._get_raw_response), _decorate_response(b._decorate_response), _enqueue_response(b._enqueue_response) @@ -162,7 +154,6 @@ class http_response_builder _keepalive_secs = b._keepalive_secs; _keepalive_msg = b._keepalive_msg; _send_topic = b._send_topic; - _ce = b._ce; _get_raw_response = b._get_raw_response; _decorate_response = b._decorate_response; _enqueue_response = b._enqueue_response; @@ -224,13 +215,6 @@ class http_response_builder return *this; } - http_response_builder& cache_response() - { - _get_raw_response = &http_response::get_raw_response_cache; - _decorate_response = &http_response::decorate_response_cache; - return *this; - } - http_response_builder& deferred_response(cycle_callback_ptr cycle_callback) { _cycle_callback = cycle_callback; @@ -275,20 +259,11 @@ class http_response_builder std::string _keepalive_msg; std::string _send_topic; cycle_callback_ptr _cycle_callback; - details::cache_entry* _ce; void (http_response::*_get_raw_response)(MHD_Response**, webserver*); void (http_response::*_decorate_response)(MHD_Response*); int (http_response::*_enqueue_response)(MHD_Connection*, MHD_Response*); - http_response_builder& cache_response(details::cache_entry* ce) - { - _ce = ce; - _get_raw_response = &http_response::get_raw_response_cache; - _decorate_response = &http_response::decorate_response_cache; - return *this; - } - friend class http_response; }; diff --git a/src/httpserver/webserver.hpp b/src/httpserver/webserver.hpp index 8ce410c9..4b9e611a 100644 --- a/src/httpserver/webserver.hpp +++ b/src/httpserver/webserver.hpp @@ -61,7 +61,6 @@ struct httpserver_ska; namespace details { struct daemon_item; struct modded_request; - struct cache_entry; class comet_manager; } @@ -121,22 +120,6 @@ class webserver std::string& message ); - http_response* get_from_cache(const std::string& key, bool* valid, - bool lock = false, bool write = false - ); - http_response* get_from_cache(const std::string& key, bool* valid, - details::cache_entry** ce, bool lock = false, bool write = false - ); - void lock_cache_element(details::cache_entry* ce, bool write = false); - void unlock_cache_element(details::cache_entry* ce); - details::cache_entry* put_in_cache(const std::string& key, http_response* value, - bool* new_elem, bool lock = false, - bool write = false, int validity = -1 - ); - void remove_from_cache(const std::string& key); - bool is_valid(const std::string& key); - void clean_cache(); - log_access_ptr get_access_logger() const { return this->log_access; @@ -213,9 +196,7 @@ class webserver std::map registered_resources; std::map registered_resources_str; - std::map response_cache; int next_to_choose; - pthread_rwlock_t cache_guard; std::set bans; std::set allowances; @@ -276,10 +257,6 @@ class webserver void **con_cls, int upgrade_socket ); - static void unlock_cache_entry(details::cache_entry*); - static void lock_cache_entry(details::cache_entry*); - static void get_response(details::cache_entry*, http_response** res); - int bodyless_requests_answer(MHD_Connection* connection, const char* method, const char* version, struct details::modded_request* mr diff --git a/src/webserver.cpp b/src/webserver.cpp index 71e2e255..92959019 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -55,7 +55,6 @@ #include "details/comet_manager.hpp" #include "webserver.hpp" #include "details/modded_request.hpp" -#include "details/cache_entry.hpp" #define _REENTRANT 1 @@ -189,7 +188,6 @@ webserver::webserver(const create_webserver& params): pthread_mutex_init(&mutexwait, NULL); pthread_rwlock_init(&runguard, NULL); pthread_cond_init(&mutexcond, NULL); - pthread_rwlock_init(&cache_guard, NULL); } webserver::~webserver() @@ -197,7 +195,6 @@ webserver::~webserver() this->stop(); pthread_mutex_destroy(&mutexwait); pthread_rwlock_destroy(&runguard); - pthread_rwlock_destroy(&cache_guard); pthread_cond_destroy(&mutexcond); delete internal_comet_manager; } @@ -1061,194 +1058,4 @@ size_t webserver::read_message(MHD_Connection* connection_id, return internal_comet_manager->read_message(connection_id, message); } -http_response* webserver::get_from_cache( - const std::string& key, - bool* valid, - bool lock, - bool write -) -{ - details::cache_entry* ce = 0x0; - return get_from_cache(key, valid, &ce, lock, write); -} - -http_response* webserver::get_from_cache( - const std::string& key, - bool* valid, - details::cache_entry** ce, - bool lock, - bool write -) -{ - pthread_rwlock_rdlock(&cache_guard); - *valid = true; - map::iterator it(response_cache.find(key)); - if(it != response_cache.end()) - { - if(lock) - (*it).second->lock(write); - if((*it).second->validity != -1) - { - timeval now; - gettimeofday(&now, NULL); - if( now.tv_sec - (*it).second->ts > (*it).second->validity) - *valid = false; - } - *ce = (*it).second; - pthread_rwlock_unlock(&cache_guard); - return (*it).second->response.ptr(); - } - else - { - pthread_rwlock_unlock(&cache_guard); - *valid = false; - return 0x0; - } -} - -bool webserver::is_valid(const std::string& key) -{ - pthread_rwlock_rdlock(&cache_guard); - map::iterator it(response_cache.find(key)); - if(it != response_cache.end()) - { - if((*it).second->validity != -1) - { - timeval now; - gettimeofday(&now, NULL); - if( now.tv_sec - (*it).second->ts > (*it).second->validity) - { - pthread_rwlock_unlock(&cache_guard); - return false; - } - else - { - pthread_rwlock_unlock(&cache_guard); - return true; - } - } - else - { - pthread_rwlock_unlock(&cache_guard); - return true; - } - } - pthread_rwlock_unlock(&cache_guard); - return false; -} - -void webserver::lock_cache_element(details::cache_entry* ce, bool write) -{ - if(ce) - ce->lock(write); -} - -void webserver::unlock_cache_element(details::cache_entry* ce) -{ - if(ce) - ce->unlock(); -} - -details::cache_entry* webserver::put_in_cache( - const std::string& key, - http_response* value, - bool* new_elem, - bool lock, - bool write, - int validity -) -{ - pthread_rwlock_wrlock(&cache_guard); - map::iterator it(response_cache.find(key)); - details::cache_entry* to_ret; - bool already_in = false; - if(it != response_cache.end()) - { - (*it).second->lock(true); - already_in = true; - } - if(validity == -1) - { - if(already_in) - { - (*it).second->response = value; - to_ret = (*it).second; - *new_elem = false; - } - else - { - pair::iterator, bool> res = - response_cache.insert(pair( - key, new details::cache_entry(value)) - ); - - to_ret = (*res.first).second; - *new_elem = res.second; - } - } - else - { - timeval now; - gettimeofday(&now, NULL); - if(already_in) - { - (*it).second->response = value; - (*it).second->ts = now.tv_sec; - (*it).second->validity = validity; - to_ret = (*it).second; - *new_elem = false; - } - else - { - pair::iterator, bool> res = - response_cache.insert(pair( - key, new details::cache_entry(value, now.tv_sec, validity)) - ); - to_ret = (*res.first).second; - *new_elem = res.second; - } - } - if(already_in) - (*it).second->unlock(); - if(lock) - to_ret->lock(write); - pthread_rwlock_unlock(&cache_guard); - return to_ret; -} - -void webserver::remove_from_cache(const std::string& key) -{ - pthread_rwlock_wrlock(&cache_guard); - map::iterator it(response_cache.find(key)); - if(it != response_cache.end()) - { - details::cache_entry* ce = (*it).second; - response_cache.erase(it); - delete ce; - } - pthread_rwlock_unlock(&cache_guard); -} - -void webserver::clean_cache() -{ - pthread_rwlock_wrlock(&cache_guard); - response_cache.clear(); //manage this because obviously causes leaks - pthread_rwlock_unlock(&cache_guard); -} - -void webserver::unlock_cache_entry(details::cache_entry* ce) -{ - ce->unlock(); -} - -void webserver::lock_cache_entry(details::cache_entry* ce) -{ - ce->lock(); -} - -void webserver::get_response(details::cache_entry* ce, http_response** res) -{ - *res = ce->response.ptr(); -} - }; From faa23dd615bb030625381a88f07febf2095ee732 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Wed, 2 Jan 2019 02:14:08 +0000 Subject: [PATCH 314/623] Fix and test resources managing files --- src/http_utils.cpp | 4 ++-- test/integ/basic.cpp | 27 +++++++++++++++++++++++++++ test/unit/http_utils_test.cpp | 2 +- 3 files changed, 30 insertions(+), 3 deletions(-) diff --git a/src/http_utils.cpp b/src/http_utils.cpp index 14bdb73d..bfa7510c 100644 --- a/src/http_utils.cpp +++ b/src/http_utils.cpp @@ -548,11 +548,11 @@ char* load_file (const char *filename) int size = fp.tellg(); fp.seekg(0, fp.beg); - char* content = new char[size]; + char* content = new char[size + 1]; fp.read(content, size); fp.close(); - content[size - 1] = 0; + content[size] = '\0'; return content; } else diff --git a/test/integ/basic.cpp b/test/integ/basic.cpp index 8ca0302b..94165e72 100644 --- a/test/integ/basic.cpp +++ b/test/integ/basic.cpp @@ -178,6 +178,15 @@ class no_response_resource : public http_resource } }; +class file_response_resource : public http_resource +{ + public: + const http_response render_GET(const http_request& req) + { + return http_response_builder("test_content", 200, "text/plain").file_response(); + } +}; + LT_BEGIN_SUITE(basic_suite) webserver* ws; @@ -697,6 +706,24 @@ LT_BEGIN_AUTO_TEST(basic_suite, register_unregister) } LT_END_AUTO_TEST(register_unregister) +LT_BEGIN_AUTO_TEST(basic_suite, file_serving_resource) + file_response_resource* resource = new file_response_resource(); + ws->register_resource("base", resource); + curl_global_init(CURL_GLOBAL_ALL); + + std::string s; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "test content of file\n"); + curl_easy_cleanup(curl); +LT_END_AUTO_TEST(file_serving_resource) + LT_BEGIN_AUTO_TEST_ENV() AUTORUN_TESTS() LT_END_AUTO_TEST_ENV() diff --git a/test/unit/http_utils_test.cpp b/test/unit/http_utils_test.cpp index 5c5ceebd..470c90a5 100644 --- a/test/unit/http_utils_test.cpp +++ b/test/unit/http_utils_test.cpp @@ -496,7 +496,7 @@ LT_BEGIN_AUTO_TEST(http_utils_suite, ip_representation6_sockaddr) LT_END_AUTO_TEST(ip_representation6_sockaddr) LT_BEGIN_AUTO_TEST(http_utils_suite, load_file) - LT_CHECK_EQ(std::string(http::load_file("test_content")), "test content of file"); + LT_CHECK_EQ(std::string(http::load_file("test_content")), "test content of file\n"); LT_END_AUTO_TEST(load_file) LT_BEGIN_AUTO_TEST(http_utils_suite, load_file_invalid) From fa35e8905a055254ad4a6444bd3d4c319cda36d9 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Wed, 2 Jan 2019 02:41:49 +0000 Subject: [PATCH 315/623] Testing ban system --- test/Makefile.am | 3 +- test/integ/ban_system.cpp | 173 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 175 insertions(+), 1 deletion(-) create mode 100644 test/integ/ban_system.cpp diff --git a/test/Makefile.am b/test/Makefile.am index 5acd1456..1e4c2daf 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -19,12 +19,13 @@ LDADD = $(top_builddir)/src/libhttpserver.la AM_CPPFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/src/httpserver/ METASOURCES = AUTO -check_PROGRAMS = basic http_utils threaded string_utilities http_endpoint +check_PROGRAMS = basic http_utils threaded string_utilities http_endpoint ban_system MOSTLYCLEANFILES = *.gcda *.gcno *.gcov basic_SOURCES = integ/basic.cpp threaded_SOURCES = integ/threaded.cpp +ban_system_SOURCES = integ/ban_system.cpp http_utils_SOURCES = unit/http_utils_test.cpp string_utilities_SOURCES = unit/string_utilities_test.cpp http_endpoint_SOURCES = unit/http_endpoint_test.cpp diff --git a/test/integ/ban_system.cpp b/test/integ/ban_system.cpp new file mode 100644 index 00000000..f9192a82 --- /dev/null +++ b/test/integ/ban_system.cpp @@ -0,0 +1,173 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011-2019 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#include "littletest.hpp" +#include +#include +#include +#include "httpserver.hpp" +#include "http_utils.hpp" + +using namespace httpserver; +using namespace std; +using namespace http; + +size_t writefunc(void *ptr, size_t size, size_t nmemb, std::string *s) +{ + s->append((char*) ptr, size*nmemb); + return size*nmemb; +} + +class ok_resource : public http_resource +{ + public: + const http_response render_GET(const http_request& req) + { + return http_response_builder("OK", 200, "text/plain").string_response(); + } +}; + +LT_BEGIN_SUITE(ban_system_suite) + void set_up() + { + } + + void tear_down() + { + } +LT_END_SUITE(ban_system_suite) + +LT_BEGIN_AUTO_TEST(ban_system_suite, accept_default_ban_blocks) + webserver ws = create_webserver(8080).default_policy(http_utils::ACCEPT); + ws.start(false); + + ok_resource* resource = new ok_resource(); + ws.register_resource("base", resource); + + { + curl_global_init(CURL_GLOBAL_ALL); + std::string s; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "OK"); + curl_easy_cleanup(curl); + } + + { + ws.ban_ip("127.0.0.1"); + + curl_global_init(CURL_GLOBAL_ALL); + std::string s; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + res = curl_easy_perform(curl); + LT_ASSERT_NEQ(res, 0); + curl_easy_cleanup(curl); + } + + { + ws.unban_ip("127.0.0.1"); + + curl_global_init(CURL_GLOBAL_ALL); + std::string s; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "OK"); + curl_easy_cleanup(curl); + } + + ws.stop(); +LT_END_AUTO_TEST(accept_default_ban_blocks) + +LT_BEGIN_AUTO_TEST(ban_system_suite, reject_default_allow_passes) + webserver ws = create_webserver(8080).default_policy(http_utils::REJECT); + ws.start(false); + + ok_resource* resource = new ok_resource(); + ws.register_resource("base", resource); + + { + curl_global_init(CURL_GLOBAL_ALL); + std::string s; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + res = curl_easy_perform(curl); + LT_ASSERT_NEQ(res, 0); + curl_easy_cleanup(curl); + } + + { + ws.allow_ip("127.0.0.1"); + + curl_global_init(CURL_GLOBAL_ALL); + std::string s; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "OK"); + } + + { + ws.disallow_ip("127.0.0.1"); + + curl_global_init(CURL_GLOBAL_ALL); + std::string s; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + res = curl_easy_perform(curl); + LT_ASSERT_NEQ(res, 0); + curl_easy_cleanup(curl); + } + + ws.stop(); +LT_END_AUTO_TEST(reject_default_allow_passes) + +LT_BEGIN_AUTO_TEST_ENV() + AUTORUN_TESTS() +LT_END_AUTO_TEST_ENV() From f1f71b4315bbebb07863deda40bd547b0fc99d65 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Wed, 2 Jan 2019 19:41:43 +0000 Subject: [PATCH 316/623] Removed unused code --- src/webserver.cpp | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/src/webserver.cpp b/src/webserver.cpp index 92959019..fa451bbb 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -238,28 +238,6 @@ bool webserver::register_resource(const std::string& resource, http_resource* hr return result.second; } -MHD_socket create_socket (int domain, int type, int protocol) -{ - int sock_cloexec = SOCK_CLOEXEC; - int ctype = SOCK_STREAM | sock_cloexec; - - /* use SOCK_STREAM rather than ai_socktype: some getaddrinfo - * implementations do not set ai_socktype, e.g. RHL6.2. */ - MHD_socket fd = socket(domain, ctype, protocol); - -#ifdef _WINDOWS - if (fd == INVALID_SOCKET) -#else - if ((fd == -1) && - (errno == EINVAL || errno == EPROTONOSUPPORT) && (sock_cloexec != 0) - ) -#endif - { - fd = socket(domain, type, protocol); - } - return fd; -} - bool webserver::start(bool blocking) { From a4396dbc652ae3b59e277c00c18a1e0f941c7de9 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Wed, 2 Jan 2019 19:42:13 +0000 Subject: [PATCH 317/623] Added tests to start/stop webserver --- test/Makefile.am | 3 +- test/integ/ws_start_stop.cpp | 226 +++++++++++++++++++++++++++++++++++ 2 files changed, 228 insertions(+), 1 deletion(-) create mode 100644 test/integ/ws_start_stop.cpp diff --git a/test/Makefile.am b/test/Makefile.am index 1e4c2daf..3be3ca25 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -19,13 +19,14 @@ LDADD = $(top_builddir)/src/libhttpserver.la AM_CPPFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/src/httpserver/ METASOURCES = AUTO -check_PROGRAMS = basic http_utils threaded string_utilities http_endpoint ban_system +check_PROGRAMS = basic http_utils threaded string_utilities http_endpoint ban_system ws_start_stop MOSTLYCLEANFILES = *.gcda *.gcno *.gcov basic_SOURCES = integ/basic.cpp threaded_SOURCES = integ/threaded.cpp ban_system_SOURCES = integ/ban_system.cpp +ws_start_stop_SOURCES = integ/ws_start_stop.cpp http_utils_SOURCES = unit/http_utils_test.cpp string_utilities_SOURCES = unit/string_utilities_test.cpp http_endpoint_SOURCES = unit/http_endpoint_test.cpp diff --git a/test/integ/ws_start_stop.cpp b/test/integ/ws_start_stop.cpp new file mode 100644 index 00000000..1ad2d59a --- /dev/null +++ b/test/integ/ws_start_stop.cpp @@ -0,0 +1,226 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011-2019 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#if defined(__MINGW32__) || defined(__CYGWIN32__) +#define _WINDOWS +#undef _WIN32_WINNT +#define _WIN32_WINNT 0x600 +#include +#include +#else +#include +#endif + +#include "littletest.hpp" +#include +#include +#include +#include "httpserver.hpp" + +using namespace std; +using namespace httpserver; + +size_t writefunc(void *ptr, size_t size, size_t nmemb, std::string *s) +{ + s->append((char*) ptr, size*nmemb); + return size*nmemb; +} + +class ok_resource : public httpserver::http_resource +{ + public: + const httpserver::http_response render_GET(const httpserver::http_request& req) + { + return httpserver::http_response_builder("OK", 200, "text/plain").string_response(); + } +}; + +LT_BEGIN_SUITE(ws_start_stop_suite) + void set_up() + { + } + + void tear_down() + { + } +LT_END_SUITE(ws_start_stop_suite) + +LT_BEGIN_AUTO_TEST(ws_start_stop_suite, start_stop) + { + webserver ws = create_webserver(8080); + ok_resource* ok = new ok_resource(); + ws.register_resource("base", ok); + ws.start(false); + + curl_global_init(CURL_GLOBAL_ALL); + std::string s; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "OK"); + curl_easy_cleanup(curl); + + ws.stop(); + } + + { + webserver ws = create_webserver(8080).start_method(http::http_utils::INTERNAL_SELECT); + ok_resource* ok = new ok_resource(); + ws.register_resource("base", ok); + ws.start(false); + + curl_global_init(CURL_GLOBAL_ALL); + std::string s; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "OK"); + curl_easy_cleanup(curl); + + ws.stop(); + } + + { + webserver ws = create_webserver(8080).start_method(http::http_utils::THREAD_PER_CONNECTION); + ok_resource* ok = new ok_resource(); + ws.register_resource("base", ok); + ws.start(false); + + curl_global_init(CURL_GLOBAL_ALL); + std::string s; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "OK"); + curl_easy_cleanup(curl); + + ws.stop(); + } +LT_END_AUTO_TEST(start_stop) + +LT_BEGIN_AUTO_TEST(ws_start_stop_suite, disable_options) + webserver ws = create_webserver(8080) + .no_ssl() + .no_ipv6() + .no_debug() + .no_pedantic() + .no_basic_auth() + .no_digest_auth() + .no_comet() + .no_regex_checking() + .no_ban_system() + .no_post_process(); + ok_resource* ok = new ok_resource(); + ws.register_resource("base", ok); + ws.start(false); + + curl_global_init(CURL_GLOBAL_ALL); + std::string s; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "OK"); + curl_easy_cleanup(curl); + + ws.stop(); +LT_END_AUTO_TEST(disable_options) + +LT_BEGIN_AUTO_TEST(ws_start_stop_suite, enable_options) + webserver ws = create_webserver(8080) + .debug() + .pedantic() + .comet() + .regex_checking() + .ban_system() + .post_process(); + ok_resource* ok = new ok_resource(); + ws.register_resource("base", ok); + ws.start(false); + + curl_global_init(CURL_GLOBAL_ALL); + std::string s; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "OK"); + curl_easy_cleanup(curl); + + ws.stop(); +LT_END_AUTO_TEST(enable_options) + +LT_BEGIN_AUTO_TEST(ws_start_stop_suite, custom_socket) + int fd = socket(AF_INET, SOCK_STREAM, 0); + + struct sockaddr_in address; + address.sin_family = AF_INET; + address.sin_addr.s_addr = inet_addr("127.0.0.1"); + address.sin_port = htons(8181); + bind(fd, (struct sockaddr*) &address, sizeof(address)); + listen(fd, 10000); + + webserver ws = create_webserver(-1).bind_socket(fd); //whatever port here doesn't matter + ok_resource* ok = new ok_resource(); + ws.register_resource("base", ok); + ws.start(false); + + curl_global_init(CURL_GLOBAL_ALL); + std::string s; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8181/base"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "OK"); + curl_easy_cleanup(curl); + + ws.stop(); +LT_END_AUTO_TEST(custom_socket) + +LT_BEGIN_AUTO_TEST_ENV() + AUTORUN_TESTS() +LT_END_AUTO_TEST_ENV() From 5b9d0e2e81c739cdc965aba3d3d6f8b7bb871d3a Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Wed, 2 Jan 2019 21:47:02 +0000 Subject: [PATCH 318/623] Added test for sweet_kill method --- test/integ/ws_start_stop.cpp | 39 ++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/test/integ/ws_start_stop.cpp b/test/integ/ws_start_stop.cpp index 1ad2d59a..283cb0fe 100644 --- a/test/integ/ws_start_stop.cpp +++ b/test/integ/ws_start_stop.cpp @@ -130,6 +130,45 @@ LT_BEGIN_AUTO_TEST(ws_start_stop_suite, start_stop) } LT_END_AUTO_TEST(start_stop) + +LT_BEGIN_AUTO_TEST(ws_start_stop_suite, sweet_kill) + webserver ws = create_webserver(8080); + ok_resource* ok = new ok_resource(); + ws.register_resource("base", ok); + ws.start(false); + + { + curl_global_init(CURL_GLOBAL_ALL); + std::string s; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "OK"); + curl_easy_cleanup(curl); + } + + ws.sweet_kill(); + + { + curl_global_init(CURL_GLOBAL_ALL); + std::string s; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 7); + curl_easy_cleanup(curl); + } +LT_END_AUTO_TEST(sweet_kill) + LT_BEGIN_AUTO_TEST(ws_start_stop_suite, disable_options) webserver ws = create_webserver(8080) .no_ssl() From 7fa1af56ed28e12507bfc3bbd23e678253f84a83 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Wed, 2 Jan 2019 21:48:51 +0000 Subject: [PATCH 319/623] Add clarifying comment --- src/webserver.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/webserver.cpp b/src/webserver.cpp index fa451bbb..6718983a 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -898,7 +898,7 @@ int webserver::finalize_answer( mr->dhrs->get_raw_response(&raw_response, this); } } - catch(...) + catch(...) // catches errors in internal error page { mr->dhrs = NEW_OR_MOVE(http_response, internal_error_page(mr, true)); mr->dhrs->get_raw_response(&raw_response, this); From a8c83343f4bbb8665f052be784c457659b0696be Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Thu, 3 Jan 2019 00:03:18 +0000 Subject: [PATCH 320/623] Fix semantic of the single_resource option --- src/httpserver/create_webserver.hpp | 10 ++++----- src/webserver.cpp | 9 ++++---- test/integ/ws_start_stop.cpp | 32 +++++++++++++++++++++++++++++ 3 files changed, 42 insertions(+), 9 deletions(-) diff --git a/src/httpserver/create_webserver.hpp b/src/httpserver/create_webserver.hpp index d3fc23b0..18df90e0 100644 --- a/src/httpserver/create_webserver.hpp +++ b/src/httpserver/create_webserver.hpp @@ -80,7 +80,7 @@ class create_webserver _ban_system_enabled(true), _post_process_enabled(true), _comet_enabled(false), - _single_resource(0x0), + _single_resource(false), _not_found_resource(0x0), _method_not_allowed_resource(0x0), _method_not_acceptable_resource(0x0), @@ -122,7 +122,7 @@ class create_webserver _ban_system_enabled(true), _post_process_enabled(true), _comet_enabled(false), - _single_resource(0x0), + _single_resource(false), _not_found_resource(0x0), _method_not_allowed_resource(0x0), _method_not_acceptable_resource(0x0), @@ -304,9 +304,9 @@ class create_webserver { _post_process_enabled = false; return *this; } - create_webserver& single_resource(render_ptr single_resource) + create_webserver& single_resource() { - _single_resource = single_resource; return *this; + _single_resource = true; return *this; } create_webserver& not_found_resource(render_ptr not_found_resource) { @@ -367,7 +367,7 @@ class create_webserver bool _ban_system_enabled; bool _post_process_enabled; bool _comet_enabled; - render_ptr _single_resource; + bool _single_resource; render_ptr _not_found_resource; render_ptr _method_not_allowed_resource; render_ptr _method_not_acceptable_resource; diff --git a/src/webserver.cpp b/src/webserver.cpp index 6718983a..bef2558c 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -180,10 +180,6 @@ webserver::webserver(const create_webserver& params): next_to_choose(0), internal_comet_manager(new details::comet_manager()) { - if(single_resource != 0x0) - this->single_resource = true; - else - this->single_resource = false; ignore_sigpipe(); pthread_mutex_init(&mutexwait, NULL); pthread_rwlock_init(&runguard, NULL); @@ -222,6 +218,11 @@ void webserver::request_completed ( bool webserver::register_resource(const std::string& resource, http_resource* hrm, bool family) { + if (single_resource && ((resource != "" && resource != "/") || !family)) + { + throw std::invalid_argument("The resource should be '' or '/' and be marked as family when using a single_resource server"); + } + details::http_endpoint idx(resource, family, true, regex_checking); pair::iterator, bool> result = registered_resources.insert( diff --git a/test/integ/ws_start_stop.cpp b/test/integ/ws_start_stop.cpp index 283cb0fe..0bfdae5d 100644 --- a/test/integ/ws_start_stop.cpp +++ b/test/integ/ws_start_stop.cpp @@ -260,6 +260,38 @@ LT_BEGIN_AUTO_TEST(ws_start_stop_suite, custom_socket) ws.stop(); LT_END_AUTO_TEST(custom_socket) +LT_BEGIN_AUTO_TEST(ws_start_stop_suite, single_resource) + webserver ws = create_webserver(8080).single_resource(); + ok_resource* ok = new ok_resource(); + ws.register_resource("/", ok, true); + ws.start(false); + + curl_global_init(CURL_GLOBAL_ALL); + std::string s; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/any/url/works"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "OK"); + curl_easy_cleanup(curl); + + ws.stop(); +LT_END_AUTO_TEST(single_resource) + +LT_BEGIN_AUTO_TEST(ws_start_stop_suite, single_resource_not_default_resource) + webserver ws = create_webserver(8080).single_resource(); + ok_resource* ok = new ok_resource(); + LT_CHECK_THROW(ws.register_resource("/other", ok, true)); + LT_CHECK_THROW(ws.register_resource("/", ok, false)); + ws.start(false); + + ws.stop(); +LT_END_AUTO_TEST(single_resource_not_default_resource) + LT_BEGIN_AUTO_TEST_ENV() AUTORUN_TESTS() LT_END_AUTO_TEST_ENV() From dc1209959456756f29ce92f8b2101b07574b3901 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Thu, 3 Jan 2019 01:00:53 +0000 Subject: [PATCH 321/623] Add new start/stop tests and ssl tests --- configure.ac | 3 + test/cert.pem | 17 +++++ test/integ/ws_start_stop.cpp | 124 +++++++++++++++++++++++++++++++++++ test/key.pem | 27 ++++++++ test/test_root_ca.pem | 25 +++++++ 5 files changed, 196 insertions(+) create mode 100644 test/cert.pem create mode 100644 test/key.pem create mode 100644 test/test_root_ca.pem diff --git a/configure.ac b/configure.ac index 82d2b31a..9cd9611b 100644 --- a/configure.ac +++ b/configure.ac @@ -309,6 +309,9 @@ AC_SUBST(EXT_LIB_PATH) AC_SUBST(EXT_LIBS) AC_CONFIG_LINKS([test/test_content:test/test_content]) +AC_CONFIG_LINKS([test/cert.pem:test/cert.pem]) +AC_CONFIG_LINKS([test/key.pem:test/key.pem]) +AC_CONFIG_LINKS([test/test_root_ca.pem:test/test_root_ca.pem]) AC_OUTPUT( libhttpserver.pc diff --git a/test/cert.pem b/test/cert.pem new file mode 100644 index 00000000..2c766dff --- /dev/null +++ b/test/cert.pem @@ -0,0 +1,17 @@ +-----BEGIN CERTIFICATE----- +MIICpjCCAZCgAwIBAgIESEPtjjALBgkqhkiG9w0BAQUwADAeFw0wODA2MDIxMjU0 +MzhaFw0wOTA2MDIxMjU0NDZaMAAwggEfMAsGCSqGSIb3DQEBAQOCAQ4AMIIBCQKC +AQC03TyUvK5HmUAirRp067taIEO4bibh5nqolUoUdo/LeblMQV+qnrv/RNAMTx5X +fNLZ45/kbM9geF8qY0vsPyQvP4jumzK0LOJYuIwmHaUm9vbXnYieILiwCuTgjaud +3VkZDoQ9fteIo+6we9UTpVqZpxpbLulBMh/VsvX0cPJ1VFC7rT59o9hAUlFf9jX/ +GmKdYI79MtgVx0OPBjmmSD6kicBBfmfgkO7bIGwlRtsIyMznxbHu6VuoX/eVxrTv +rmCwgEXLWRZ6ru8MQl5YfqeGXXRVwMeXU961KefbuvmEPccgCxm8FZ1C1cnDHFXh +siSgAzMBjC/b6KVhNQ4KnUdZAgMBAAGjLzAtMAwGA1UdEwEB/wQCMAAwHQYDVR0O +BBYEFJcUvpjvE5fF/yzUshkWDpdYiQh/MAsGCSqGSIb3DQEBBQOCAQEARP7eKSB2 +RNd6XjEjK0SrxtoTnxS3nw9sfcS7/qD1+XHdObtDFqGNSjGYFB3Gpx8fpQhCXdoN +8QUs3/5ZVa5yjZMQewWBgz8kNbnbH40F2y81MHITxxCe1Y+qqHWwVaYLsiOTqj2/ +0S3QjEJ9tvklmg7JX09HC4m5QRYfWBeQLD1u8ZjA1Sf1xJriomFVyRLI2VPO2bNe +JDMXWuP+8kMC7gEvUnJ7A92Y2yrhu3QI3bjPk8uSpHea19Q77tul1UVBJ5g+zpH3 +OsF5p0MyaVf09GTzcLds5nE/osTdXGUyHJapWReVmPm3Zn6gqYlnzD99z+DPIgIV +RhZvQx74NQnS6g== +-----END CERTIFICATE----- diff --git a/test/integ/ws_start_stop.cpp b/test/integ/ws_start_stop.cpp index 0bfdae5d..694ae926 100644 --- a/test/integ/ws_start_stop.cpp +++ b/test/integ/ws_start_stop.cpp @@ -292,6 +292,130 @@ LT_BEGIN_AUTO_TEST(ws_start_stop_suite, single_resource_not_default_resource) ws.stop(); LT_END_AUTO_TEST(single_resource_not_default_resource) +LT_BEGIN_AUTO_TEST(ws_start_stop_suite, thread_per_connection_fails_with_max_threads) + { + webserver ws = create_webserver(8080) + .start_method(http::http_utils::THREAD_PER_CONNECTION) + .max_threads(5); + LT_CHECK_THROW(ws.start(false)); + } +LT_END_AUTO_TEST(thread_per_connection_fails_with_max_threads) + +LT_BEGIN_AUTO_TEST(ws_start_stop_suite, tuning_options) + webserver ws = create_webserver(8080) + .max_connections(10) + .max_threads(10) + .memory_limit(10000) + .per_IP_connection_limit(10) + .max_thread_stack_size(10) + .nonce_nc_size(10); + + ok_resource* ok = new ok_resource(); + ws.register_resource("base", ok); + ws.start(false); + + curl_global_init(CURL_GLOBAL_ALL); + std::string s; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "OK"); + curl_easy_cleanup(curl); + + ws.stop(); +LT_END_AUTO_TEST(tuning_options) + +LT_BEGIN_AUTO_TEST(ws_start_stop_suite, ssl_base) + webserver ws = create_webserver(8080) + .use_ssl() + .https_mem_key("key.pem") + .https_mem_cert("cert.pem"); + + ok_resource* ok = new ok_resource(); + ws.register_resource("base", ok); + ws.start(false); + + curl_global_init(CURL_GLOBAL_ALL); + std::string s; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); // avoid verifying ssl + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); // avoid verifying ssl + curl_easy_setopt(curl, CURLOPT_URL, "https://localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "OK"); + curl_easy_cleanup(curl); + + ws.stop(); +LT_END_AUTO_TEST(ssl_base) + +LT_BEGIN_AUTO_TEST(ws_start_stop_suite, ssl_with_protocol_priorities) + webserver ws = create_webserver(8080) + .use_ssl() + .https_mem_key("key.pem") + .https_mem_cert("cert.pem") + .https_priorities("TLS1.0-AES-SHA1"); + + ok_resource* ok = new ok_resource(); + ws.register_resource("base", ok); + ws.start(false); + + curl_global_init(CURL_GLOBAL_ALL); + std::string s; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); // avoid verifying ssl + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); // avoid verifying ssl + curl_easy_setopt(curl, CURLOPT_URL, "https://localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "OK"); + curl_easy_cleanup(curl); + + ws.stop(); +LT_END_AUTO_TEST(ssl_with_protocol_priorities) + +LT_BEGIN_AUTO_TEST(ws_start_stop_suite, ssl_with_trust) + webserver ws = create_webserver(8080) + .use_ssl() + .https_mem_key("key.pem") + .https_mem_cert("cert.pem") + .https_mem_trust("test_root_ca.pem"); + + ok_resource* ok = new ok_resource(); + ws.register_resource("base", ok); + ws.start(false); + + curl_global_init(CURL_GLOBAL_ALL); + std::string s; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); // avoid verifying ssl + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); // avoid verifying ssl + curl_easy_setopt(curl, CURLOPT_URL, "https://localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "OK"); + curl_easy_cleanup(curl); + + ws.stop(); +LT_END_AUTO_TEST(ssl_with_trust) + LT_BEGIN_AUTO_TEST_ENV() AUTORUN_TESTS() LT_END_AUTO_TEST_ENV() diff --git a/test/key.pem b/test/key.pem new file mode 100644 index 00000000..a5848eed --- /dev/null +++ b/test/key.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEAtN08lLyuR5lAIq0adOu7WiBDuG4m4eZ6qJVKFHaPy3m5TEFf +qp67/0TQDE8eV3zS2eOf5GzPYHhfKmNL7D8kLz+I7psytCziWLiMJh2lJvb2152I +niC4sArk4I2rnd1ZGQ6EPX7XiKPusHvVE6VamacaWy7pQTIf1bL19HDydVRQu60+ +faPYQFJRX/Y1/xpinWCO/TLYFcdDjwY5pkg+pInAQX5n4JDu2yBsJUbbCMjM58Wx +7ulbqF/3lca0765gsIBFy1kWeq7vDEJeWH6nhl10VcDHl1PetSnn27r5hD3HIAsZ +vBWdQtXJwxxV4bIkoAMzAYwv2+ilYTUOCp1HWQIDAQABAoIBAArOQv3R7gmqDspj +lDaTFOz0C4e70QfjGMX0sWnakYnDGn6DU19iv3GnX1S072ejtgc9kcJ4e8VUO79R +EmqpdRR7k8dJr3RTUCyjzf/C+qiCzcmhCFYGN3KRHA6MeEnkvRuBogX4i5EG1k5l +/5t+YBTZBnqXKWlzQLKoUAiMLPg0eRWh+6q7H4N7kdWWBmTpako7TEqpIwuEnPGx +u3EPuTR+LN6lF55WBePbCHccUHUQaXuav18NuDkcJmCiMArK9SKb+h0RqLD6oMI/ +dKD6n8cZXeMBkK+C8U/K0sN2hFHACsu30b9XfdnljgP9v+BP8GhnB0nCB6tNBCPo +32srOwECgYEAxWh3iBT4lWqL6bZavVbnhmvtif4nHv2t2/hOs/CAq8iLAw0oWGZc ++JEZTUDMvFRlulr0kcaWra+4fN3OmJnjeuFXZq52lfMgXBIKBmoSaZpIh2aDY1Rd +RbEse7nQl9hTEPmYspiXLGtnAXW7HuWqVfFFP3ya8rUS3t4d07Hig8ECgYEA6ou6 +OHiBRTbtDqLIv8NghARc/AqwNWgEc9PelCPe5bdCOLBEyFjqKiT2MttnSSUc2Zob +XhYkHC6zN1Mlq30N0e3Q61YK9LxMdU1vsluXxNq2rfK1Scb1oOlOOtlbV3zA3VRF +hV3t1nOA9tFmUrwZi0CUMWJE/zbPAyhwWotKyZkCgYEAh0kFicPdbABdrCglXVae +SnfSjVwYkVuGd5Ze0WADvjYsVkYBHTvhgRNnRJMg+/vWz3Sf4Ps4rgUbqK8Vc20b +AU5G6H6tlCvPRGm0ZxrwTWDHTcuKRVs+pJE8C/qWoklE/AAhjluWVoGwUMbPGuiH +6Gf1bgHF6oj/Sq7rv/VLZ8ECgYBeq7ml05YyLuJutuwa4yzQ/MXfghzv4aVyb0F3 +QCdXR6o2IYgR6jnSewrZKlA9aPqFJrwHNR6sNXlnSmt5Fcf/RWO/qgJQGLUv3+rG +7kuLTNDR05azSdiZc7J89ID3Bkb+z2YkV+6JUiPq/Ei1+nDBEXb/m+/HqALU/nyj +P3gXeQKBgBusb8Rbd+KgxSA0hwY6aoRTPRt8LNvXdsB9vRcKKHUFQvxUWiUSS+L9 +/Qu1sJbrUquKOHqksV5wCnWnAKyJNJlhHuBToqQTgKXjuNmVdYSe631saiI7PHyC +eRJ6DxULPxABytJrYCRrNqmXi5TCiqR2mtfalEMOPxz8rUU8dYyx +-----END RSA PRIVATE KEY----- diff --git a/test/test_root_ca.pem b/test/test_root_ca.pem new file mode 100644 index 00000000..998460f1 --- /dev/null +++ b/test/test_root_ca.pem @@ -0,0 +1,25 @@ +-----BEGIN CERTIFICATE----- +MIIEIDCCAwigAwIBAgIQNE7VVyDV7exJ9C/ON9srbTANBgkqhkiG9w0BAQUFADCB +qTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5jLjEoMCYGA1UECxMf +Q2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYGA1UECxMvKGMpIDIw +MDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNlIG9ubHkxHzAdBgNV +BAMTFnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwHhcNMDYxMTE3MDAwMDAwWhcNMzYw +NzE2MjM1OTU5WjCBqTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDHRoYXd0ZSwgSW5j +LjEoMCYGA1UECxMfQ2VydGlmaWNhdGlvbiBTZXJ2aWNlcyBEaXZpc2lvbjE4MDYG +A1UECxMvKGMpIDIwMDYgdGhhd3RlLCBJbmMuIC0gRm9yIGF1dGhvcml6ZWQgdXNl +IG9ubHkxHzAdBgNVBAMTFnRoYXd0ZSBQcmltYXJ5IFJvb3QgQ0EwggEiMA0GCSqG +SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCsoPD7gFnUnMekz52hWXMJEEUMDSxuaPFs +W0hoSVk3/AszGcJ3f8wQLZU0HObrTQmnHNK4yZc2AreJ1CRfBsDMRJSUjQJib+ta +3RGNKJpchJAQeg29dGYvajig4tVUROsdB58Hum/u6f1OCyn1PoSgAfGcq/gcfomk +6KHYcWUNo1F77rzSImANuVud37r8UVsLr5iy6S7pBOhih94ryNdOwUxkHt3Ph1i6 +Sk/KaAcdHJ1KxtUvkcx8cXIcxcBn6zL9yZJclNqFwJu/U30rCfSMnZEfl2pSy94J +NqR32HuHUETVPm4pafs5SSYeCaWAe0At6+gnhcn+Yf1+5nyXHdWdAgMBAAGjQjBA +MA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBR7W0XP +r87Lev0xkhpqtvNG61dIUDANBgkqhkiG9w0BAQUFAAOCAQEAeRHAS7ORtvzw6WfU +DW5FvlXok9LOAz/t2iWwHVfLHjp2oEzsUHboZHIMpKnxuIvW1oeEuzLlQRHAd9mz +YJ3rG9XRbkREqaYB7FViHXe4XI5ISXycO1cRrK1zN44veFyQaEfZYGDm/Ac9IiAX +xPcW6cTYcvnIc3zfFi8VqT79aie2oetaupgf1eNNZAqdE8hhuvU5HIe6uL17In/2 +/qxAeeWsEG89jxt5dovEN7MhGITlNgDrYyCZuen+MwS7QcjBAvlEYyCegc5C09Y/ +LHbTY5xZ3Y+m4Q6gLkH3LpVHz7z9M/P2C2F+fpErgUfCJzDupxBdN49cOSvkBPB7 +jVaMaA== +-----END CERTIFICATE----- From b7487b5f4a46a0f660621bcd3dad696356bc1378 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Thu, 3 Jan 2019 01:03:10 +0000 Subject: [PATCH 322/623] Fix authors --- AUTHORS | 3 +++ 1 file changed, 3 insertions(+) diff --git a/AUTHORS b/AUTHORS index 6a68d8b7..a3d752c2 100644 --- a/AUTHORS +++ b/AUTHORS @@ -24,3 +24,6 @@ Marcel Pursche - Fixed error management and regex handling Julian Picht + +- Fix string termination when loading files in memory +martamoreton From 6e85f70a73217288aea9830f96693903edf8b7f2 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Thu, 3 Jan 2019 02:44:28 +0000 Subject: [PATCH 323/623] Test authentication --- src/webserver.cpp | 2 + test/Makefile.am | 3 +- test/integ/authentication.cpp | 176 ++++++++++++++++++++++++++++++++++ 3 files changed, 180 insertions(+), 1 deletion(-) create mode 100644 test/integ/authentication.cpp diff --git a/src/webserver.cpp b/src/webserver.cpp index bef2558c..23954c19 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -749,7 +749,9 @@ void webserver::end_request_construction( user = MHD_basic_auth_get_username_password(connection, &pass); } if(digest_auth_enabled) + { digested_user = MHD_digest_auth_get_username(connection); + } mr->dhr->set_version(version); const MHD_ConnectionInfo * conninfo = MHD_get_connection_info( connection, diff --git a/test/Makefile.am b/test/Makefile.am index 3be3ca25..7f1806c7 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -19,7 +19,7 @@ LDADD = $(top_builddir)/src/libhttpserver.la AM_CPPFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/src/httpserver/ METASOURCES = AUTO -check_PROGRAMS = basic http_utils threaded string_utilities http_endpoint ban_system ws_start_stop +check_PROGRAMS = basic http_utils threaded string_utilities http_endpoint ban_system ws_start_stop authentication MOSTLYCLEANFILES = *.gcda *.gcno *.gcov @@ -27,6 +27,7 @@ basic_SOURCES = integ/basic.cpp threaded_SOURCES = integ/threaded.cpp ban_system_SOURCES = integ/ban_system.cpp ws_start_stop_SOURCES = integ/ws_start_stop.cpp +authentication_SOURCES = integ/authentication.cpp http_utils_SOURCES = unit/http_utils_test.cpp string_utilities_SOURCES = unit/string_utilities_test.cpp http_endpoint_SOURCES = unit/http_endpoint_test.cpp diff --git a/test/integ/authentication.cpp b/test/integ/authentication.cpp new file mode 100644 index 00000000..ba3ea322 --- /dev/null +++ b/test/integ/authentication.cpp @@ -0,0 +1,176 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011-2019 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#if defined(__MINGW32__) || defined(__CYGWIN32__) +#define _WINDOWS +#undef _WIN32_WINNT +#define _WIN32_WINNT 0x600 +#include +#include +#else +#include +#endif + +#include "littletest.hpp" +#include +#include +#include +#include "httpserver.hpp" + +#define MY_OPAQUE "11733b200778ce33060f31c9af70a870ba96ddd4" + +using namespace std; +using namespace httpserver; + +size_t writefunc(void *ptr, size_t size, size_t nmemb, std::string *s) +{ + s->append((char*) ptr, size*nmemb); + return size*nmemb; +} + +class user_pass_resource : public httpserver::http_resource +{ + public: + const httpserver::http_response render_GET(const httpserver::http_request& req) + { + return httpserver::http_response_builder(req.get_user() + " " + req.get_pass(), 200, "text/plain").string_response(); + } +}; + +class digest_resource : public httpserver::http_resource +{ + public: + const httpserver::http_response render_GET(const httpserver::http_request& req) + { + if (req.get_digested_user() == "") { + return httpserver::http_response_builder("FAIL").digest_auth_fail_response("test@example.com", MY_OPAQUE, true); + } + else + { + bool reload_nonce = false;; + if(!req.check_digest_auth("test@example.com", "mypass", 300, reload_nonce)) + { + return httpserver::http_response_builder("FAIL").digest_auth_fail_response("test@example.com", MY_OPAQUE, reload_nonce); + } + } + return httpserver::http_response_builder("SUCCESS", 200, "text/plain").string_response(); + } +}; + +LT_BEGIN_SUITE(authentication_suite) + void set_up() + { + } + + void tear_down() + { + } +LT_END_SUITE(authentication_suite) + +LT_BEGIN_AUTO_TEST(authentication_suite, base_auth) + webserver ws = create_webserver(8080); + + user_pass_resource* user_pass = new user_pass_resource(); + ws.register_resource("base", user_pass); + ws.start(false); + + curl_global_init(CURL_GLOBAL_ALL); + std::string s; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_USERNAME, "myuser"); + curl_easy_setopt(curl, CURLOPT_PASSWORD, "mypass"); + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "myuser mypass"); + curl_easy_cleanup(curl); + + ws.stop(); +LT_END_AUTO_TEST(base_auth) + +LT_BEGIN_AUTO_TEST(authentication_suite, digest_auth) + webserver ws = create_webserver(8080) + .digest_auth_random("myrandom") + .nonce_nc_size(300); + + digest_resource* digest = new digest_resource(); + ws.register_resource("base", digest); + ws.start(false); + + curl_global_init(CURL_GLOBAL_ALL); + std::string s; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST); + curl_easy_setopt(curl, CURLOPT_USERPWD, "myuser:mypass"); + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + curl_easy_setopt(curl, CURLOPT_TIMEOUT, 150L); + curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 150L); + curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); + curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "SUCCESS"); + curl_easy_cleanup(curl); + + ws.stop(); +LT_END_AUTO_TEST(digest_auth) + +LT_BEGIN_AUTO_TEST(authentication_suite, digest_auth_wrong_pass) + webserver ws = create_webserver(8080) + .digest_auth_random("myrandom") + .nonce_nc_size(300); + + digest_resource* digest = new digest_resource(); + ws.register_resource("base", digest); + ws.start(false); + + curl_global_init(CURL_GLOBAL_ALL); + std::string s; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST); + curl_easy_setopt(curl, CURLOPT_USERPWD, "myuser:wrongpass"); + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + curl_easy_setopt(curl, CURLOPT_TIMEOUT, 150L); + curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 150L); + curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); + curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "FAIL"); + curl_easy_cleanup(curl); + + ws.stop(); +LT_END_AUTO_TEST(digest_auth_wrong_pass) + +LT_BEGIN_AUTO_TEST_ENV() + AUTORUN_TESTS() +LT_END_AUTO_TEST_ENV() From c507fc9f7fde6ce533d3a6e04d5341106e7814d5 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Wed, 2 Jan 2019 18:56:06 -0800 Subject: [PATCH 324/623] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 12c5cdfb..b2f882dd 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,8 @@ The libhttpserver (0.8.0) reference manual ========================================== [![Build Status](https://travis-ci.org/etr/libhttpserver.png?branch=master)](https://travis-ci.org/etr/libhttpserver) +[![codecov](https://codecov.io/gh/etr/libhttpserver/branch/master/graph/badge.svg)](https://codecov.io/gh/etr/libhttpserver) + [![ko-fi](https://www.ko-fi.com/img/donate_sm.png)](https://ko-fi.com/F1F5HY8B) This library has been originally developed under the zencoders flags and this community has always supported me all along this work so I am happy to put the logo on this readme. From 7ed3501f935298c88c02a67f9e9d45e64764500e Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Thu, 3 Jan 2019 03:05:37 +0000 Subject: [PATCH 325/623] Test for basic auth fail --- test/integ/authentication.cpp | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/test/integ/authentication.cpp b/test/integ/authentication.cpp index ba3ea322..1241212e 100644 --- a/test/integ/authentication.cpp +++ b/test/integ/authentication.cpp @@ -50,6 +50,10 @@ class user_pass_resource : public httpserver::http_resource public: const httpserver::http_response render_GET(const httpserver::http_request& req) { + if (req.get_user() != "myuser" || req.get_pass() != "mypass") + { + return httpserver::http_response_builder("FAIL").basic_auth_fail_response("test@example.com"); + } return httpserver::http_response_builder(req.get_user() + " " + req.get_pass(), 200, "text/plain").string_response(); } }; @@ -109,6 +113,31 @@ LT_BEGIN_AUTO_TEST(authentication_suite, base_auth) ws.stop(); LT_END_AUTO_TEST(base_auth) +LT_BEGIN_AUTO_TEST(authentication_suite, base_auth_fail) + webserver ws = create_webserver(8080); + + user_pass_resource* user_pass = new user_pass_resource(); + ws.register_resource("base", user_pass); + ws.start(false); + + curl_global_init(CURL_GLOBAL_ALL); + std::string s; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_USERNAME, "myuser"); + curl_easy_setopt(curl, CURLOPT_PASSWORD, "wrongpass"); + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "FAIL"); + curl_easy_cleanup(curl); + + ws.stop(); +LT_END_AUTO_TEST(base_auth_fail) + LT_BEGIN_AUTO_TEST(authentication_suite, digest_auth) webserver ws = create_webserver(8080) .digest_auth_random("myrandom") From a72c56bc0e1416e0871357c72f90968ab4f298b3 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Thu, 3 Jan 2019 03:38:32 +0000 Subject: [PATCH 326/623] Use credible values for starting options in test --- test/integ/ws_start_stop.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/test/integ/ws_start_stop.cpp b/test/integ/ws_start_stop.cpp index 694ae926..fb656a54 100644 --- a/test/integ/ws_start_stop.cpp +++ b/test/integ/ws_start_stop.cpp @@ -307,12 +307,13 @@ LT_BEGIN_AUTO_TEST(ws_start_stop_suite, tuning_options) .max_threads(10) .memory_limit(10000) .per_IP_connection_limit(10) - .max_thread_stack_size(10) + .max_thread_stack_size(4*1024*1024) .nonce_nc_size(10); + ; ok_resource* ok = new ok_resource(); ws.register_resource("base", ok); - ws.start(false); + LT_CHECK_NOTHROW(ws.start(false)); curl_global_init(CURL_GLOBAL_ALL); std::string s; @@ -363,7 +364,7 @@ LT_BEGIN_AUTO_TEST(ws_start_stop_suite, ssl_with_protocol_priorities) .use_ssl() .https_mem_key("key.pem") .https_mem_cert("cert.pem") - .https_priorities("TLS1.0-AES-SHA1"); + .https_priorities("NONE:+VERS-TLS1.0:+AES-128-CBC:+SHA1:+RSA:+COMP-NULL"); ok_resource* ok = new ok_resource(); ws.register_resource("base", ok); From a9b8351a11c197fc2685f349c8e480f913a8c6a4 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Thu, 3 Jan 2019 03:40:28 +0000 Subject: [PATCH 327/623] Clarify that THREAD_PER_CONNECTION and max_thread_stack_size are incompatible --- src/webserver.cpp | 2 +- test/integ/ws_start_stop.cpp | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/webserver.cpp b/src/webserver.cpp index 23954c19..2aaa631e 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -270,7 +270,7 @@ bool webserver::start(bool blocking) // iov.push_back(gen(MHD_OPTION_SOCK_ADDR, (intptr_t) bind_address)); if(bind_socket != 0) iov.push_back(gen(MHD_OPTION_LISTEN_SOCKET, bind_socket)); - if(start_method == http_utils::THREAD_PER_CONNECTION && max_threads != 0) + if(start_method == http_utils::THREAD_PER_CONNECTION && (max_threads != 0 || max_thread_stack_size != 0)) { throw std::invalid_argument("Cannot specify maximum number of threads when using a thread per connection"); } diff --git a/test/integ/ws_start_stop.cpp b/test/integ/ws_start_stop.cpp index fb656a54..6effe3e1 100644 --- a/test/integ/ws_start_stop.cpp +++ b/test/integ/ws_start_stop.cpp @@ -301,6 +301,15 @@ LT_BEGIN_AUTO_TEST(ws_start_stop_suite, thread_per_connection_fails_with_max_thr } LT_END_AUTO_TEST(thread_per_connection_fails_with_max_threads) +LT_BEGIN_AUTO_TEST(ws_start_stop_suite, thread_per_connection_fails_with_max_threads_stack_size) + { + webserver ws = create_webserver(8080) + .start_method(http::http_utils::THREAD_PER_CONNECTION) + .max_thread_stack_size(4*1024*1024); + LT_CHECK_THROW(ws.start(false)); + } +LT_END_AUTO_TEST(thread_per_connection_fails_with_max_threads_stack_size) + LT_BEGIN_AUTO_TEST(ws_start_stop_suite, tuning_options) webserver ws = create_webserver(8080) .max_connections(10) From a20549eff24d48b1713df8a59787087557e74fc9 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Thu, 3 Jan 2019 04:14:53 +0000 Subject: [PATCH 328/623] Test on blocking server --- test/integ/ws_start_stop.cpp | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/test/integ/ws_start_stop.cpp b/test/integ/ws_start_stop.cpp index 6effe3e1..872ed287 100644 --- a/test/integ/ws_start_stop.cpp +++ b/test/integ/ws_start_stop.cpp @@ -33,6 +33,7 @@ #include #include #include "httpserver.hpp" +#include using namespace std; using namespace httpserver; @@ -426,6 +427,36 @@ LT_BEGIN_AUTO_TEST(ws_start_stop_suite, ssl_with_trust) ws.stop(); LT_END_AUTO_TEST(ssl_with_trust) +void start_ws_blocking(webserver* ws) +{ + ok_resource* ok = new ok_resource(); + ws->register_resource("base", ok); + ws->start(true); +} + +LT_BEGIN_AUTO_TEST(ws_start_stop_suite, blocking_server) + webserver ws = create_webserver(8080); + std::thread server(start_ws_blocking, &ws); + + curl_global_init(CURL_GLOBAL_ALL); + std::string s; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); // avoid verifying ssl + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); // avoid verifying ssl + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "OK"); + curl_easy_cleanup(curl); + + ws.stop(); + server.join(); +LT_END_AUTO_TEST(blocking_server) + LT_BEGIN_AUTO_TEST_ENV() AUTORUN_TESTS() LT_END_AUTO_TEST_ENV() From b273c5165bad65986a73b6eca9961120ccbc5643 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Thu, 3 Jan 2019 04:24:16 +0000 Subject: [PATCH 329/623] Using pthread rather than thread in test --- test/integ/ws_start_stop.cpp | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/test/integ/ws_start_stop.cpp b/test/integ/ws_start_stop.cpp index 872ed287..0f254e7a 100644 --- a/test/integ/ws_start_stop.cpp +++ b/test/integ/ws_start_stop.cpp @@ -33,7 +33,7 @@ #include #include #include "httpserver.hpp" -#include +#include using namespace std; using namespace httpserver; @@ -427,16 +427,21 @@ LT_BEGIN_AUTO_TEST(ws_start_stop_suite, ssl_with_trust) ws.stop(); LT_END_AUTO_TEST(ssl_with_trust) -void start_ws_blocking(webserver* ws) +void* start_ws_blocking(void* par) { + webserver* ws = (webserver*) par; ok_resource* ok = new ok_resource(); ws->register_resource("base", ok); ws->start(true); + + return 0x0; } LT_BEGIN_AUTO_TEST(ws_start_stop_suite, blocking_server) webserver ws = create_webserver(8080); - std::thread server(start_ws_blocking, &ws); + + pthread_t tid; + pthread_create(&tid, NULL, start_ws_blocking, (void *) &ws); curl_global_init(CURL_GLOBAL_ALL); std::string s; @@ -454,7 +459,10 @@ LT_BEGIN_AUTO_TEST(ws_start_stop_suite, blocking_server) curl_easy_cleanup(curl); ws.stop(); - server.join(); + + char* b; + pthread_join(tid,(void**) &b); + free(b); LT_END_AUTO_TEST(blocking_server) LT_BEGIN_AUTO_TEST_ENV() From 6e15c629c6f4ec2bdea9f0e5d3cec2985f8c481f Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Thu, 3 Jan 2019 08:03:16 +0000 Subject: [PATCH 330/623] Add tests for comet mechanisms --- test/Makefile.am | 3 +- test/integ/comet_tests.cpp | 168 +++++++++++++++++++++++++++++++++++++ 2 files changed, 170 insertions(+), 1 deletion(-) create mode 100644 test/integ/comet_tests.cpp diff --git a/test/Makefile.am b/test/Makefile.am index 7f1806c7..9d2e1c4a 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -19,7 +19,7 @@ LDADD = $(top_builddir)/src/libhttpserver.la AM_CPPFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/src/httpserver/ METASOURCES = AUTO -check_PROGRAMS = basic http_utils threaded string_utilities http_endpoint ban_system ws_start_stop authentication +check_PROGRAMS = basic http_utils threaded string_utilities http_endpoint ban_system ws_start_stop authentication comet_tests MOSTLYCLEANFILES = *.gcda *.gcno *.gcov @@ -28,6 +28,7 @@ threaded_SOURCES = integ/threaded.cpp ban_system_SOURCES = integ/ban_system.cpp ws_start_stop_SOURCES = integ/ws_start_stop.cpp authentication_SOURCES = integ/authentication.cpp +comet_tests_SOURCES = integ/comet_tests.cpp http_utils_SOURCES = unit/http_utils_test.cpp string_utilities_SOURCES = unit/string_utilities_test.cpp http_endpoint_SOURCES = unit/http_endpoint_test.cpp diff --git a/test/integ/comet_tests.cpp b/test/integ/comet_tests.cpp new file mode 100644 index 00000000..26f04480 --- /dev/null +++ b/test/integ/comet_tests.cpp @@ -0,0 +1,168 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011-2019 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#if defined(__MINGW32__) || defined(__CYGWIN32__) +#define _WINDOWS +#undef _WIN32_WINNT +#define _WIN32_WINNT 0x600 +#include +#include +#else +#include +#endif + +#include "littletest.hpp" +#include +#include +#include +#include "httpserver.hpp" +#include +#include + +#define MY_OPAQUE "11733b200778ce33060f31c9af70a870ba96ddd4" + +using namespace std; +using namespace httpserver; + +struct closure_type +{ + std::string s; + int counter; +}; + +size_t writefunc_listener(void *ptr, size_t size, size_t nmemb, closure_type* cls) +{ + char* new_content = (char*) ptr; + if (cls->counter > 0) + { + return 0; + } + + cls->counter++; + std::string prefix = "RECEIVED: "; + cls->s.insert(0, prefix); + cls->s.append(new_content, size*nmemb); + return size*nmemb; +} + +size_t writefunc(void *ptr, size_t size, size_t nmemb, std::string* s) +{ + s->append((char*) ptr, size*nmemb); + return size*nmemb; +} + +class polling_resource : public httpserver::http_resource +{ + public: + const httpserver::http_response render_GET(const httpserver::http_request& req) + { + std::string topic = "interesting_topic"; + if (req.get_arg("action") == "receive") + { + std::vector topics; + topics.push_back(topic); + return httpserver::http_response_builder("interesting listener").long_polling_receive_response(topics); + } + else + { + return httpserver::http_response_builder("interesting message").long_polling_send_response(topic); + } + } +}; + +LT_BEGIN_SUITE(authentication_suite) + void set_up() + { + } + + void tear_down() + { + } +LT_END_SUITE(authentication_suite) + +void* listener(void* par) +{ + curl_global_init(CURL_GLOBAL_ALL); + + CURL *curl = curl_easy_init(); + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base?action=receive"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc_listener); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, (closure_type*) par); + curl_easy_perform(curl); + curl_easy_cleanup(curl); + + return 0x0; +} + +LT_BEGIN_AUTO_TEST(authentication_suite, comet) + webserver* ws = new webserver(create_webserver(8080).comet()); + + polling_resource* pr = new polling_resource(); + ws->register_resource("base", pr); + ws->start(false); + + closure_type cls; + cls.counter = 0; + + pthread_t tid; + pthread_create(&tid, NULL, listener, (void *) &cls); + + sleep(1); + + { + curl_global_init(CURL_GLOBAL_ALL); + std::string s; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base?action=send"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "interesting message"); + curl_easy_cleanup(curl); + } + + { + curl_global_init(CURL_GLOBAL_ALL); + std::string s; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base?action=send"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "interesting message"); + curl_easy_cleanup(curl); + } + + pthread_join(tid, NULL); + + LT_CHECK_EQ(cls.s, "RECEIVED: interesting message"); + //not stopping to avoid errors with pending connections +LT_END_AUTO_TEST(comet) + +LT_BEGIN_AUTO_TEST_ENV() + AUTORUN_TESTS() +LT_END_AUTO_TEST_ENV() From 430f8ee999786bccce18ae5c3ee0e58b0db18091 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Thu, 3 Jan 2019 21:08:09 +0000 Subject: [PATCH 331/623] Remove custom method_not_acceptable (unused). Added tests --- src/httpserver/create_webserver.hpp | 12 +---- src/httpserver/webserver.hpp | 6 --- src/webserver.cpp | 3 +- test/integ/ws_start_stop.cpp | 79 +++++++++++++++++++++++++++++ 4 files changed, 81 insertions(+), 19 deletions(-) diff --git a/src/httpserver/create_webserver.hpp b/src/httpserver/create_webserver.hpp index 18df90e0..88458fa3 100644 --- a/src/httpserver/create_webserver.hpp +++ b/src/httpserver/create_webserver.hpp @@ -37,7 +37,7 @@ namespace httpserver { class webserver; class http_request; -typedef http_response(*render_ptr)(const http_request&); +typedef const http_response(*render_ptr)(const http_request&); typedef bool(*validator_ptr)(const std::string&); typedef void(*unescaper_ptr)(char*); typedef void(*log_access_ptr)(const std::string&); @@ -83,7 +83,6 @@ class create_webserver _single_resource(false), _not_found_resource(0x0), _method_not_allowed_resource(0x0), - _method_not_acceptable_resource(0x0), _internal_error_resource(0x0) { } @@ -125,7 +124,6 @@ class create_webserver _single_resource(false), _not_found_resource(0x0), _method_not_allowed_resource(0x0), - _method_not_acceptable_resource(0x0), _internal_error_resource(0x0) { } @@ -319,13 +317,6 @@ class create_webserver _method_not_allowed_resource = method_not_allowed_resource; return *this; } - create_webserver& method_not_acceptable_resource( - render_ptr method_not_acceptable_resource - ) - { - _method_not_acceptable_resource = method_not_acceptable_resource; - return *this; - } create_webserver& internal_error_resource( render_ptr internal_error_resource ) @@ -370,7 +361,6 @@ class create_webserver bool _single_resource; render_ptr _not_found_resource; render_ptr _method_not_allowed_resource; - render_ptr _method_not_acceptable_resource; render_ptr _internal_error_resource; friend class webserver; diff --git a/src/httpserver/webserver.hpp b/src/httpserver/webserver.hpp index 4b9e611a..ee5610e4 100644 --- a/src/httpserver/webserver.hpp +++ b/src/httpserver/webserver.hpp @@ -191,7 +191,6 @@ class webserver pthread_cond_t mutexcond; render_ptr not_found_resource; render_ptr method_not_allowed_resource; - render_ptr method_not_acceptable_resource; render_ptr internal_error_resource; std::map registered_resources; std::map registered_resources_str; @@ -212,11 +211,6 @@ class webserver const http_response internal_error_page(details::modded_request* mr, bool force_our = false) const; const http_response not_found_page(details::modded_request* mr) const; - static int method_not_acceptable_page - ( - const void *cls, - struct MHD_Connection *connection - ); static void request_completed(void *cls, struct MHD_Connection *connection, void **con_cls, enum MHD_RequestTerminationCode toe diff --git a/src/webserver.cpp b/src/webserver.cpp index 2aaa631e..93940c06 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -175,7 +175,6 @@ webserver::webserver(const create_webserver& params): single_resource(params._single_resource), not_found_resource(params._not_found_resource), method_not_allowed_resource(params._method_not_allowed_resource), - method_not_acceptable_resource(params._method_not_acceptable_resource), internal_error_resource(params._internal_error_resource), next_to_choose(0), internal_comet_manager(new details::comet_manager()) @@ -607,7 +606,7 @@ const http_response webserver::not_found_page(details::modded_request* mr) const const http_response webserver::method_not_allowed_page(details::modded_request* mr) const { - if(method_not_acceptable_resource != 0x0) + if(method_not_allowed_resource != 0x0) { return method_not_allowed_resource(*mr->dhr); } diff --git a/test/integ/ws_start_stop.cpp b/test/integ/ws_start_stop.cpp index 0f254e7a..db4f9971 100644 --- a/test/integ/ws_start_stop.cpp +++ b/test/integ/ws_start_stop.cpp @@ -53,6 +53,16 @@ class ok_resource : public httpserver::http_resource } }; +const httpserver::http_response not_found_custom(const httpserver::http_request& req) +{ + return httpserver::http_response_builder("Not found custom", 404, "text/plain").string_response(); +} + +const httpserver::http_response not_allowed_custom(const httpserver::http_request& req) +{ + return httpserver::http_response_builder("Not allowed custom", 405, "text/plain").string_response(); +} + LT_BEGIN_SUITE(ws_start_stop_suite) void set_up() { @@ -465,6 +475,75 @@ LT_BEGIN_AUTO_TEST(ws_start_stop_suite, blocking_server) free(b); LT_END_AUTO_TEST(blocking_server) +LT_BEGIN_AUTO_TEST(ws_start_stop_suite, custom_error_resources) + webserver ws = create_webserver(8080) + .not_found_resource(not_found_custom) + .method_not_allowed_resource(not_allowed_custom); + + ok_resource* ok = new ok_resource(); + ws.register_resource("base", ok); + ws.start(false); + + { + curl_global_init(CURL_GLOBAL_ALL); + std::string s; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "OK"); + curl_easy_cleanup(curl); + } + + { + curl_global_init(CURL_GLOBAL_ALL); + std::string s; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/not_registered"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "Not found custom"); + + long http_code = 0; + curl_easy_getinfo (curl, CURLINFO_RESPONSE_CODE, &http_code); + LT_ASSERT_EQ(http_code, 404); + + curl_easy_cleanup(curl); + } + + { + ok->set_allowing("PUT", false); + + curl_global_init(CURL_GLOBAL_ALL); + std::string s; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PUT"); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "Not allowed custom"); + + long http_code = 0; + curl_easy_getinfo (curl, CURLINFO_RESPONSE_CODE, &http_code); + LT_ASSERT_EQ(http_code, 405); + + curl_easy_cleanup(curl); + } + + ws.stop(); +LT_END_AUTO_TEST(custom_error_resources) + LT_BEGIN_AUTO_TEST_ENV() AUTORUN_TESTS() LT_END_AUTO_TEST_ENV() From 558e18dc28cbc97ca4956c9bb1ba5e35c754dd2e Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Thu, 3 Jan 2019 21:59:54 +0000 Subject: [PATCH 332/623] Test for exception throwing resources --- test/integ/basic.cpp | 64 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/test/integ/basic.cpp b/test/integ/basic.cpp index 94165e72..49ccb815 100644 --- a/test/integ/basic.cpp +++ b/test/integ/basic.cpp @@ -187,6 +187,24 @@ class file_response_resource : public http_resource } }; +class exception_resource : public http_resource +{ + public: + const http_response render_GET(const http_request& req) + { + throw std::domain_error("invalid"); + } +}; + +class error_resource : public http_resource +{ + public: + const http_response render_GET(const http_request& req) + { + throw "invalid"; + } +}; + LT_BEGIN_SUITE(basic_suite) webserver* ws; @@ -724,6 +742,52 @@ LT_BEGIN_AUTO_TEST(basic_suite, file_serving_resource) curl_easy_cleanup(curl); LT_END_AUTO_TEST(file_serving_resource) +LT_BEGIN_AUTO_TEST(basic_suite, exception_forces_500) + exception_resource* resource = new exception_resource(); + ws->register_resource("base", resource); + curl_global_init(CURL_GLOBAL_ALL); + + std::string s; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "Internal Error"); + + long http_code = 0; + curl_easy_getinfo (curl, CURLINFO_RESPONSE_CODE, &http_code); + LT_ASSERT_EQ(http_code, 500); + + curl_easy_cleanup(curl); +LT_END_AUTO_TEST(exception_forces_500) + +LT_BEGIN_AUTO_TEST(basic_suite, untyped_error_forces_500) + error_resource* resource = new error_resource(); + ws->register_resource("base", resource); + curl_global_init(CURL_GLOBAL_ALL); + + std::string s; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "Internal Error"); + + long http_code = 0; + curl_easy_getinfo (curl, CURLINFO_RESPONSE_CODE, &http_code); + LT_ASSERT_EQ(http_code, 500); + + curl_easy_cleanup(curl); +LT_END_AUTO_TEST(untyped_error_forces_500) + LT_BEGIN_AUTO_TEST_ENV() AUTORUN_TESTS() LT_END_AUTO_TEST_ENV() From 7f652e612de3b77e881aa9d979a5e49a3dfe2b4d Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Fri, 4 Jan 2019 20:33:05 +0000 Subject: [PATCH 333/623] Added tests on request and response string streaming --- test/integ/basic.cpp | 107 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 107 insertions(+) diff --git a/test/integ/basic.cpp b/test/integ/basic.cpp index 49ccb815..e4965453 100644 --- a/test/integ/basic.cpp +++ b/test/integ/basic.cpp @@ -205,6 +205,49 @@ class error_resource : public http_resource } }; +class print_request_resource : public http_resource +{ + public: + print_request_resource(std::stringstream* ss) + { + this->ss = ss; + } + + const http_response render_GET(const http_request& req) + { + (*ss) << req; + return http_response_builder("OK", 200, "text/plain").string_response(); + } + + private: + std::stringstream* ss; +}; + +class print_response_resource : public http_resource +{ + public: + print_response_resource(std::stringstream* ss) + { + this->ss = ss; + } + + const http_response render_GET(const http_request& req) + { + http_response hresp(http_response_builder("OK", 200, "text/plain") + .with_header("MyResponseHeader", "MyResponseHeaderValue") + .with_footer("MyResponseFooter", "MyResponseFooterValue") + .with_cookie("MyResponseCookie", "MyResponseCookieValue") + .string_response() + ); + (*ss) << hresp; + + return hresp; + } + + private: + std::stringstream* ss; +}; + LT_BEGIN_SUITE(basic_suite) webserver* ws; @@ -788,6 +831,70 @@ LT_BEGIN_AUTO_TEST(basic_suite, untyped_error_forces_500) curl_easy_cleanup(curl); LT_END_AUTO_TEST(untyped_error_forces_500) +LT_BEGIN_AUTO_TEST(basic_suite, request_is_printable) + std::stringstream ss; + print_request_resource* resource = new print_request_resource(&ss); + ws->register_resource("base", resource); + curl_global_init(CURL_GLOBAL_ALL); + + std::string s; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + + struct curl_slist *list = NULL; + list = curl_slist_append(list, "MyHeader: MyValue"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list); + + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "OK"); + + std::string actual = ss.str(); + LT_CHECK_EQ(actual.find("GET Request") != string::npos, true); + LT_CHECK_EQ(actual.find("Headers [") != string::npos, true); + LT_CHECK_EQ(actual.find("Host") != string::npos, true); + LT_CHECK_EQ(actual.find("Accept:\"*/*\"") != string::npos, true); + LT_CHECK_EQ(actual.find("MyHeader:\"MyValue\"") != string::npos, true); + LT_CHECK_EQ(actual.find("Version [ HTTP/1.1 ]") != string::npos, true); + + curl_easy_cleanup(curl); +LT_END_AUTO_TEST(request_is_printable) + +LT_BEGIN_AUTO_TEST(basic_suite, response_is_printable) + std::stringstream ss; + print_response_resource* resource = new print_response_resource(&ss); + ws->register_resource("base", resource); + curl_global_init(CURL_GLOBAL_ALL); + + std::string s; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + + struct curl_slist *list = NULL; + list = curl_slist_append(list, "MyHeader: MyValue"); + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list); + + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "OK"); + + std::string actual = ss.str(); + LT_CHECK_EQ(actual.find("Response [response_code:200]") != string::npos, true); + LT_CHECK_EQ(actual.find("Headers [Content-Type:\"text/plain\" MyResponseHeader:\"MyResponseHeaderValue\" ]") != string::npos, true); + LT_CHECK_EQ(actual.find("Footers [MyResponseFooter:\"MyResponseFooterValue\" ]") != string::npos, true); + LT_CHECK_EQ(actual.find("Cookies [MyResponseCookie:\"MyResponseCookieValue\" ]") != string::npos, true); + + curl_easy_cleanup(curl); +LT_END_AUTO_TEST(response_is_printable) + LT_BEGIN_AUTO_TEST_ENV() AUTORUN_TESTS() LT_END_AUTO_TEST_ENV() From cbb8d889660da50a36b0a6fa6c6b04e6c185a4fe Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Fri, 4 Jan 2019 21:36:57 +0000 Subject: [PATCH 334/623] Added tests on deferred resources --- test/Makefile.am | 3 +- test/integ/deferred.cpp | 110 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 112 insertions(+), 1 deletion(-) create mode 100644 test/integ/deferred.cpp diff --git a/test/Makefile.am b/test/Makefile.am index 9d2e1c4a..0c02c692 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -19,7 +19,7 @@ LDADD = $(top_builddir)/src/libhttpserver.la AM_CPPFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/src/httpserver/ METASOURCES = AUTO -check_PROGRAMS = basic http_utils threaded string_utilities http_endpoint ban_system ws_start_stop authentication comet_tests +check_PROGRAMS = basic http_utils threaded string_utilities http_endpoint ban_system ws_start_stop authentication comet_tests deferred MOSTLYCLEANFILES = *.gcda *.gcno *.gcov @@ -29,6 +29,7 @@ ban_system_SOURCES = integ/ban_system.cpp ws_start_stop_SOURCES = integ/ws_start_stop.cpp authentication_SOURCES = integ/authentication.cpp comet_tests_SOURCES = integ/comet_tests.cpp +deferred_SOURCES = integ/deferred.cpp http_utils_SOURCES = unit/http_utils_test.cpp string_utilities_SOURCES = unit/string_utilities_test.cpp http_endpoint_SOURCES = unit/http_endpoint_test.cpp diff --git a/test/integ/deferred.cpp b/test/integ/deferred.cpp new file mode 100644 index 00000000..e7c91c7b --- /dev/null +++ b/test/integ/deferred.cpp @@ -0,0 +1,110 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011-2019 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#if defined(__MINGW32__) || defined(__CYGWIN32__) +#define _WINDOWS +#undef _WIN32_WINNT +#define _WIN32_WINNT 0x600 +#include +#include +#else +#include +#endif + +#include "littletest.hpp" +#include +#include +#include +#include "httpserver.hpp" +#include +#include + +using namespace std; +using namespace httpserver; + +size_t writefunc(void *ptr, size_t size, size_t nmemb, std::string *s) +{ + s->append((char*) ptr, size*nmemb); + return size*nmemb; +} + +static int counter = 0; + +ssize_t test_callback (char* buf, size_t max) +{ + if (counter == 2) + { + return -1; + } + else + { + memset(buf, 0, max); + strcat(buf, "test"); + counter++; + return strlen(buf); + } +} + +class deferred_resource : public httpserver::http_resource +{ + public: + const httpserver::http_response render_GET(const httpserver::http_request& req) + { + return httpserver::http_response_builder("cycle callback response").deferred_response(test_callback); + } +}; + +LT_BEGIN_SUITE(deferred_suite) + webserver* ws; + + void set_up() + { + ws = new webserver(create_webserver(8080)); + ws->start(false); + } + + void tear_down() + { + ws->stop(); + delete ws; + } +LT_END_SUITE(deferred_suite) + +LT_BEGIN_AUTO_TEST(deferred_suite, deferred_response) + deferred_resource* resource = new deferred_resource(); + ws->register_resource("base", resource); + curl_global_init(CURL_GLOBAL_ALL); + + std::string s; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "testtest"); + curl_easy_cleanup(curl); +LT_END_AUTO_TEST(deferred_response) + +LT_BEGIN_AUTO_TEST_ENV() + AUTORUN_TESTS() +LT_END_AUTO_TEST_ENV() From 89c6b10668d0a84575de529419d22087dd805a1e Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sat, 5 Jan 2019 18:00:58 -0800 Subject: [PATCH 335/623] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index b2f882dd..97f6cc63 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,7 @@ The libhttpserver (0.8.0) reference manual [![Build Status](https://travis-ci.org/etr/libhttpserver.png?branch=master)](https://travis-ci.org/etr/libhttpserver) [![codecov](https://codecov.io/gh/etr/libhttpserver/branch/master/graph/badge.svg)](https://codecov.io/gh/etr/libhttpserver) +[![Codacy Badge](https://api.codacy.com/project/badge/Grade/5fa4bdc3815b4c10977f3badefedecd6)](https://www.codacy.com/app/etr/libhttpserver?utm_source=github.com&utm_medium=referral&utm_content=etr/libhttpserver&utm_campaign=Badge_Grade) [![ko-fi](https://www.ko-fi.com/img/donate_sm.png)](https://ko-fi.com/F1F5HY8B) From 5b22550eebec81a88d7b5b47443d31d8e250fa33 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 6 Jan 2019 03:21:13 +0000 Subject: [PATCH 336/623] Additional memory checks on travis (currently with prevented fail) --- .travis.yml | 61 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/.travis.yml b/.travis.yml index c5da85fb..7e8f81b6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -55,6 +55,13 @@ install: - if [ "$CROSS_COMPILE" = "1" ] && [ "$ARM_ARCH_DIR" = "aarch64-linux-gnu" ]; then sed -i 's+/usr/bin/ld+$HOME/linker_bin/ld+g' Makefile; fi - make - make check TESTS= +before_script: + - if [ "$BUILD_TYPE" = "asan" ]; then export CFLAGS='-fsanitize=address'; export LDFLAGS='-fsanitize=address'; fi + - if [ "$BUILD_TYPE" = "asanpc" ]; then export CFLAGS='-fsanitize=address -fsanitize=pointer-compare'; export LDFLAGS='-fsanitize=address -fsanitize=pointer-compare'; fi + - if [ "$BUILD_TYPE" = "asanps" ]; then export CFLAGS='-fsanitize=address -fsanitize=pointer-subtract'; export LDFLAGS='-fsanitize=address -fsanitize=pointer-subtract'; fi + - if [ "$BUILD_TYPE" = "lsan" ]; then export CFLAGS='-fsanitize=leak'; export LDFLAGS='-fsanitize=leak'; fi + - if [ "$BUILD_TYPE" = "tsan" ]; then export CFLAGS='-fsanitize=thread'; export LDFLAGS='-fsanitize=thread'; fi + - if [ "$BUILD_TYPE" = "ubsan" ]; then export export CFLAGS='-fsanitize=undefined'; export LDFLAGS='-fsanitize=undefined'; fi script: - if [[ "$CROSS_COMPILE" == 1 ]]; then cd test ; @@ -100,6 +107,54 @@ matrix: - g++-5 env: - MATRIX_EVAL="CC=gcc-5 && CXX=g++-5" + - os: linux + compiler: gcc + addons: + apt: + sources: + - ubuntu-toolchain-r-test + env: + - BUILD_TYPE=asan + - os: linux + compiler: gcc + addons: + apt: + sources: + - ubuntu-toolchain-r-test + env: + - BUILD_TYPE=asanpc + - os: linux + compiler: gcc + addons: + apt: + sources: + - ubuntu-toolchain-r-test + env: + - BUILD_TYPE=asanps + - os: linux + compiler: gcc + addons: + apt: + sources: + - ubuntu-toolchain-r-test + env: + - BUILD_TYPE=lsan + - os: linux + compiler: gcc + addons: + apt: + sources: + - ubuntu-toolchain-r-test + env: + - BUILD_TYPE=tsan + - os: linux + compiler: gcc + addons: + apt: + sources: + - ubuntu-toolchain-r-test + env: + - BUILD_TYPE=ubsan - os: linux addons: apt: @@ -229,3 +284,9 @@ matrix: allow_failures: - env: MATRIX_EVAL="CROSS_COMPILE=1 && ARM_ARCH_DIR=arm-linux-gnueabi && CC=arm-linux-gnueabi-gcc" - env: MATRIX_EVAL="CROSS_COMPILE=1 && ARM_LD_PATH=$HOME/linker_bin && ARM_ARCH_DIR=aarch64-linux-gnu" + - env: BUILD_TYPE=asan + - env: BUILD_TYPE=asanpc + - env: BUILD_TYPE=asanps + - env: BUILD_TYPE=lsan + - env: BUILD_TYPE=tsan + - env: BUILD_TYPE=ubsan From a3d5c8d20819e50a27c80eeed9792a0d4ab39282 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 6 Jan 2019 03:25:37 +0000 Subject: [PATCH 337/623] Using matrix eval --- .travis.yml | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/.travis.yml b/.travis.yml index 7e8f81b6..3932fa83 100644 --- a/.travis.yml +++ b/.travis.yml @@ -114,7 +114,7 @@ matrix: sources: - ubuntu-toolchain-r-test env: - - BUILD_TYPE=asan + - MATRIX_EVAL="BUILD_TYPE=asan" - os: linux compiler: gcc addons: @@ -122,7 +122,7 @@ matrix: sources: - ubuntu-toolchain-r-test env: - - BUILD_TYPE=asanpc + - MATRIX_EVAL="BUILD_TYPE=asanpc" - os: linux compiler: gcc addons: @@ -130,7 +130,7 @@ matrix: sources: - ubuntu-toolchain-r-test env: - - BUILD_TYPE=asanps + - MATRIX_EVAL="BUILD_TYPE=asanps" - os: linux compiler: gcc addons: @@ -138,7 +138,7 @@ matrix: sources: - ubuntu-toolchain-r-test env: - - BUILD_TYPE=lsan + - MATRIX_EVAL="BUILD_TYPE=lsan" - os: linux compiler: gcc addons: @@ -146,7 +146,7 @@ matrix: sources: - ubuntu-toolchain-r-test env: - - BUILD_TYPE=tsan + - MATRIX_EVAL="BUILD_TYPE=tsan" - os: linux compiler: gcc addons: @@ -154,7 +154,7 @@ matrix: sources: - ubuntu-toolchain-r-test env: - - BUILD_TYPE=ubsan + - MATRIX_EVAL="BUILD_TYPE=ubsan" - os: linux addons: apt: @@ -284,9 +284,9 @@ matrix: allow_failures: - env: MATRIX_EVAL="CROSS_COMPILE=1 && ARM_ARCH_DIR=arm-linux-gnueabi && CC=arm-linux-gnueabi-gcc" - env: MATRIX_EVAL="CROSS_COMPILE=1 && ARM_LD_PATH=$HOME/linker_bin && ARM_ARCH_DIR=aarch64-linux-gnu" - - env: BUILD_TYPE=asan - - env: BUILD_TYPE=asanpc - - env: BUILD_TYPE=asanps - - env: BUILD_TYPE=lsan - - env: BUILD_TYPE=tsan - - env: BUILD_TYPE=ubsan + - env: MATRIX_EVAL="BUILD_TYPE=asan" + - env: MATRIX_EVAL="BUILD_TYPE=asanpc" + - env: MATRIX_EVAL="BUILD_TYPE=asanps" + - env: MATRIX_EVAL="BUILD_TYPE=lsan" + - env: MATRIX_EVAL="BUILD_TYPE=tsan" + - env: MATRIX_EVAL="BUILD_TYPE=ubsan" From ea8b0a41ee35a46688eb28eadae0b83c1a9f2861 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 6 Jan 2019 03:27:56 +0000 Subject: [PATCH 338/623] Decorate allow failure with compiler --- .travis.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.travis.yml b/.travis.yml index 3932fa83..40003161 100644 --- a/.travis.yml +++ b/.travis.yml @@ -285,8 +285,14 @@ matrix: - env: MATRIX_EVAL="CROSS_COMPILE=1 && ARM_ARCH_DIR=arm-linux-gnueabi && CC=arm-linux-gnueabi-gcc" - env: MATRIX_EVAL="CROSS_COMPILE=1 && ARM_LD_PATH=$HOME/linker_bin && ARM_ARCH_DIR=aarch64-linux-gnu" - env: MATRIX_EVAL="BUILD_TYPE=asan" + compiler: gcc - env: MATRIX_EVAL="BUILD_TYPE=asanpc" + compiler: gcc - env: MATRIX_EVAL="BUILD_TYPE=asanps" + compiler: gcc - env: MATRIX_EVAL="BUILD_TYPE=lsan" + compiler: gcc - env: MATRIX_EVAL="BUILD_TYPE=tsan" + compiler: gcc - env: MATRIX_EVAL="BUILD_TYPE=ubsan" + compiler: gcc From e762ef5a99d67d6b21c6481f5fbd9fd428d2959b Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 6 Jan 2019 03:30:38 +0000 Subject: [PATCH 339/623] Align matrix setting and ignore failure --- .travis.yml | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/.travis.yml b/.travis.yml index 40003161..14bfb147 100644 --- a/.travis.yml +++ b/.travis.yml @@ -108,7 +108,6 @@ matrix: env: - MATRIX_EVAL="CC=gcc-5 && CXX=g++-5" - os: linux - compiler: gcc addons: apt: sources: @@ -116,7 +115,6 @@ matrix: env: - MATRIX_EVAL="BUILD_TYPE=asan" - os: linux - compiler: gcc addons: apt: sources: @@ -124,7 +122,6 @@ matrix: env: - MATRIX_EVAL="BUILD_TYPE=asanpc" - os: linux - compiler: gcc addons: apt: sources: @@ -132,7 +129,6 @@ matrix: env: - MATRIX_EVAL="BUILD_TYPE=asanps" - os: linux - compiler: gcc addons: apt: sources: @@ -140,7 +136,6 @@ matrix: env: - MATRIX_EVAL="BUILD_TYPE=lsan" - os: linux - compiler: gcc addons: apt: sources: @@ -148,7 +143,6 @@ matrix: env: - MATRIX_EVAL="BUILD_TYPE=tsan" - os: linux - compiler: gcc addons: apt: sources: @@ -285,14 +279,8 @@ matrix: - env: MATRIX_EVAL="CROSS_COMPILE=1 && ARM_ARCH_DIR=arm-linux-gnueabi && CC=arm-linux-gnueabi-gcc" - env: MATRIX_EVAL="CROSS_COMPILE=1 && ARM_LD_PATH=$HOME/linker_bin && ARM_ARCH_DIR=aarch64-linux-gnu" - env: MATRIX_EVAL="BUILD_TYPE=asan" - compiler: gcc - env: MATRIX_EVAL="BUILD_TYPE=asanpc" - compiler: gcc - env: MATRIX_EVAL="BUILD_TYPE=asanps" - compiler: gcc - env: MATRIX_EVAL="BUILD_TYPE=lsan" - compiler: gcc - env: MATRIX_EVAL="BUILD_TYPE=tsan" - compiler: gcc - env: MATRIX_EVAL="BUILD_TYPE=ubsan" - compiler: gcc From e1e7ef5f90e30dd2481b9a347155da38127d4347 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 6 Jan 2019 03:33:12 +0000 Subject: [PATCH 340/623] Align matrix with ignore failure --- .travis.yml | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/.travis.yml b/.travis.yml index 14bfb147..f3c41a24 100644 --- a/.travis.yml +++ b/.travis.yml @@ -112,43 +112,37 @@ matrix: apt: sources: - ubuntu-toolchain-r-test - env: - - MATRIX_EVAL="BUILD_TYPE=asan" + env: MATRIX_EVAL="BUILD_TYPE=asan" - os: linux addons: apt: sources: - ubuntu-toolchain-r-test - env: - - MATRIX_EVAL="BUILD_TYPE=asanpc" + env: MATRIX_EVAL="BUILD_TYPE=asanpc" - os: linux addons: apt: sources: - ubuntu-toolchain-r-test - env: - - MATRIX_EVAL="BUILD_TYPE=asanps" + env: MATRIX_EVAL="BUILD_TYPE=asanps" - os: linux addons: apt: sources: - ubuntu-toolchain-r-test - env: - - MATRIX_EVAL="BUILD_TYPE=lsan" + env: MATRIX_EVAL="BUILD_TYPE=lsan" - os: linux addons: apt: sources: - ubuntu-toolchain-r-test - env: - - MATRIX_EVAL="BUILD_TYPE=tsan" + env: MATRIX_EVAL="BUILD_TYPE=tsan" - os: linux addons: apt: sources: - ubuntu-toolchain-r-test - env: - - MATRIX_EVAL="BUILD_TYPE=ubsan" + env: MATRIX_EVAL="BUILD_TYPE=ubsan" - os: linux addons: apt: From 1008b0358eccad7c4df944e1a5ecccb4504f16be Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 6 Jan 2019 03:48:04 +0000 Subject: [PATCH 341/623] Move setting for checks ahead of the install block --- .travis.yml | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/.travis.yml b/.travis.yml index f3c41a24..f5e351b7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -27,6 +27,12 @@ before_install: - make - sudo make install - cd .. + - if [ "$BUILD_TYPE" = "asan" ]; then export CFLAGS='-fsanitize=address'; export LDFLAGS='-fsanitize=address'; fi + - if [ "$BUILD_TYPE" = "asanpc" ]; then export CFLAGS='-fsanitize=address -fsanitize=pointer-compare'; export LDFLAGS='-fsanitize=address -fsanitize=pointer-compare'; fi + - if [ "$BUILD_TYPE" = "asanps" ]; then export CFLAGS='-fsanitize=address -fsanitize=pointer-subtract'; export LDFLAGS='-fsanitize=address -fsanitize=pointer-subtract'; fi + - if [ "$BUILD_TYPE" = "lsan" ]; then export CFLAGS='-fsanitize=leak'; export LDFLAGS='-fsanitize=leak'; fi + - if [ "$BUILD_TYPE" = "tsan" ]; then export CFLAGS='-fsanitize=thread'; export LDFLAGS='-fsanitize=thread'; fi + - if [ "$BUILD_TYPE" = "ubsan" ]; then export export CFLAGS='-fsanitize=undefined'; export LDFLAGS='-fsanitize=undefined'; fi install: - if [[ "$CROSS_COMPILE" == 1 ]] ; then if [[ "$ARM_ARCH_DIR" == "aarch64-linux-gnu" ]] ; then @@ -55,13 +61,6 @@ install: - if [ "$CROSS_COMPILE" = "1" ] && [ "$ARM_ARCH_DIR" = "aarch64-linux-gnu" ]; then sed -i 's+/usr/bin/ld+$HOME/linker_bin/ld+g' Makefile; fi - make - make check TESTS= -before_script: - - if [ "$BUILD_TYPE" = "asan" ]; then export CFLAGS='-fsanitize=address'; export LDFLAGS='-fsanitize=address'; fi - - if [ "$BUILD_TYPE" = "asanpc" ]; then export CFLAGS='-fsanitize=address -fsanitize=pointer-compare'; export LDFLAGS='-fsanitize=address -fsanitize=pointer-compare'; fi - - if [ "$BUILD_TYPE" = "asanps" ]; then export CFLAGS='-fsanitize=address -fsanitize=pointer-subtract'; export LDFLAGS='-fsanitize=address -fsanitize=pointer-subtract'; fi - - if [ "$BUILD_TYPE" = "lsan" ]; then export CFLAGS='-fsanitize=leak'; export LDFLAGS='-fsanitize=leak'; fi - - if [ "$BUILD_TYPE" = "tsan" ]; then export CFLAGS='-fsanitize=thread'; export LDFLAGS='-fsanitize=thread'; fi - - if [ "$BUILD_TYPE" = "ubsan" ]; then export export CFLAGS='-fsanitize=undefined'; export LDFLAGS='-fsanitize=undefined'; fi script: - if [[ "$CROSS_COMPILE" == 1 ]]; then cd test ; From c4bef484ffae229f033e14ac1bfdb4d49c0b2509 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 6 Jan 2019 04:13:29 +0000 Subject: [PATCH 342/623] Use g++7 for memory analysis and enfore debug flags --- .travis.yml | 42 +++++++++++++++++++++++++++--------------- 1 file changed, 27 insertions(+), 15 deletions(-) diff --git a/.travis.yml b/.travis.yml index f5e351b7..772733d1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -103,45 +103,57 @@ matrix: sources: - ubuntu-toolchain-r-test packages: - - g++-5 - env: - - MATRIX_EVAL="CC=gcc-5 && CXX=g++-5" + - g++-7 + env: MATRIX_EVAL="BUILD_TYPE=asan CC=gcc-7 && CXX=g++-7 DEBUG=debug" - os: linux addons: apt: sources: - ubuntu-toolchain-r-test - env: MATRIX_EVAL="BUILD_TYPE=asan" + packages: + - g++-7 + env: MATRIX_EVAL="BUILD_TYPE=asanpc CC=gcc-7 && CXX=g++-7 DEBUG=debug" - os: linux addons: apt: sources: - ubuntu-toolchain-r-test - env: MATRIX_EVAL="BUILD_TYPE=asanpc" + packages: + - g++-7 + env: MATRIX_EVAL="BUILD_TYPE=asanps CC=gcc-7 && CXX=g++-7 DEBUG=debug" - os: linux addons: apt: sources: - ubuntu-toolchain-r-test - env: MATRIX_EVAL="BUILD_TYPE=asanps" + packages: + - g++-7 + env: MATRIX_EVAL="BUILD_TYPE=lsan CC=gcc-7 && CXX=g++-7 DEBUG=debug" - os: linux addons: apt: sources: - ubuntu-toolchain-r-test - env: MATRIX_EVAL="BUILD_TYPE=lsan" + packages: + - g++-7 + env: MATRIX_EVAL="BUILD_TYPE=tsan CC=gcc-7 && CXX=g++-7 DEBUG=debug" - os: linux addons: apt: sources: - ubuntu-toolchain-r-test - env: MATRIX_EVAL="BUILD_TYPE=tsan" + packages: + - g++-7 + env: MATRIX_EVAL="BUILD_TYPE=ubsan CC=gcc-7 && CXX=g++-7 DEBUG=debug" - os: linux addons: apt: sources: - ubuntu-toolchain-r-test - env: MATRIX_EVAL="BUILD_TYPE=ubsan" + packages: + - g++-5 + env: + - MATRIX_EVAL="CC=gcc-5 && CXX=g++-5" - os: linux addons: apt: @@ -271,9 +283,9 @@ matrix: allow_failures: - env: MATRIX_EVAL="CROSS_COMPILE=1 && ARM_ARCH_DIR=arm-linux-gnueabi && CC=arm-linux-gnueabi-gcc" - env: MATRIX_EVAL="CROSS_COMPILE=1 && ARM_LD_PATH=$HOME/linker_bin && ARM_ARCH_DIR=aarch64-linux-gnu" - - env: MATRIX_EVAL="BUILD_TYPE=asan" - - env: MATRIX_EVAL="BUILD_TYPE=asanpc" - - env: MATRIX_EVAL="BUILD_TYPE=asanps" - - env: MATRIX_EVAL="BUILD_TYPE=lsan" - - env: MATRIX_EVAL="BUILD_TYPE=tsan" - - env: MATRIX_EVAL="BUILD_TYPE=ubsan" + - env: MATRIX_EVAL="BUILD_TYPE=asan CC=gcc-7 && CXX=g++-7 DEBUG=debug" + - env: MATRIX_EVAL="BUILD_TYPE=asanpc CC=gcc-7 && CXX=g++-7 DEBUG=debug" + - env: MATRIX_EVAL="BUILD_TYPE=asanps CC=gcc-7 && CXX=g++-7 DEBUG=debug" + - env: MATRIX_EVAL="BUILD_TYPE=lsan CC=gcc-7 && CXX=g++-7 DEBUG=debug" + - env: MATRIX_EVAL="BUILD_TYPE=tsan CC=gcc-7 && CXX=g++-7 DEBUG=debug" + - env: MATRIX_EVAL="BUILD_TYPE=ubsan CC=gcc-7 && CXX=g++-7 DEBUG=debug" From 099354c4d4bfaeb85087a4bd0007cd7336541718 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 6 Jan 2019 04:13:57 +0000 Subject: [PATCH 343/623] Avoid double instantiation --- src/webserver.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/webserver.cpp b/src/webserver.cpp index 93940c06..36ac00cf 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -966,7 +966,6 @@ int webserver::answer_to_connection(void* cls, MHD_Connection* connection, ); } - mr->standardized_url = new string(); internal_unescaper((void*) static_cast(cls), (char*) url); mr->standardized_url = new string(http_utils::standardize_url(url)); From fe0b828570e7aab399daaad71ff2bcb4f7b3c654 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 6 Jan 2019 07:34:06 +0000 Subject: [PATCH 344/623] Avoid leaks resuling from ASAN tests --- src/details/http_endpoint.cpp | 4 + src/http_utils.cpp | 14 ++-- src/httpserver/create_webserver.hpp | 12 +-- src/httpserver/http_resource.hpp | 2 + src/httpserver/http_utils.hpp | 2 +- test/integ/authentication.cpp | 16 ++-- test/integ/ban_system.cpp | 30 +++----- test/integ/basic.cpp | 114 ++++++++++++++-------------- test/integ/deferred.cpp | 4 +- test/integ/threaded.cpp | 4 +- test/integ/ws_start_stop.cpp | 68 ++++++++--------- test/unit/http_endpoint_test.cpp | 2 + test/unit/http_utils_test.cpp | 8 +- 13 files changed, 138 insertions(+), 142 deletions(-) diff --git a/src/details/http_endpoint.cpp b/src/details/http_endpoint.cpp index 1b70f0be..6c2ae1e0 100644 --- a/src/details/http_endpoint.cpp +++ b/src/details/http_endpoint.cpp @@ -147,9 +147,13 @@ http_endpoint& http_endpoint::operator =(const http_endpoint& h) this->family_url = h.family_url; this->reg_compiled = h.reg_compiled; if(this->reg_compiled) + { + regfree(&(this->re_url_normalized)); + regcomp(&(this->re_url_normalized), url_normalized.c_str(), REG_EXTENDED|REG_ICASE|REG_NOSUB ); + } this->url_pars = h.url_pars; this->url_pieces = h.url_pieces; this->chunk_positions = h.chunk_positions; diff --git a/src/http_utils.cpp b/src/http_utils.cpp index bfa7510c..e78fe188 100644 --- a/src/http_utils.cpp +++ b/src/http_utils.cpp @@ -539,20 +539,18 @@ bool ip_representation::operator <(const ip_representation& b) const return this_score < b_score; } -char* load_file (const char *filename) +const std::string load_file (const std::string& filename) { ifstream fp(filename, ios::in | ios::binary | ios::ate); if(fp.is_open()) { - fp.seekg(0, fp.end); - int size = fp.tellg(); - fp.seekg(0, fp.beg); + std::string content; - char* content = new char[size + 1]; - fp.read(content, size); - fp.close(); + fp.seekg(0, std::ios::end); + content.reserve(fp.tellg()); + fp.seekg(0, std::ios::beg); - content[size] = '\0'; + content.assign((std::istreambuf_iterator(fp)), std::istreambuf_iterator()); return content; } else diff --git a/src/httpserver/create_webserver.hpp b/src/httpserver/create_webserver.hpp index 88458fa3..c6c48f79 100644 --- a/src/httpserver/create_webserver.hpp +++ b/src/httpserver/create_webserver.hpp @@ -197,23 +197,17 @@ class create_webserver create_webserver& no_pedantic() { _pedantic = false; return *this; } create_webserver& https_mem_key(const std::string& https_mem_key) { - char* _https_mem_key_pt = http::load_file(https_mem_key.c_str()); - _https_mem_key = _https_mem_key_pt; - delete _https_mem_key_pt; + _https_mem_key = http::load_file(https_mem_key); return *this; } create_webserver& https_mem_cert(const std::string& https_mem_cert) { - char* _https_mem_cert_pt = http::load_file(https_mem_cert.c_str()); - _https_mem_cert = _https_mem_cert_pt; - delete _https_mem_cert_pt; + _https_mem_cert = http::load_file(https_mem_cert); return *this; } create_webserver& https_mem_trust(const std::string& https_mem_trust) { - char* _https_mem_trust_pt = http::load_file(https_mem_trust.c_str()); - _https_mem_trust = _https_mem_trust_pt; - delete _https_mem_trust_pt; + _https_mem_trust = http::load_file(https_mem_trust); return *this; } create_webserver& raw_https_mem_key(const std::string& https_mem_key) diff --git a/src/httpserver/http_resource.hpp b/src/httpserver/http_resource.hpp index 7cbff849..90671d18 100644 --- a/src/httpserver/http_resource.hpp +++ b/src/httpserver/http_resource.hpp @@ -60,6 +60,7 @@ class http_resource **/ virtual ~http_resource() { + allowed_methods.clear(); } /** @@ -204,6 +205,7 @@ class http_resource { resource_init(allowed_methods); } + /** * Copy constructor **/ diff --git a/src/httpserver/http_utils.hpp b/src/httpserver/http_utils.hpp index ba0dd357..e2896d19 100644 --- a/src/httpserver/http_utils.hpp +++ b/src/httpserver/http_utils.hpp @@ -354,7 +354,7 @@ void dump_arg_map(std::ostream &os, const std::string &prefix, */ size_t http_unescape (char *val); -char* load_file (const char *filename); +const std::string load_file (const std::string& filename); }; }; diff --git a/test/integ/authentication.cpp b/test/integ/authentication.cpp index 1241212e..f278c916 100644 --- a/test/integ/authentication.cpp +++ b/test/integ/authentication.cpp @@ -91,8 +91,8 @@ LT_END_SUITE(authentication_suite) LT_BEGIN_AUTO_TEST(authentication_suite, base_auth) webserver ws = create_webserver(8080); - user_pass_resource* user_pass = new user_pass_resource(); - ws.register_resource("base", user_pass); + user_pass_resource user_pass; + ws.register_resource("base", &user_pass); ws.start(false); curl_global_init(CURL_GLOBAL_ALL); @@ -116,8 +116,8 @@ LT_END_AUTO_TEST(base_auth) LT_BEGIN_AUTO_TEST(authentication_suite, base_auth_fail) webserver ws = create_webserver(8080); - user_pass_resource* user_pass = new user_pass_resource(); - ws.register_resource("base", user_pass); + user_pass_resource user_pass; + ws.register_resource("base", &user_pass); ws.start(false); curl_global_init(CURL_GLOBAL_ALL); @@ -143,8 +143,8 @@ LT_BEGIN_AUTO_TEST(authentication_suite, digest_auth) .digest_auth_random("myrandom") .nonce_nc_size(300); - digest_resource* digest = new digest_resource(); - ws.register_resource("base", digest); + digest_resource digest; + ws.register_resource("base", &digest); ws.start(false); curl_global_init(CURL_GLOBAL_ALL); @@ -174,8 +174,8 @@ LT_BEGIN_AUTO_TEST(authentication_suite, digest_auth_wrong_pass) .digest_auth_random("myrandom") .nonce_nc_size(300); - digest_resource* digest = new digest_resource(); - ws.register_resource("base", digest); + digest_resource digest; + ws.register_resource("base", &digest); ws.start(false); curl_global_init(CURL_GLOBAL_ALL); diff --git a/test/integ/ban_system.cpp b/test/integ/ban_system.cpp index f9192a82..3deda0bb 100644 --- a/test/integ/ban_system.cpp +++ b/test/integ/ban_system.cpp @@ -58,11 +58,12 @@ LT_BEGIN_AUTO_TEST(ban_system_suite, accept_default_ban_blocks) webserver ws = create_webserver(8080).default_policy(http_utils::ACCEPT); ws.start(false); - ok_resource* resource = new ok_resource(); - ws.register_resource("base", resource); + ok_resource resource; + ws.register_resource("base", &resource); - { curl_global_init(CURL_GLOBAL_ALL); + + { std::string s; CURL *curl = curl_easy_init(); CURLcode res; @@ -79,14 +80,10 @@ LT_BEGIN_AUTO_TEST(ban_system_suite, accept_default_ban_blocks) { ws.ban_ip("127.0.0.1"); - curl_global_init(CURL_GLOBAL_ALL); - std::string s; CURL *curl = curl_easy_init(); CURLcode res; curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); res = curl_easy_perform(curl); LT_ASSERT_NEQ(res, 0); curl_easy_cleanup(curl); @@ -95,7 +92,6 @@ LT_BEGIN_AUTO_TEST(ban_system_suite, accept_default_ban_blocks) { ws.unban_ip("127.0.0.1"); - curl_global_init(CURL_GLOBAL_ALL); std::string s; CURL *curl = curl_easy_init(); CURLcode res; @@ -109,6 +105,7 @@ LT_BEGIN_AUTO_TEST(ban_system_suite, accept_default_ban_blocks) curl_easy_cleanup(curl); } + curl_global_cleanup(); ws.stop(); LT_END_AUTO_TEST(accept_default_ban_blocks) @@ -116,18 +113,16 @@ LT_BEGIN_AUTO_TEST(ban_system_suite, reject_default_allow_passes) webserver ws = create_webserver(8080).default_policy(http_utils::REJECT); ws.start(false); - ok_resource* resource = new ok_resource(); - ws.register_resource("base", resource); + ok_resource resource; + ws.register_resource("base", &resource); - { curl_global_init(CURL_GLOBAL_ALL); - std::string s; + + { CURL *curl = curl_easy_init(); CURLcode res; curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); res = curl_easy_perform(curl); LT_ASSERT_NEQ(res, 0); curl_easy_cleanup(curl); @@ -136,7 +131,6 @@ LT_BEGIN_AUTO_TEST(ban_system_suite, reject_default_allow_passes) { ws.allow_ip("127.0.0.1"); - curl_global_init(CURL_GLOBAL_ALL); std::string s; CURL *curl = curl_easy_init(); CURLcode res; @@ -147,24 +141,22 @@ LT_BEGIN_AUTO_TEST(ban_system_suite, reject_default_allow_passes) res = curl_easy_perform(curl); LT_ASSERT_EQ(res, 0); LT_CHECK_EQ(s, "OK"); + curl_easy_cleanup(curl); } { ws.disallow_ip("127.0.0.1"); - curl_global_init(CURL_GLOBAL_ALL); - std::string s; CURL *curl = curl_easy_init(); CURLcode res; curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); res = curl_easy_perform(curl); LT_ASSERT_NEQ(res, 0); curl_easy_cleanup(curl); } + curl_global_cleanup(); ws.stop(); LT_END_AUTO_TEST(reject_default_allow_passes) diff --git a/test/integ/basic.cpp b/test/integ/basic.cpp index e4965453..df9eec9b 100644 --- a/test/integ/basic.cpp +++ b/test/integ/basic.cpp @@ -270,10 +270,10 @@ LT_BEGIN_AUTO_TEST(basic_suite, server_runs) LT_END_AUTO_TEST(server_runs) LT_BEGIN_AUTO_TEST(basic_suite, two_endpoints) - ok_resource* ok = new ok_resource(); - ws->register_resource("OK", ok); - nok_resource* nok = new nok_resource(); - ws->register_resource("NOK", nok); + ok_resource ok; + ws->register_resource("OK", &ok); + nok_resource nok; + ws->register_resource("NOK", &nok); curl_global_init(CURL_GLOBAL_ALL); std::string s; @@ -306,8 +306,8 @@ LT_BEGIN_AUTO_TEST(basic_suite, two_endpoints) LT_END_AUTO_TEST(two_endpoints) LT_BEGIN_AUTO_TEST(basic_suite, read_body) - simple_resource* resource = new simple_resource(); - ws->register_resource("base", resource); + simple_resource resource; + ws->register_resource("base", &resource); curl_global_init(CURL_GLOBAL_ALL); std::string s; CURL *curl = curl_easy_init(); @@ -323,8 +323,8 @@ LT_BEGIN_AUTO_TEST(basic_suite, read_body) LT_END_AUTO_TEST(read_body) LT_BEGIN_AUTO_TEST(basic_suite, read_long_body) - long_content_resource* resource = new long_content_resource(); - ws->register_resource("base", resource); + long_content_resource resource; + ws->register_resource("base", &resource); curl_global_init(CURL_GLOBAL_ALL); std::string s; CURL *curl = curl_easy_init(); @@ -340,8 +340,8 @@ LT_BEGIN_AUTO_TEST(basic_suite, read_long_body) LT_END_AUTO_TEST(read_long_body) LT_BEGIN_AUTO_TEST(basic_suite, resource_setting_header) - header_set_test_resource* resource = new header_set_test_resource(); - ws->register_resource("base", resource); + header_set_test_resource resource; + ws->register_resource("base", &resource); curl_global_init(CURL_GLOBAL_ALL); std::string s; map ss; @@ -361,8 +361,8 @@ LT_BEGIN_AUTO_TEST(basic_suite, resource_setting_header) LT_END_AUTO_TEST(resource_setting_header) LT_BEGIN_AUTO_TEST(basic_suite, resource_setting_cookie) - cookie_set_test_resource* resource = new cookie_set_test_resource(); - ws->register_resource("base", resource); + cookie_set_test_resource resource; + ws->register_resource("base", &resource); curl_global_init(CURL_GLOBAL_ALL); std::string s; CURL *curl = curl_easy_init(); @@ -382,15 +382,9 @@ LT_BEGIN_AUTO_TEST(basic_suite, resource_setting_cookie) LT_CHECK_EQ(s, "OK"); std::string read_cookie = ""; - if(!res && cookies) - { - read_cookie = cookies->data; - curl_slist_free_all(cookies); - } - else - { - LT_FAIL("No cookie being set"); - } + read_cookie = cookies->data; + curl_slist_free_all(cookies); + std::vector cookie_parts = string_utilities::string_split(read_cookie, '\t', false); LT_CHECK_EQ(cookie_parts[5], "MyCookie"); LT_CHECK_EQ(cookie_parts[6], "CookieValue"); @@ -399,8 +393,8 @@ LT_BEGIN_AUTO_TEST(basic_suite, resource_setting_cookie) LT_END_AUTO_TEST(resource_setting_cookie) LT_BEGIN_AUTO_TEST(basic_suite, request_with_header) - header_reading_resource* resource = new header_reading_resource(); - ws->register_resource("base", resource); + header_reading_resource resource; + ws->register_resource("base", &resource); curl_global_init(CURL_GLOBAL_ALL); std::string s; CURL *curl = curl_easy_init(); @@ -422,8 +416,8 @@ LT_BEGIN_AUTO_TEST(basic_suite, request_with_header) LT_END_AUTO_TEST(request_with_header) LT_BEGIN_AUTO_TEST(basic_suite, request_with_cookie) - cookie_reading_resource* resource = new cookie_reading_resource(); - ws->register_resource("base", resource); + cookie_reading_resource resource; + ws->register_resource("base", &resource); curl_global_init(CURL_GLOBAL_ALL); std::string s; CURL *curl = curl_easy_init(); @@ -440,8 +434,8 @@ LT_BEGIN_AUTO_TEST(basic_suite, request_with_cookie) LT_END_AUTO_TEST(request_with_cookie) LT_BEGIN_AUTO_TEST(basic_suite, complete) - complete_test_resource* resource = new complete_test_resource(); - ws->register_resource("base", resource); + complete_test_resource resource; + ws->register_resource("base", &resource); curl_global_init(CURL_GLOBAL_ALL); { @@ -495,8 +489,8 @@ LT_BEGIN_AUTO_TEST(basic_suite, complete) LT_END_AUTO_TEST(complete) LT_BEGIN_AUTO_TEST(basic_suite, only_render) - only_render_resource* resource = new only_render_resource(); - ws->register_resource("base", resource); + only_render_resource resource; + ws->register_resource("base", &resource); curl_global_init(CURL_GLOBAL_ALL); std::string s; CURL* curl; @@ -574,8 +568,8 @@ LT_BEGIN_AUTO_TEST(basic_suite, only_render) LT_END_AUTO_TEST(only_render) LT_BEGIN_AUTO_TEST(basic_suite, postprocessor) - simple_resource* resource = new simple_resource(); - ws->register_resource("base", resource); + simple_resource resource; + ws->register_resource("base", &resource); curl_global_init(CURL_GLOBAL_ALL); std::string s; CURL *curl = curl_easy_init(); @@ -591,8 +585,8 @@ LT_BEGIN_AUTO_TEST(basic_suite, postprocessor) LT_END_AUTO_TEST(postprocessor) LT_BEGIN_AUTO_TEST(basic_suite, empty_arg) - simple_resource* resource = new simple_resource(); - ws->register_resource("base", resource); + simple_resource resource; + ws->register_resource("base", &resource); curl_global_init(CURL_GLOBAL_ALL); CURL *curl = curl_easy_init(); CURLcode res; @@ -605,8 +599,8 @@ LT_BEGIN_AUTO_TEST(basic_suite, empty_arg) LT_END_AUTO_TEST(empty_arg) LT_BEGIN_AUTO_TEST(basic_suite, no_response) - no_response_resource* resource = new no_response_resource(); - ws->register_resource("base", resource); + no_response_resource resource; + ws->register_resource("base", &resource); curl_global_init(CURL_GLOBAL_ALL); CURL* curl = curl_easy_init(); @@ -621,8 +615,8 @@ LT_BEGIN_AUTO_TEST(basic_suite, no_response) LT_END_AUTO_TEST(no_response) LT_BEGIN_AUTO_TEST(basic_suite, regex_matching) - simple_resource* resource = new simple_resource(); - ws->register_resource("regex/matching/number/[0-9]+", resource); + simple_resource resource; + ws->register_resource("regex/matching/number/[0-9]+", &resource); curl_global_init(CURL_GLOBAL_ALL); std::string s; @@ -639,8 +633,8 @@ LT_BEGIN_AUTO_TEST(basic_suite, regex_matching) LT_END_AUTO_TEST(regex_matching) LT_BEGIN_AUTO_TEST(basic_suite, regex_matching_arg) - args_resource* resource = new args_resource(); - ws->register_resource("this/captures/{arg}/passed/in/input", resource); + args_resource resource; + ws->register_resource("this/captures/{arg}/passed/in/input", &resource); curl_global_init(CURL_GLOBAL_ALL); std::string s; @@ -657,8 +651,8 @@ LT_BEGIN_AUTO_TEST(basic_suite, regex_matching_arg) LT_END_AUTO_TEST(regex_matching_arg) LT_BEGIN_AUTO_TEST(basic_suite, regex_matching_arg_custom) - args_resource* resource = new args_resource(); - ws->register_resource("this/captures/numeric/{arg|([0-9]+)}/passed/in/input", resource); + args_resource resource; + ws->register_resource("this/captures/numeric/{arg|([0-9]+)}/passed/in/input", &resource); curl_global_init(CURL_GLOBAL_ALL); { @@ -694,8 +688,8 @@ LT_BEGIN_AUTO_TEST(basic_suite, regex_matching_arg_custom) LT_END_AUTO_TEST(regex_matching_arg_custom) LT_BEGIN_AUTO_TEST(basic_suite, querystring_processing) - args_resource* resource = new args_resource(); - ws->register_resource("this/captures/args/passed/in/the/querystring", resource); + args_resource resource; + ws->register_resource("this/captures/args/passed/in/the/querystring", &resource); curl_global_init(CURL_GLOBAL_ALL); std::string s; @@ -712,8 +706,8 @@ LT_BEGIN_AUTO_TEST(basic_suite, querystring_processing) LT_END_AUTO_TEST(querystring_processing) LT_BEGIN_AUTO_TEST(basic_suite, register_unregister) - simple_resource* resource = new simple_resource(); - ws->register_resource("base", resource); + simple_resource resource; + ws->register_resource("base", &resource); curl_global_init(CURL_GLOBAL_ALL); { @@ -751,7 +745,7 @@ LT_BEGIN_AUTO_TEST(basic_suite, register_unregister) curl_easy_cleanup(curl); } - ws->register_resource("base", resource); + ws->register_resource("base", &resource); { std::string s; CURL *curl = curl_easy_init(); @@ -768,8 +762,8 @@ LT_BEGIN_AUTO_TEST(basic_suite, register_unregister) LT_END_AUTO_TEST(register_unregister) LT_BEGIN_AUTO_TEST(basic_suite, file_serving_resource) - file_response_resource* resource = new file_response_resource(); - ws->register_resource("base", resource); + file_response_resource resource; + ws->register_resource("base", &resource); curl_global_init(CURL_GLOBAL_ALL); std::string s; @@ -786,8 +780,8 @@ LT_BEGIN_AUTO_TEST(basic_suite, file_serving_resource) LT_END_AUTO_TEST(file_serving_resource) LT_BEGIN_AUTO_TEST(basic_suite, exception_forces_500) - exception_resource* resource = new exception_resource(); - ws->register_resource("base", resource); + exception_resource resource; + ws->register_resource("base", &resource); curl_global_init(CURL_GLOBAL_ALL); std::string s; @@ -809,8 +803,8 @@ LT_BEGIN_AUTO_TEST(basic_suite, exception_forces_500) LT_END_AUTO_TEST(exception_forces_500) LT_BEGIN_AUTO_TEST(basic_suite, untyped_error_forces_500) - error_resource* resource = new error_resource(); - ws->register_resource("base", resource); + error_resource resource; + ws->register_resource("base", &resource); curl_global_init(CURL_GLOBAL_ALL); std::string s; @@ -833,8 +827,8 @@ LT_END_AUTO_TEST(untyped_error_forces_500) LT_BEGIN_AUTO_TEST(basic_suite, request_is_printable) std::stringstream ss; - print_request_resource* resource = new print_request_resource(&ss); - ws->register_resource("base", resource); + print_request_resource resource(&ss); + ws->register_resource("base", &resource); curl_global_init(CURL_GLOBAL_ALL); std::string s; @@ -846,10 +840,12 @@ LT_BEGIN_AUTO_TEST(basic_suite, request_is_printable) curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); struct curl_slist *list = NULL; - list = curl_slist_append(list, "MyHeader: MyValue"); + list = curl_slist_append(NULL, "MyHeader: MyValue"); curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list); res = curl_easy_perform(curl); + curl_slist_free_all(list); + LT_ASSERT_EQ(res, 0); LT_CHECK_EQ(s, "OK"); @@ -866,8 +862,8 @@ LT_END_AUTO_TEST(request_is_printable) LT_BEGIN_AUTO_TEST(basic_suite, response_is_printable) std::stringstream ss; - print_response_resource* resource = new print_response_resource(&ss); - ws->register_resource("base", resource); + print_response_resource resource(&ss); + ws->register_resource("base", &resource); curl_global_init(CURL_GLOBAL_ALL); std::string s; @@ -879,10 +875,12 @@ LT_BEGIN_AUTO_TEST(basic_suite, response_is_printable) curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); struct curl_slist *list = NULL; - list = curl_slist_append(list, "MyHeader: MyValue"); + list = curl_slist_append(NULL, "MyHeader: MyValue"); curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list); res = curl_easy_perform(curl); + curl_slist_free_all(list); + LT_ASSERT_EQ(res, 0); LT_CHECK_EQ(s, "OK"); diff --git a/test/integ/deferred.cpp b/test/integ/deferred.cpp index e7c91c7b..3e68c8ee 100644 --- a/test/integ/deferred.cpp +++ b/test/integ/deferred.cpp @@ -88,8 +88,8 @@ LT_BEGIN_SUITE(deferred_suite) LT_END_SUITE(deferred_suite) LT_BEGIN_AUTO_TEST(deferred_suite, deferred_response) - deferred_resource* resource = new deferred_resource(); - ws->register_resource("base", resource); + deferred_resource resource; + ws->register_resource("base", &resource); curl_global_init(CURL_GLOBAL_ALL); std::string s; diff --git a/test/integ/threaded.cpp b/test/integ/threaded.cpp index ad03b721..62f7d973 100644 --- a/test/integ/threaded.cpp +++ b/test/integ/threaded.cpp @@ -54,8 +54,8 @@ LT_BEGIN_SUITE(threaded_suite) LT_END_SUITE(threaded_suite) LT_BEGIN_AUTO_TEST(threaded_suite, base) - ok_resource* resource = new ok_resource(); - ws->register_resource("base", resource); + ok_resource resource; + ws->register_resource("base", &resource); curl_global_init(CURL_GLOBAL_ALL); std::string s; CURL* curl; diff --git a/test/integ/ws_start_stop.cpp b/test/integ/ws_start_stop.cpp index db4f9971..25d085f1 100644 --- a/test/integ/ws_start_stop.cpp +++ b/test/integ/ws_start_stop.cpp @@ -76,8 +76,8 @@ LT_END_SUITE(ws_start_stop_suite) LT_BEGIN_AUTO_TEST(ws_start_stop_suite, start_stop) { webserver ws = create_webserver(8080); - ok_resource* ok = new ok_resource(); - ws.register_resource("base", ok); + ok_resource ok; + ws.register_resource("base", &ok); ws.start(false); curl_global_init(CURL_GLOBAL_ALL); @@ -98,8 +98,8 @@ LT_BEGIN_AUTO_TEST(ws_start_stop_suite, start_stop) { webserver ws = create_webserver(8080).start_method(http::http_utils::INTERNAL_SELECT); - ok_resource* ok = new ok_resource(); - ws.register_resource("base", ok); + ok_resource ok; + ws.register_resource("base", &ok); ws.start(false); curl_global_init(CURL_GLOBAL_ALL); @@ -120,8 +120,8 @@ LT_BEGIN_AUTO_TEST(ws_start_stop_suite, start_stop) { webserver ws = create_webserver(8080).start_method(http::http_utils::THREAD_PER_CONNECTION); - ok_resource* ok = new ok_resource(); - ws.register_resource("base", ok); + ok_resource ok; + ws.register_resource("base", &ok); ws.start(false); curl_global_init(CURL_GLOBAL_ALL); @@ -144,8 +144,8 @@ LT_END_AUTO_TEST(start_stop) LT_BEGIN_AUTO_TEST(ws_start_stop_suite, sweet_kill) webserver ws = create_webserver(8080); - ok_resource* ok = new ok_resource(); - ws.register_resource("base", ok); + ok_resource ok; + ws.register_resource("base", &ok); ws.start(false); { @@ -192,8 +192,8 @@ LT_BEGIN_AUTO_TEST(ws_start_stop_suite, disable_options) .no_regex_checking() .no_ban_system() .no_post_process(); - ok_resource* ok = new ok_resource(); - ws.register_resource("base", ok); + ok_resource ok; + ws.register_resource("base", &ok); ws.start(false); curl_global_init(CURL_GLOBAL_ALL); @@ -220,8 +220,8 @@ LT_BEGIN_AUTO_TEST(ws_start_stop_suite, enable_options) .regex_checking() .ban_system() .post_process(); - ok_resource* ok = new ok_resource(); - ws.register_resource("base", ok); + ok_resource ok; + ws.register_resource("base", &ok); ws.start(false); curl_global_init(CURL_GLOBAL_ALL); @@ -251,8 +251,8 @@ LT_BEGIN_AUTO_TEST(ws_start_stop_suite, custom_socket) listen(fd, 10000); webserver ws = create_webserver(-1).bind_socket(fd); //whatever port here doesn't matter - ok_resource* ok = new ok_resource(); - ws.register_resource("base", ok); + ok_resource ok; + ws.register_resource("base", &ok); ws.start(false); curl_global_init(CURL_GLOBAL_ALL); @@ -273,8 +273,8 @@ LT_END_AUTO_TEST(custom_socket) LT_BEGIN_AUTO_TEST(ws_start_stop_suite, single_resource) webserver ws = create_webserver(8080).single_resource(); - ok_resource* ok = new ok_resource(); - ws.register_resource("/", ok, true); + ok_resource ok; + ws.register_resource("/", &ok, true); ws.start(false); curl_global_init(CURL_GLOBAL_ALL); @@ -295,9 +295,9 @@ LT_END_AUTO_TEST(single_resource) LT_BEGIN_AUTO_TEST(ws_start_stop_suite, single_resource_not_default_resource) webserver ws = create_webserver(8080).single_resource(); - ok_resource* ok = new ok_resource(); - LT_CHECK_THROW(ws.register_resource("/other", ok, true)); - LT_CHECK_THROW(ws.register_resource("/", ok, false)); + ok_resource ok; + LT_CHECK_THROW(ws.register_resource("/other", &ok, true)); + LT_CHECK_THROW(ws.register_resource("/", &ok, false)); ws.start(false); ws.stop(); @@ -331,8 +331,8 @@ LT_BEGIN_AUTO_TEST(ws_start_stop_suite, tuning_options) .nonce_nc_size(10); ; - ok_resource* ok = new ok_resource(); - ws.register_resource("base", ok); + ok_resource ok; + ws.register_resource("base", &ok); LT_CHECK_NOTHROW(ws.start(false)); curl_global_init(CURL_GLOBAL_ALL); @@ -357,8 +357,8 @@ LT_BEGIN_AUTO_TEST(ws_start_stop_suite, ssl_base) .https_mem_key("key.pem") .https_mem_cert("cert.pem"); - ok_resource* ok = new ok_resource(); - ws.register_resource("base", ok); + ok_resource ok; + ws.register_resource("base", &ok); ws.start(false); curl_global_init(CURL_GLOBAL_ALL); @@ -386,8 +386,8 @@ LT_BEGIN_AUTO_TEST(ws_start_stop_suite, ssl_with_protocol_priorities) .https_mem_cert("cert.pem") .https_priorities("NONE:+VERS-TLS1.0:+AES-128-CBC:+SHA1:+RSA:+COMP-NULL"); - ok_resource* ok = new ok_resource(); - ws.register_resource("base", ok); + ok_resource ok; + ws.register_resource("base", &ok); ws.start(false); curl_global_init(CURL_GLOBAL_ALL); @@ -415,8 +415,8 @@ LT_BEGIN_AUTO_TEST(ws_start_stop_suite, ssl_with_trust) .https_mem_cert("cert.pem") .https_mem_trust("test_root_ca.pem"); - ok_resource* ok = new ok_resource(); - ws.register_resource("base", ok); + ok_resource ok; + ws.register_resource("base", &ok); ws.start(false); curl_global_init(CURL_GLOBAL_ALL); @@ -440,8 +440,8 @@ LT_END_AUTO_TEST(ssl_with_trust) void* start_ws_blocking(void* par) { webserver* ws = (webserver*) par; - ok_resource* ok = new ok_resource(); - ws->register_resource("base", ok); + ok_resource ok; + ws->register_resource("base", &ok); ws->start(true); return 0x0; @@ -453,12 +453,12 @@ LT_BEGIN_AUTO_TEST(ws_start_stop_suite, blocking_server) pthread_t tid; pthread_create(&tid, NULL, start_ws_blocking, (void *) &ws); + sleep(1); + curl_global_init(CURL_GLOBAL_ALL); std::string s; CURL *curl = curl_easy_init(); CURLcode res; - curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); // avoid verifying ssl - curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); // avoid verifying ssl curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); @@ -480,8 +480,8 @@ LT_BEGIN_AUTO_TEST(ws_start_stop_suite, custom_error_resources) .not_found_resource(not_found_custom) .method_not_allowed_resource(not_allowed_custom); - ok_resource* ok = new ok_resource(); - ws.register_resource("base", ok); + ok_resource ok; + ws.register_resource("base", &ok); ws.start(false); { @@ -520,7 +520,7 @@ LT_BEGIN_AUTO_TEST(ws_start_stop_suite, custom_error_resources) } { - ok->set_allowing("PUT", false); + ok.set_allowing("PUT", false); curl_global_init(CURL_GLOBAL_ALL); std::string s; diff --git a/test/unit/http_endpoint_test.cpp b/test/unit/http_endpoint_test.cpp index 8dab9e24..1075710b 100644 --- a/test/unit/http_endpoint_test.cpp +++ b/test/unit/http_endpoint_test.cpp @@ -245,7 +245,9 @@ LT_BEGIN_AUTO_TEST(http_endpoint_suite, http_endpoint_assignment) LT_CHECK_NEQ(a.get_url_complete(), b.get_url_complete()); + std::cout << "before assigning" << std::endl; b = a; + std::cout << "after assigning" << std::endl; LT_CHECK_EQ(a.get_url_complete(), b.get_url_complete()); LT_CHECK_EQ(a.get_url_normalized(), b.get_url_normalized()); diff --git a/test/unit/http_utils_test.cpp b/test/unit/http_utils_test.cpp index 470c90a5..cc81ceb8 100644 --- a/test/unit/http_utils_test.cpp +++ b/test/unit/http_utils_test.cpp @@ -56,6 +56,9 @@ LT_BEGIN_AUTO_TEST(http_utils_suite, unescape) LT_CHECK_EQ(string(string_with_plus), string(expected)); LT_CHECK_EQ(expected_size, 3); + + free(string_with_plus); + free(expected); LT_END_AUTO_TEST(unescape) LT_BEGIN_AUTO_TEST(http_utils_suite, unescape_plus) @@ -68,6 +71,9 @@ LT_BEGIN_AUTO_TEST(http_utils_suite, unescape_plus) LT_CHECK_EQ(string(string_with_plus), string(expected)); LT_CHECK_EQ(expected_size, 3); + + free(string_with_plus); + free(expected); LT_END_AUTO_TEST(unescape_plus) LT_BEGIN_AUTO_TEST(http_utils_suite, tokenize_url) @@ -496,7 +502,7 @@ LT_BEGIN_AUTO_TEST(http_utils_suite, ip_representation6_sockaddr) LT_END_AUTO_TEST(ip_representation6_sockaddr) LT_BEGIN_AUTO_TEST(http_utils_suite, load_file) - LT_CHECK_EQ(std::string(http::load_file("test_content")), "test content of file\n"); + LT_CHECK_EQ(http::load_file("test_content"), "test content of file\n"); LT_END_AUTO_TEST(load_file) LT_BEGIN_AUTO_TEST(http_utils_suite, load_file_invalid) From 201a91378a621e7b1410e712535fa88cb1381168 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 6 Jan 2019 07:43:23 +0000 Subject: [PATCH 345/623] Make code compatible with C++98 --- src/http_utils.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/http_utils.cpp b/src/http_utils.cpp index e78fe188..368c9ba8 100644 --- a/src/http_utils.cpp +++ b/src/http_utils.cpp @@ -541,14 +541,14 @@ bool ip_representation::operator <(const ip_representation& b) const const std::string load_file (const std::string& filename) { - ifstream fp(filename, ios::in | ios::binary | ios::ate); + ifstream fp(filename.c_str(), ios::in | ios::binary | ios::ate); if(fp.is_open()) { std::string content; - fp.seekg(0, std::ios::end); + fp.seekg(0, fp.end); content.reserve(fp.tellg()); - fp.seekg(0, std::ios::beg); + fp.seekg(0, fp.beg); content.assign((std::istreambuf_iterator(fp)), std::istreambuf_iterator()); return content; From 083da0c69f7a4965fc24161206cf1fca6a071091 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 6 Jan 2019 07:54:40 +0000 Subject: [PATCH 346/623] Fix travis yml script --- .travis.yml | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/.travis.yml b/.travis.yml index 772733d1..a4d2ea6b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -104,7 +104,7 @@ matrix: - ubuntu-toolchain-r-test packages: - g++-7 - env: MATRIX_EVAL="BUILD_TYPE=asan CC=gcc-7 && CXX=g++-7 DEBUG=debug" + env: MATRIX_EVAL="BUILD_TYPE=asan && CC=gcc-7 && CXX=g++-7 && DEBUG=debug" - os: linux addons: apt: @@ -112,7 +112,7 @@ matrix: - ubuntu-toolchain-r-test packages: - g++-7 - env: MATRIX_EVAL="BUILD_TYPE=asanpc CC=gcc-7 && CXX=g++-7 DEBUG=debug" + env: MATRIX_EVAL="BUILD_TYPE=asanpc && CC=gcc-7 && CXX=g++-7 && DEBUG=debug" - os: linux addons: apt: @@ -120,7 +120,7 @@ matrix: - ubuntu-toolchain-r-test packages: - g++-7 - env: MATRIX_EVAL="BUILD_TYPE=asanps CC=gcc-7 && CXX=g++-7 DEBUG=debug" + env: MATRIX_EVAL="BUILD_TYPE=asanps && CC=gcc-7 && CXX=g++-7 && DEBUG=debug" - os: linux addons: apt: @@ -128,7 +128,7 @@ matrix: - ubuntu-toolchain-r-test packages: - g++-7 - env: MATRIX_EVAL="BUILD_TYPE=lsan CC=gcc-7 && CXX=g++-7 DEBUG=debug" + env: MATRIX_EVAL="BUILD_TYPE=lsan && CC=gcc-7 && CXX=g++-7 && DEBUG=debug" - os: linux addons: apt: @@ -136,7 +136,7 @@ matrix: - ubuntu-toolchain-r-test packages: - g++-7 - env: MATRIX_EVAL="BUILD_TYPE=tsan CC=gcc-7 && CXX=g++-7 DEBUG=debug" + env: MATRIX_EVAL="BUILD_TYPE=tsan && CC=gcc-7 && CXX=g++-7 && DEBUG=debug" - os: linux addons: apt: @@ -144,7 +144,7 @@ matrix: - ubuntu-toolchain-r-test packages: - g++-7 - env: MATRIX_EVAL="BUILD_TYPE=ubsan CC=gcc-7 && CXX=g++-7 DEBUG=debug" + env: MATRIX_EVAL="BUILD_TYPE=ubsan && CC=gcc-7 && CXX=g++-7 && DEBUG=debug" - os: linux addons: apt: @@ -283,9 +283,9 @@ matrix: allow_failures: - env: MATRIX_EVAL="CROSS_COMPILE=1 && ARM_ARCH_DIR=arm-linux-gnueabi && CC=arm-linux-gnueabi-gcc" - env: MATRIX_EVAL="CROSS_COMPILE=1 && ARM_LD_PATH=$HOME/linker_bin && ARM_ARCH_DIR=aarch64-linux-gnu" - - env: MATRIX_EVAL="BUILD_TYPE=asan CC=gcc-7 && CXX=g++-7 DEBUG=debug" - - env: MATRIX_EVAL="BUILD_TYPE=asanpc CC=gcc-7 && CXX=g++-7 DEBUG=debug" - - env: MATRIX_EVAL="BUILD_TYPE=asanps CC=gcc-7 && CXX=g++-7 DEBUG=debug" - - env: MATRIX_EVAL="BUILD_TYPE=lsan CC=gcc-7 && CXX=g++-7 DEBUG=debug" - - env: MATRIX_EVAL="BUILD_TYPE=tsan CC=gcc-7 && CXX=g++-7 DEBUG=debug" - - env: MATRIX_EVAL="BUILD_TYPE=ubsan CC=gcc-7 && CXX=g++-7 DEBUG=debug" + - env: MATRIX_EVAL="BUILD_TYPE=asan && CC=gcc-7 && CXX=g++-7 && DEBUG=debug" + - env: MATRIX_EVAL="BUILD_TYPE=asanpc && CC=gcc-7 && CXX=g++-7 && DEBUG=debug" + - env: MATRIX_EVAL="BUILD_TYPE=asanps && CC=gcc-7 && CXX=g++-7 && DEBUG=debug" + - env: MATRIX_EVAL="BUILD_TYPE=lsan && CC=gcc-7 && CXX=g++-7 && DEBUG=debug" + - env: MATRIX_EVAL="BUILD_TYPE=tsan && CC=gcc-7 && CXX=g++-7 && DEBUG=debug" + - env: MATRIX_EVAL="BUILD_TYPE=ubsan && CC=gcc-7 && CXX=g++-7 && DEBUG=debug" From 4cda642671128621ca52af40f1dd9f92d1a3f8b2 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 6 Jan 2019 08:31:11 +0000 Subject: [PATCH 347/623] Install packages for ASAN --- .travis.yml | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/.travis.yml b/.travis.yml index a4d2ea6b..48a39814 100644 --- a/.travis.yml +++ b/.travis.yml @@ -104,6 +104,14 @@ matrix: - ubuntu-toolchain-r-test packages: - g++-7 + - libasan4 + - libasan4-dbg + - libtsan0 + - libtsan0-dbg + - liblsan0 + - liblsan0-dbg + - libubsan0 + - libubsan0-dbg env: MATRIX_EVAL="BUILD_TYPE=asan && CC=gcc-7 && CXX=g++-7 && DEBUG=debug" - os: linux addons: @@ -112,6 +120,14 @@ matrix: - ubuntu-toolchain-r-test packages: - g++-7 + - libasan4 + - libasan4-dbg + - libtsan0 + - libtsan0-dbg + - liblsan0 + - liblsan0-dbg + - libubsan0 + - libubsan0-dbg env: MATRIX_EVAL="BUILD_TYPE=asanpc && CC=gcc-7 && CXX=g++-7 && DEBUG=debug" - os: linux addons: @@ -120,6 +136,14 @@ matrix: - ubuntu-toolchain-r-test packages: - g++-7 + - libasan4 + - libasan4-dbg + - libtsan0 + - libtsan0-dbg + - liblsan0 + - liblsan0-dbg + - libubsan0 + - libubsan0-dbg env: MATRIX_EVAL="BUILD_TYPE=asanps && CC=gcc-7 && CXX=g++-7 && DEBUG=debug" - os: linux addons: @@ -128,6 +152,14 @@ matrix: - ubuntu-toolchain-r-test packages: - g++-7 + - libasan4 + - libasan4-dbg + - libtsan0 + - libtsan0-dbg + - liblsan0 + - liblsan0-dbg + - libubsan0 + - libubsan0-dbg env: MATRIX_EVAL="BUILD_TYPE=lsan && CC=gcc-7 && CXX=g++-7 && DEBUG=debug" - os: linux addons: @@ -136,6 +168,14 @@ matrix: - ubuntu-toolchain-r-test packages: - g++-7 + - libasan4 + - libasan4-dbg + - libtsan0 + - libtsan0-dbg + - liblsan0 + - liblsan0-dbg + - libubsan0 + - libubsan0-dbg env: MATRIX_EVAL="BUILD_TYPE=tsan && CC=gcc-7 && CXX=g++-7 && DEBUG=debug" - os: linux addons: @@ -144,6 +184,14 @@ matrix: - ubuntu-toolchain-r-test packages: - g++-7 + - libasan4 + - libasan4-dbg + - libtsan0 + - libtsan0-dbg + - liblsan0 + - liblsan0-dbg + - libubsan0 + - libubsan0-dbg env: MATRIX_EVAL="BUILD_TYPE=ubsan && CC=gcc-7 && CXX=g++-7 && DEBUG=debug" - os: linux addons: From 5e3cb6f82aa334fa48b2cfded25c21464a039abd Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 6 Jan 2019 01:01:14 -0800 Subject: [PATCH 348/623] Moved compiler checks to clang --- .travis.yml | 95 ++++++++++++----------------------------------------- 1 file changed, 21 insertions(+), 74 deletions(-) diff --git a/.travis.yml b/.travis.yml index 48a39814..bccafd97 100644 --- a/.travis.yml +++ b/.travis.yml @@ -28,8 +28,7 @@ before_install: - sudo make install - cd .. - if [ "$BUILD_TYPE" = "asan" ]; then export CFLAGS='-fsanitize=address'; export LDFLAGS='-fsanitize=address'; fi - - if [ "$BUILD_TYPE" = "asanpc" ]; then export CFLAGS='-fsanitize=address -fsanitize=pointer-compare'; export LDFLAGS='-fsanitize=address -fsanitize=pointer-compare'; fi - - if [ "$BUILD_TYPE" = "asanps" ]; then export CFLAGS='-fsanitize=address -fsanitize=pointer-subtract'; export LDFLAGS='-fsanitize=address -fsanitize=pointer-subtract'; fi + - if [ "$BUILD_TYPE" = "msan" ]; then export CFLAGS='-fsanitize=memory'; export LDFLAGS='-fsanitize=memory'; fi - if [ "$BUILD_TYPE" = "lsan" ]; then export CFLAGS='-fsanitize=leak'; export LDFLAGS='-fsanitize=leak'; fi - if [ "$BUILD_TYPE" = "tsan" ]; then export CFLAGS='-fsanitize=thread'; export LDFLAGS='-fsanitize=thread'; fi - if [ "$BUILD_TYPE" = "ubsan" ]; then export export CFLAGS='-fsanitize=undefined'; export LDFLAGS='-fsanitize=undefined'; fi @@ -102,97 +101,46 @@ matrix: apt: sources: - ubuntu-toolchain-r-test + - llvm-toolchain-precise-3.8 packages: - - g++-7 - - libasan4 - - libasan4-dbg - - libtsan0 - - libtsan0-dbg - - liblsan0 - - liblsan0-dbg - - libubsan0 - - libubsan0-dbg - env: MATRIX_EVAL="BUILD_TYPE=asan && CC=gcc-7 && CXX=g++-7 && DEBUG=debug" - - os: linux - addons: - apt: - sources: - - ubuntu-toolchain-r-test - packages: - - g++-7 - - libasan4 - - libasan4-dbg - - libtsan0 - - libtsan0-dbg - - liblsan0 - - liblsan0-dbg - - libubsan0 - - libubsan0-dbg - env: MATRIX_EVAL="BUILD_TYPE=asanpc && CC=gcc-7 && CXX=g++-7 && DEBUG=debug" + - clang-3.8 + env: MATRIX_EVAL="BUILD_TYPE=asan && CC=clang-3.8 && CXX=clang++-3.8 && DEBUG=debug" - os: linux addons: apt: sources: - ubuntu-toolchain-r-test + - llvm-toolchain-precise-3.8 packages: - - g++-7 - - libasan4 - - libasan4-dbg - - libtsan0 - - libtsan0-dbg - - liblsan0 - - liblsan0-dbg - - libubsan0 - - libubsan0-dbg - env: MATRIX_EVAL="BUILD_TYPE=asanps && CC=gcc-7 && CXX=g++-7 && DEBUG=debug" + - clang-3.8 + env: MATRIX_EVAL="BUILD_TYPE=msan && CC=clang-3.8 && CXX=clang++-3.8 && DEBUG=debug" - os: linux addons: apt: sources: - ubuntu-toolchain-r-test + - llvm-toolchain-precise-3.8 packages: - - g++-7 - - libasan4 - - libasan4-dbg - - libtsan0 - - libtsan0-dbg - - liblsan0 - - liblsan0-dbg - - libubsan0 - - libubsan0-dbg - env: MATRIX_EVAL="BUILD_TYPE=lsan && CC=gcc-7 && CXX=g++-7 && DEBUG=debug" + - clang-3.8 + env: MATRIX_EVAL="BUILD_TYPE=lsan && CC=clang-3.8 && CXX=clang++-3.8 && DEBUG=debug" - os: linux addons: apt: sources: - ubuntu-toolchain-r-test + - llvm-toolchain-precise-3.8 packages: - - g++-7 - - libasan4 - - libasan4-dbg - - libtsan0 - - libtsan0-dbg - - liblsan0 - - liblsan0-dbg - - libubsan0 - - libubsan0-dbg - env: MATRIX_EVAL="BUILD_TYPE=tsan && CC=gcc-7 && CXX=g++-7 && DEBUG=debug" + - clang-3.8 + env: MATRIX_EVAL="BUILD_TYPE=tsan && CC=clang-3.8 && CXX=clang++-3.8 && DEBUG=debug" - os: linux addons: apt: sources: - ubuntu-toolchain-r-test + - llvm-toolchain-precise-3.8 packages: - - g++-7 - - libasan4 - - libasan4-dbg - - libtsan0 - - libtsan0-dbg - - liblsan0 - - liblsan0-dbg - - libubsan0 - - libubsan0-dbg - env: MATRIX_EVAL="BUILD_TYPE=ubsan && CC=gcc-7 && CXX=g++-7 && DEBUG=debug" + - clang-3.8 + env: MATRIX_EVAL="BUILD_TYPE=ubsan && CC=clang-3.8 && CXX=clang++-3.8 && DEBUG=debug" - os: linux addons: apt: @@ -331,9 +279,8 @@ matrix: allow_failures: - env: MATRIX_EVAL="CROSS_COMPILE=1 && ARM_ARCH_DIR=arm-linux-gnueabi && CC=arm-linux-gnueabi-gcc" - env: MATRIX_EVAL="CROSS_COMPILE=1 && ARM_LD_PATH=$HOME/linker_bin && ARM_ARCH_DIR=aarch64-linux-gnu" - - env: MATRIX_EVAL="BUILD_TYPE=asan && CC=gcc-7 && CXX=g++-7 && DEBUG=debug" - - env: MATRIX_EVAL="BUILD_TYPE=asanpc && CC=gcc-7 && CXX=g++-7 && DEBUG=debug" - - env: MATRIX_EVAL="BUILD_TYPE=asanps && CC=gcc-7 && CXX=g++-7 && DEBUG=debug" - - env: MATRIX_EVAL="BUILD_TYPE=lsan && CC=gcc-7 && CXX=g++-7 && DEBUG=debug" - - env: MATRIX_EVAL="BUILD_TYPE=tsan && CC=gcc-7 && CXX=g++-7 && DEBUG=debug" - - env: MATRIX_EVAL="BUILD_TYPE=ubsan && CC=gcc-7 && CXX=g++-7 && DEBUG=debug" + - env: MATRIX_EVAL="BUILD_TYPE=asan && CC=clang-3.8 && CXX=clang++-3.8 && DEBUG=debug" + - env: MATRIX_EVAL="BUILD_TYPE=msan && CC=clang-3.8 && CXX=clang++-3.8 && DEBUG=debug" + - env: MATRIX_EVAL="BUILD_TYPE=lsan && CC=clang-3.8 && CXX=clang++-3.8 && DEBUG=debug" + - env: MATRIX_EVAL="BUILD_TYPE=tsan && CC=clang-3.8 && CXX=clang++-3.8 && DEBUG=debug" + - env: MATRIX_EVAL="BUILD_TYPE=ubsan && CC=clang-3.8 && CXX=clang++-3.8 && DEBUG=debug" From a5910e86c54ab9f026e3c9429aefa9e7d02f0948 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 6 Jan 2019 02:13:26 -0800 Subject: [PATCH 349/623] Split debug controls from coverage --- configure.ac | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/configure.ac b/configure.ac index 9cd9611b..b9c659e1 100644 --- a/configure.ac +++ b/configure.ac @@ -246,6 +246,24 @@ AC_MSG_RESULT([$debugit]) AM_LDFLAGS="-lstdc++" if test x"$debugit" = x"yes"; then + AC_DEFINE([DEBUG],[],[Debug Mode]) + AM_CXXFLAGS="$AM_CXXFLAGS -DDEBUG -g -Wall -Wno-uninitialized -O0" + AM_CFLAGS="$AM_CXXFLAGS -DDEBUG -g -Wall -Wno-uninitialized -O0" +else + AC_DEFINE([NDEBUG],[],[No-debug Mode]) + AM_CXXFLAGS="$AM_CXXFLAGS -O3" + AM_CFLAGS="$AM_CXXFLAGS -O3" +fi + +AC_MSG_CHECKING([whether to build with coverage information]) +AC_ARG_ENABLE([coverage], + [AS_HELP_STRING([--enable-coverage], + [enable coverage data generation (def=no)])], + [coverit="$enableval"], + [coverit=no]) +AC_MSG_RESULT([$coverit]) + +if test x"$coverit" = x"yes"; then case $host_os in darwin* ) echo "Coverage not supported on OSX" @@ -256,14 +274,6 @@ if test x"$debugit" = x"yes"; then cond_gcov="yes" ;; esac - - AC_DEFINE([DEBUG],[],[Debug Mode]) - AM_CXXFLAGS="$AM_CXXFLAGS -DDEBUG -g -Wall -Wno-uninitialized -O0" - AM_CFLAGS="$AM_CXXFLAGS -DDEBUG -g -Wall -Wno-uninitialized -O0" -else - AC_DEFINE([NDEBUG],[],[No-debug Mode]) - AM_CXXFLAGS="$AM_CXXFLAGS -O3" - AM_CFLAGS="$AM_CXXFLAGS -O3" fi if test "$CROSS_COMPILE" == "1"; then From be9589328b899619752806b19b88c1ed7f63ad13 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 6 Jan 2019 02:19:56 -0800 Subject: [PATCH 350/623] Fix given split between debug and coverage --- .travis.yml | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/.travis.yml b/.travis.yml index bccafd97..93cc696d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,8 +6,8 @@ compiler: - gcc - clang env: - - DEBUG="debug" - - DEBUG="nodebug" + - DEBUG="debug" COVEAGE="coverage" + - DEBUG="nodebug" COVERAGE="nocoverage" - LINKING="static" before_install: - eval "${MATRIX_EVAL}" @@ -47,6 +47,8 @@ install: - cd build - if [ "$LINKING" = "static" ]; then ../configure --enable-static --disable-fastopen; + elif [ "$DEBUG" = "debug" ] && [ "$COVERAGE" = "coverage" ]; then + ../configure --enable-debug --enable-coverage --disable-shared --disable-fastopen; elif [ "$DEBUG" = "debug" ]; then ../configure --enable-debug --disable-shared --disable-fastopen; elif [ "$CROSS_COMPILE" = "1" ] && [ "$ARM_ARCH_DIR" = "aarch64-linux-gnu" ]; then @@ -88,11 +90,11 @@ script: if [ "$TRAVIS_OS_NAME" = "linux" ]; then cd ../src/; cppcheck --error-exitcode=1 .; cd ../build; fi fi after_success: - - if [ "$DEBUG" = "debug" ] && [ "$TRAVIS_OS_NAME" = "linux" ]; then bash <(curl -s https://codecov.io/bash); fi + - if [ "$DEBUG" = "debug" ] && [ "$COVERAGE" = "coverage" ] && [ "$TRAVIS_OS_NAME" = "linux" ]; then bash <(curl -s https://codecov.io/bash); fi matrix: exclude: - compiler: clang - env: DEBUG="debug" + env: DEBUG="debug" COVERAGE="coverage" - compiler: clang env: LINKING='static' include: @@ -104,7 +106,7 @@ matrix: - llvm-toolchain-precise-3.8 packages: - clang-3.8 - env: MATRIX_EVAL="BUILD_TYPE=asan && CC=clang-3.8 && CXX=clang++-3.8 && DEBUG=debug" + env: MATRIX_EVAL="BUILD_TYPE=asan && CC=clang-3.8 && CXX=clang++-3.8 && DEBUG=debug COVERAGE=nocoverage" - os: linux addons: apt: @@ -113,7 +115,7 @@ matrix: - llvm-toolchain-precise-3.8 packages: - clang-3.8 - env: MATRIX_EVAL="BUILD_TYPE=msan && CC=clang-3.8 && CXX=clang++-3.8 && DEBUG=debug" + env: MATRIX_EVAL="BUILD_TYPE=msan && CC=clang-3.8 && CXX=clang++-3.8 && DEBUG=debug COVERAGE=nocoverage" - os: linux addons: apt: @@ -122,7 +124,7 @@ matrix: - llvm-toolchain-precise-3.8 packages: - clang-3.8 - env: MATRIX_EVAL="BUILD_TYPE=lsan && CC=clang-3.8 && CXX=clang++-3.8 && DEBUG=debug" + env: MATRIX_EVAL="BUILD_TYPE=lsan && CC=clang-3.8 && CXX=clang++-3.8 && DEBUG=debug COVERAGE=nocoverage" - os: linux addons: apt: @@ -131,7 +133,7 @@ matrix: - llvm-toolchain-precise-3.8 packages: - clang-3.8 - env: MATRIX_EVAL="BUILD_TYPE=tsan && CC=clang-3.8 && CXX=clang++-3.8 && DEBUG=debug" + env: MATRIX_EVAL="BUILD_TYPE=tsan && CC=clang-3.8 && CXX=clang++-3.8 && DEBUG=debug COVERAGE=nocoverage" - os: linux addons: apt: @@ -140,7 +142,7 @@ matrix: - llvm-toolchain-precise-3.8 packages: - clang-3.8 - env: MATRIX_EVAL="BUILD_TYPE=ubsan && CC=clang-3.8 && CXX=clang++-3.8 && DEBUG=debug" + env: MATRIX_EVAL="BUILD_TYPE=ubsan && CC=clang-3.8 && CXX=clang++-3.8 && DEBUG=debug COVERAGE=nocoverage" - os: linux addons: apt: From 46e71d5af903fbc00cf4722ca2909863e785f646 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 6 Jan 2019 02:21:41 -0800 Subject: [PATCH 351/623] Ignore analysis builds --- .travis.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 93cc696d..9428159e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -281,8 +281,8 @@ matrix: allow_failures: - env: MATRIX_EVAL="CROSS_COMPILE=1 && ARM_ARCH_DIR=arm-linux-gnueabi && CC=arm-linux-gnueabi-gcc" - env: MATRIX_EVAL="CROSS_COMPILE=1 && ARM_LD_PATH=$HOME/linker_bin && ARM_ARCH_DIR=aarch64-linux-gnu" - - env: MATRIX_EVAL="BUILD_TYPE=asan && CC=clang-3.8 && CXX=clang++-3.8 && DEBUG=debug" - - env: MATRIX_EVAL="BUILD_TYPE=msan && CC=clang-3.8 && CXX=clang++-3.8 && DEBUG=debug" - - env: MATRIX_EVAL="BUILD_TYPE=lsan && CC=clang-3.8 && CXX=clang++-3.8 && DEBUG=debug" - - env: MATRIX_EVAL="BUILD_TYPE=tsan && CC=clang-3.8 && CXX=clang++-3.8 && DEBUG=debug" - - env: MATRIX_EVAL="BUILD_TYPE=ubsan && CC=clang-3.8 && CXX=clang++-3.8 && DEBUG=debug" + - env: MATRIX_EVAL="BUILD_TYPE=asan && CC=clang-3.8 && CXX=clang++-3.8 && DEBUG=debug COVERAGE=nocoverage" + - env: MATRIX_EVAL="BUILD_TYPE=msan && CC=clang-3.8 && CXX=clang++-3.8 && DEBUG=debug COVERAGE=nocoverage" + - env: MATRIX_EVAL="BUILD_TYPE=lsan && CC=clang-3.8 && CXX=clang++-3.8 && DEBUG=debug COVERAGE=nocoverage" + - env: MATRIX_EVAL="BUILD_TYPE=tsan && CC=clang-3.8 && CXX=clang++-3.8 && DEBUG=debug COVERAGE=nocoverage" + - env: MATRIX_EVAL="BUILD_TYPE=ubsan && CC=clang-3.8 && CXX=clang++-3.8 && DEBUG=debug COVERAGE=nocoverage" From 071b53176c6b79bd0ff198e5f2014dd54e5dd3f3 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 6 Jan 2019 02:25:40 -0800 Subject: [PATCH 352/623] Fix typo --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 9428159e..f8fafc09 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,7 +6,7 @@ compiler: - gcc - clang env: - - DEBUG="debug" COVEAGE="coverage" + - DEBUG="debug" COVERAGE="coverage" - DEBUG="nodebug" COVERAGE="nocoverage" - LINKING="static" before_install: From 95f87cdc9212914a7e0d8ad3a462228bcb5a2f33 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 6 Jan 2019 02:30:48 -0800 Subject: [PATCH 353/623] Fix analysis builds --- .travis.yml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/.travis.yml b/.travis.yml index f8fafc09..bacdd859 100644 --- a/.travis.yml +++ b/.travis.yml @@ -106,7 +106,7 @@ matrix: - llvm-toolchain-precise-3.8 packages: - clang-3.8 - env: MATRIX_EVAL="BUILD_TYPE=asan && CC=clang-3.8 && CXX=clang++-3.8 && DEBUG=debug COVERAGE=nocoverage" + env: MATRIX_EVAL="BUILD_TYPE=asan && CC=clang-3.8 && CXX=clang++-3.8 && DEBUG=debug && COVERAGE=nocoverage" - os: linux addons: apt: @@ -115,7 +115,7 @@ matrix: - llvm-toolchain-precise-3.8 packages: - clang-3.8 - env: MATRIX_EVAL="BUILD_TYPE=msan && CC=clang-3.8 && CXX=clang++-3.8 && DEBUG=debug COVERAGE=nocoverage" + env: MATRIX_EVAL="BUILD_TYPE=msan && CC=clang-3.8 && CXX=clang++-3.8 && DEBUG=debug && COVERAGE=nocoverage" - os: linux addons: apt: @@ -124,7 +124,7 @@ matrix: - llvm-toolchain-precise-3.8 packages: - clang-3.8 - env: MATRIX_EVAL="BUILD_TYPE=lsan && CC=clang-3.8 && CXX=clang++-3.8 && DEBUG=debug COVERAGE=nocoverage" + env: MATRIX_EVAL="BUILD_TYPE=lsan && CC=clang-3.8 && CXX=clang++-3.8 && DEBUG=debug && COVERAGE=nocoverage" - os: linux addons: apt: @@ -133,7 +133,7 @@ matrix: - llvm-toolchain-precise-3.8 packages: - clang-3.8 - env: MATRIX_EVAL="BUILD_TYPE=tsan && CC=clang-3.8 && CXX=clang++-3.8 && DEBUG=debug COVERAGE=nocoverage" + env: MATRIX_EVAL="BUILD_TYPE=tsan && CC=clang-3.8 && CXX=clang++-3.8 && DEBUG=debug && COVERAGE=nocoverage" - os: linux addons: apt: @@ -142,7 +142,7 @@ matrix: - llvm-toolchain-precise-3.8 packages: - clang-3.8 - env: MATRIX_EVAL="BUILD_TYPE=ubsan && CC=clang-3.8 && CXX=clang++-3.8 && DEBUG=debug COVERAGE=nocoverage" + env: MATRIX_EVAL="BUILD_TYPE=ubsan && CC=clang-3.8 && CXX=clang++-3.8 && DEBUG=debug && COVERAGE=nocoverage" - os: linux addons: apt: @@ -281,8 +281,8 @@ matrix: allow_failures: - env: MATRIX_EVAL="CROSS_COMPILE=1 && ARM_ARCH_DIR=arm-linux-gnueabi && CC=arm-linux-gnueabi-gcc" - env: MATRIX_EVAL="CROSS_COMPILE=1 && ARM_LD_PATH=$HOME/linker_bin && ARM_ARCH_DIR=aarch64-linux-gnu" - - env: MATRIX_EVAL="BUILD_TYPE=asan && CC=clang-3.8 && CXX=clang++-3.8 && DEBUG=debug COVERAGE=nocoverage" - - env: MATRIX_EVAL="BUILD_TYPE=msan && CC=clang-3.8 && CXX=clang++-3.8 && DEBUG=debug COVERAGE=nocoverage" - - env: MATRIX_EVAL="BUILD_TYPE=lsan && CC=clang-3.8 && CXX=clang++-3.8 && DEBUG=debug COVERAGE=nocoverage" - - env: MATRIX_EVAL="BUILD_TYPE=tsan && CC=clang-3.8 && CXX=clang++-3.8 && DEBUG=debug COVERAGE=nocoverage" - - env: MATRIX_EVAL="BUILD_TYPE=ubsan && CC=clang-3.8 && CXX=clang++-3.8 && DEBUG=debug COVERAGE=nocoverage" + - env: MATRIX_EVAL="BUILD_TYPE=asan && CC=clang-3.8 && CXX=clang++-3.8 && DEBUG=debug && COVERAGE=nocoverage" + - env: MATRIX_EVAL="BUILD_TYPE=msan && CC=clang-3.8 && CXX=clang++-3.8 && DEBUG=debug && COVERAGE=nocoverage" + - env: MATRIX_EVAL="BUILD_TYPE=lsan && CC=clang-3.8 && CXX=clang++-3.8 && DEBUG=debug && COVERAGE=nocoverage" + - env: MATRIX_EVAL="BUILD_TYPE=tsan && CC=clang-3.8 && CXX=clang++-3.8 && DEBUG=debug && COVERAGE=nocoverage" + - env: MATRIX_EVAL="BUILD_TYPE=ubsan && CC=clang-3.8 && CXX=clang++-3.8 && DEBUG=debug && COVERAGE=nocoverage" From 5c665365eb5331b838def950332ce694755fdafc Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 6 Jan 2019 10:58:56 -0800 Subject: [PATCH 354/623] sanitize flags in CXXFLAGS --- .travis.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index bacdd859..c3d50276 100644 --- a/.travis.yml +++ b/.travis.yml @@ -27,11 +27,11 @@ before_install: - make - sudo make install - cd .. - - if [ "$BUILD_TYPE" = "asan" ]; then export CFLAGS='-fsanitize=address'; export LDFLAGS='-fsanitize=address'; fi - - if [ "$BUILD_TYPE" = "msan" ]; then export CFLAGS='-fsanitize=memory'; export LDFLAGS='-fsanitize=memory'; fi - - if [ "$BUILD_TYPE" = "lsan" ]; then export CFLAGS='-fsanitize=leak'; export LDFLAGS='-fsanitize=leak'; fi - - if [ "$BUILD_TYPE" = "tsan" ]; then export CFLAGS='-fsanitize=thread'; export LDFLAGS='-fsanitize=thread'; fi - - if [ "$BUILD_TYPE" = "ubsan" ]; then export export CFLAGS='-fsanitize=undefined'; export LDFLAGS='-fsanitize=undefined'; fi + - if [ "$BUILD_TYPE" = "asan" ]; then export CFLAGS='-fsanitize=address'; export CXXLAGS='-fsanitize=address'; export LDFLAGS='-fsanitize=address'; fi + - if [ "$BUILD_TYPE" = "msan" ]; then export CFLAGS='-fsanitize=memory'; export CXXLAGS='-fsanitize=memory'; export LDFLAGS='-fsanitize=memory'; fi + - if [ "$BUILD_TYPE" = "lsan" ]; then export CFLAGS='-fsanitize=leak'; export CXXLAGS='-fsanitize=leak'; export LDFLAGS='-fsanitize=leak'; fi + - if [ "$BUILD_TYPE" = "tsan" ]; then export CFLAGS='-fsanitize=thread'; export CXXLAGS='-fsanitize=thread'; export LDFLAGS='-fsanitize=thread'; fi + - if [ "$BUILD_TYPE" = "ubsan" ]; then export export CFLAGS='-fsanitize=undefined'; export CXXLAGS='-fsanitize=undefined'; export LDFLAGS='-fsanitize=undefined'; fi install: - if [[ "$CROSS_COMPILE" == 1 ]] ; then if [[ "$ARM_ARCH_DIR" == "aarch64-linux-gnu" ]] ; then From 69e9312a2f74bccefebfd0bf869bf06baaafac3d Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 6 Jan 2019 11:17:36 -0800 Subject: [PATCH 355/623] Promoting dynamic analysis to main builds --- .travis.yml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index c3d50276..8d3f2805 100644 --- a/.travis.yml +++ b/.travis.yml @@ -281,8 +281,3 @@ matrix: allow_failures: - env: MATRIX_EVAL="CROSS_COMPILE=1 && ARM_ARCH_DIR=arm-linux-gnueabi && CC=arm-linux-gnueabi-gcc" - env: MATRIX_EVAL="CROSS_COMPILE=1 && ARM_LD_PATH=$HOME/linker_bin && ARM_ARCH_DIR=aarch64-linux-gnu" - - env: MATRIX_EVAL="BUILD_TYPE=asan && CC=clang-3.8 && CXX=clang++-3.8 && DEBUG=debug && COVERAGE=nocoverage" - - env: MATRIX_EVAL="BUILD_TYPE=msan && CC=clang-3.8 && CXX=clang++-3.8 && DEBUG=debug && COVERAGE=nocoverage" - - env: MATRIX_EVAL="BUILD_TYPE=lsan && CC=clang-3.8 && CXX=clang++-3.8 && DEBUG=debug && COVERAGE=nocoverage" - - env: MATRIX_EVAL="BUILD_TYPE=tsan && CC=clang-3.8 && CXX=clang++-3.8 && DEBUG=debug && COVERAGE=nocoverage" - - env: MATRIX_EVAL="BUILD_TYPE=ubsan && CC=clang-3.8 && CXX=clang++-3.8 && DEBUG=debug && COVERAGE=nocoverage" From c9b0c8fd54325c81cd22107af6c1d57f94f02503 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Mon, 7 Jan 2019 02:04:43 +0000 Subject: [PATCH 356/623] Enable check with valgrind --- configure.ac | 3 + m4/ax_valgrind_check.m4 | 239 ++++++++++++++++++++++++++++++++++++++++ test/Makefile.am | 4 + 3 files changed, 246 insertions(+) create mode 100644 m4/ax_valgrind_check.m4 diff --git a/configure.ac b/configure.ac index b9c659e1..b84c1d53 100644 --- a/configure.ac +++ b/configure.ac @@ -31,6 +31,9 @@ AC_CONFIG_HEADERS([config.h]) AC_CONFIG_MACRO_DIR([m4]) AC_CANONICAL_HOST +AX_VALGRIND_DFLT +AX_VALGRIND_CHECK + OLD_CXXFLAGS=$CXXFLAGS LT_INIT AC_PROG_CC diff --git a/m4/ax_valgrind_check.m4 b/m4/ax_valgrind_check.m4 new file mode 100644 index 00000000..70337981 --- /dev/null +++ b/m4/ax_valgrind_check.m4 @@ -0,0 +1,239 @@ +# =========================================================================== +# https://www.gnu.org/software/autoconf-archive/ax_valgrind_check.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_VALGRIND_DFLT(memcheck|helgrind|drd|sgcheck, on|off) +# AX_VALGRIND_CHECK() +# +# DESCRIPTION +# +# AX_VALGRIND_CHECK checks whether Valgrind is present and, if so, allows +# running `make check` under a variety of Valgrind tools to check for +# memory and threading errors. +# +# Defines VALGRIND_CHECK_RULES which should be substituted in your +# Makefile; and $enable_valgrind which can be used in subsequent configure +# output. VALGRIND_ENABLED is defined and substituted, and corresponds to +# the value of the --enable-valgrind option, which defaults to being +# enabled if Valgrind is installed and disabled otherwise. Individual +# Valgrind tools can be disabled via --disable-valgrind-, the +# default is configurable via the AX_VALGRIND_DFLT command or is to use +# all commands not disabled via AX_VALGRIND_DFLT. All AX_VALGRIND_DFLT +# calls must be made before the call to AX_VALGRIND_CHECK. +# +# If unit tests are written using a shell script and automake's +# LOG_COMPILER system, the $(VALGRIND) variable can be used within the +# shell scripts to enable Valgrind, as described here: +# +# https://www.gnu.org/software/gnulib/manual/html_node/Running-self_002dtests-under-valgrind.html +# +# Usage example: +# +# configure.ac: +# +# AX_VALGRIND_DFLT([sgcheck], [off]) +# AX_VALGRIND_CHECK +# +# in each Makefile.am with tests: +# +# @VALGRIND_CHECK_RULES@ +# VALGRIND_SUPPRESSIONS_FILES = my-project.supp +# EXTRA_DIST = my-project.supp +# +# This results in a "check-valgrind" rule being added. Running `make +# check-valgrind` in that directory will recursively run the module's test +# suite (`make check`) once for each of the available Valgrind tools (out +# of memcheck, helgrind and drd) while the sgcheck will be skipped unless +# enabled again on the commandline with --enable-valgrind-sgcheck. The +# results for each check will be output to test-suite-$toolname.log. The +# target will succeed if there are zero errors and fail otherwise. +# +# Alternatively, a "check-valgrind-$TOOL" rule will be added, for $TOOL in +# memcheck, helgrind, drd and sgcheck. These are useful because often only +# some of those tools can be ran cleanly on a codebase. +# +# The macro supports running with and without libtool. +# +# LICENSE +# +# Copyright (c) 2014, 2015, 2016 Philip Withnall +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 17 + +dnl Configured tools +m4_define([valgrind_tool_list], [[memcheck], [helgrind], [drd], [sgcheck]]) +m4_set_add_all([valgrind_exp_tool_set], [sgcheck]) +m4_foreach([vgtool], [valgrind_tool_list], + [m4_define([en_dflt_valgrind_]vgtool, [on])]) + +AC_DEFUN([AX_VALGRIND_DFLT],[ + m4_define([en_dflt_valgrind_$1], [$2]) +])dnl + +AM_EXTRA_RECURSIVE_TARGETS([check-valgrind]) +m4_foreach([vgtool], [valgrind_tool_list], + [AM_EXTRA_RECURSIVE_TARGETS([check-valgrind-]vgtool)]) + +AC_DEFUN([AX_VALGRIND_CHECK],[ + dnl Check for --enable-valgrind + AC_ARG_ENABLE([valgrind], + [AS_HELP_STRING([--enable-valgrind], [Whether to enable Valgrind on the unit tests])], + [enable_valgrind=$enableval],[enable_valgrind=]) + + AS_IF([test "$enable_valgrind" != "no"],[ + # Check for Valgrind. + AC_CHECK_PROG([VALGRIND],[valgrind],[valgrind]) + AS_IF([test "$VALGRIND" = ""],[ + AS_IF([test "$enable_valgrind" = "yes"],[ + AC_MSG_ERROR([Could not find valgrind; either install it or reconfigure with --disable-valgrind]) + ],[ + enable_valgrind=no + ]) + ],[ + enable_valgrind=yes + ]) + ]) + + AM_CONDITIONAL([VALGRIND_ENABLED],[test "$enable_valgrind" = "yes"]) + AC_SUBST([VALGRIND_ENABLED],[$enable_valgrind]) + + # Check for Valgrind tools we care about. + [valgrind_enabled_tools=] + m4_foreach([vgtool],[valgrind_tool_list],[ + AC_ARG_ENABLE([valgrind-]vgtool, + m4_if(m4_defn([en_dflt_valgrind_]vgtool),[off],dnl +[AS_HELP_STRING([--enable-valgrind-]vgtool, [Whether to use ]vgtool[ during the Valgrind tests])],dnl +[AS_HELP_STRING([--disable-valgrind-]vgtool, [Whether to skip ]vgtool[ during the Valgrind tests])]), + [enable_valgrind_]vgtool[=$enableval], + [enable_valgrind_]vgtool[=]) + AS_IF([test "$enable_valgrind" = "no"],[ + enable_valgrind_]vgtool[=no], + [test "$enable_valgrind_]vgtool[" ]dnl +m4_if(m4_defn([en_dflt_valgrind_]vgtool), [off], [= "yes"], [!= "no"]),[ + AC_CACHE_CHECK([for Valgrind tool ]vgtool, + [ax_cv_valgrind_tool_]vgtool,[ + ax_cv_valgrind_tool_]vgtool[=no + m4_set_contains([valgrind_exp_tool_set],vgtool, + [m4_define([vgtoolx],[exp-]vgtool)], + [m4_define([vgtoolx],vgtool)]) + AS_IF([`$VALGRIND --tool=]vgtoolx[ --help >/dev/null 2>&1`],[ + ax_cv_valgrind_tool_]vgtool[=yes + ]) + ]) + AS_IF([test "$ax_cv_valgrind_tool_]vgtool[" = "no"],[ + AS_IF([test "$enable_valgrind_]vgtool[" = "yes"],[ + AC_MSG_ERROR([Valgrind does not support ]vgtool[; reconfigure with --disable-valgrind-]vgtool) + ],[ + enable_valgrind_]vgtool[=no + ]) + ],[ + enable_valgrind_]vgtool[=yes + ]) + ]) + AS_IF([test "$enable_valgrind_]vgtool[" = "yes"],[ + valgrind_enabled_tools="$valgrind_enabled_tools ]m4_bpatsubst(vgtool,[^exp-])[" + ]) + AC_SUBST([ENABLE_VALGRIND_]vgtool,[$enable_valgrind_]vgtool) + ]) + AC_SUBST([valgrind_tools],["]m4_join([ ], valgrind_tool_list)["]) + AC_SUBST([valgrind_enabled_tools],[$valgrind_enabled_tools]) + +[VALGRIND_CHECK_RULES=' +# Valgrind check +# +# Optional: +# - VALGRIND_SUPPRESSIONS_FILES: Space-separated list of Valgrind suppressions +# files to load. (Default: empty) +# - VALGRIND_FLAGS: General flags to pass to all Valgrind tools. +# (Default: --num-callers=30) +# - VALGRIND_$toolname_FLAGS: Flags to pass to Valgrind $toolname (one of: +# memcheck, helgrind, drd, sgcheck). (Default: various) + +# Optional variables +VALGRIND_SUPPRESSIONS ?= $(addprefix --suppressions=,$(VALGRIND_SUPPRESSIONS_FILES)) +VALGRIND_FLAGS ?= --num-callers=30 +VALGRIND_memcheck_FLAGS ?= --leak-check=full --show-reachable=no +VALGRIND_helgrind_FLAGS ?= --history-level=approx +VALGRIND_drd_FLAGS ?= +VALGRIND_sgcheck_FLAGS ?= + +# Internal use +valgrind_log_files = $(addprefix test-suite-,$(addsuffix .log,$(valgrind_tools))) + +valgrind_memcheck_flags = --tool=memcheck $(VALGRIND_memcheck_FLAGS) +valgrind_helgrind_flags = --tool=helgrind $(VALGRIND_helgrind_FLAGS) +valgrind_drd_flags = --tool=drd $(VALGRIND_drd_FLAGS) +valgrind_sgcheck_flags = --tool=exp-sgcheck $(VALGRIND_sgcheck_FLAGS) + +valgrind_quiet = $(valgrind_quiet_$(V)) +valgrind_quiet_ = $(valgrind_quiet_$(AM_DEFAULT_VERBOSITY)) +valgrind_quiet_0 = --quiet +valgrind_v_use = $(valgrind_v_use_$(V)) +valgrind_v_use_ = $(valgrind_v_use_$(AM_DEFAULT_VERBOSITY)) +valgrind_v_use_0 = @echo " USE " $(patsubst check-valgrind-%-am,%,$''@):; + +# Support running with and without libtool. +ifneq ($(LIBTOOL),) +valgrind_lt = $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=execute +else +valgrind_lt = +endif + +# Use recursive makes in order to ignore errors during check +check-valgrind-am: +ifeq ($(VALGRIND_ENABLED),yes) + $(A''M_V_at)$(MAKE) $(AM_MAKEFLAGS) -k \ + $(foreach tool, $(valgrind_enabled_tools), check-valgrind-$(tool)) +else + @echo "Need to reconfigure with --enable-valgrind" +endif + +# Valgrind running +VALGRIND_TESTS_ENVIRONMENT = \ + $(TESTS_ENVIRONMENT) \ + env VALGRIND=$(VALGRIND) \ + G_SLICE=always-malloc,debug-blocks \ + G_DEBUG=fatal-warnings,fatal-criticals,gc-friendly + +VALGRIND_LOG_COMPILER = \ + $(valgrind_lt) \ + $(VALGRIND) $(VALGRIND_SUPPRESSIONS) --error-exitcode=1 $(VALGRIND_FLAGS) + +define valgrind_tool_rule +check-valgrind-$(1)-am: +ifeq ($$(VALGRIND_ENABLED)-$$(ENABLE_VALGRIND_$(1)),yes-yes) +ifneq ($$(TESTS),) + $$(valgrind_v_use)$$(MAKE) check-TESTS \ + TESTS_ENVIRONMENT="$$(VALGRIND_TESTS_ENVIRONMENT)" \ + LOG_COMPILER="$$(VALGRIND_LOG_COMPILER)" \ + LOG_FLAGS="$$(valgrind_$(1)_flags)" \ + TEST_SUITE_LOG=test-suite-$(1).log +endif +else ifeq ($$(VALGRIND_ENABLED),yes) + @echo "Need to reconfigure with --enable-valgrind-$(1)" +else + @echo "Need to reconfigure with --enable-valgrind" +endif +endef + +$(foreach tool,$(valgrind_tools),$(eval $(call valgrind_tool_rule,$(tool)))) + +A''M_DISTCHECK_CONFIGURE_FLAGS ?= +A''M_DISTCHECK_CONFIGURE_FLAGS += --disable-valgrind + +MOSTLYCLEANFILES ?= +MOSTLYCLEANFILES += $(valgrind_log_files) + +.PHONY: check-valgrind $(add-prefix check-valgrind-,$(valgrind_tools)) +'] + + AC_SUBST([VALGRIND_CHECK_RULES]) + m4_ifdef([_AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE([VALGRIND_CHECK_RULES])]) +]) diff --git a/test/Makefile.am b/test/Makefile.am index 0c02c692..30bc5000 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -44,3 +44,7 @@ AM_LDFLAGS += -O0 --coverage -lgcov --no-inline endif TESTS = $(check_PROGRAMS) + +@VALGRIND_CHECK_RULES@ +#VALGRIND_SUPPRESSIONS_FILES = libhttpserver.supp +#EXTRA_DIST = libhttpserver.supp From d685890f0d4b6bb2e08a9891f4ad66dcd505e54e Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Mon, 7 Jan 2019 04:16:02 +0000 Subject: [PATCH 357/623] Eliminate unsafe access to char* --- src/http_utils.cpp | 30 ++++++++++++++------------- src/httpserver/create_webserver.hpp | 2 +- src/httpserver/http_utils.hpp | 2 +- src/httpserver/webserver.hpp | 2 +- src/string_utilities.cpp | 4 ++-- src/webserver.cpp | 32 +++++++++++++++-------------- test/integ/deferred.cpp | 2 +- test/unit/http_utils_test.cpp | 21 +++++++++++-------- 8 files changed, 52 insertions(+), 43 deletions(-) diff --git a/src/http_utils.cpp b/src/http_utils.cpp index 368c9ba8..b3521e82 100644 --- a/src/http_utils.cpp +++ b/src/http_utils.cpp @@ -281,42 +281,44 @@ unsigned short get_port(const struct sockaddr* sa) } } -size_t http_unescape(char *val) +size_t http_unescape(std::string& val) { - char *rpos = val; - char *wpos = val; + if (val.empty()) return 0; + + int rpos = 0; + int wpos = 0; + unsigned int num; - while ('\0' != *rpos) + while ('\0' != val[rpos]) { - switch (*rpos) + switch (val[rpos]) { case '+': - *wpos = ' '; + val[wpos] = ' '; wpos++; rpos++; break; case '%': - if ( (1 == sscanf (&rpos[1], - "%2x", &num)) || - (1 == sscanf (&rpos[1], - "%2X", &num)) + if ( (1 == sscanf (val.substr(rpos + 1).c_str(), "%2x", &num)) || + (1 == sscanf (val.substr(rpos + 1).c_str(), "%2X", &num)) ) { - *wpos = (unsigned char) num; + val[wpos] = (unsigned char) num; wpos++; rpos += 3; break; } /* intentional fall through! */ default: - *wpos = *rpos; + val[wpos] = val[rpos]; wpos++; rpos++; } } - *wpos = '\0'; /* add 0-terminator */ - return wpos - val; /* = strlen(val) */ + val[wpos] = '\0'; /* add 0-terminator */ + val.resize(wpos); + return wpos; /* = strlen(val) */ } ip_representation::ip_representation(const struct sockaddr* ip) diff --git a/src/httpserver/create_webserver.hpp b/src/httpserver/create_webserver.hpp index c6c48f79..e50ff842 100644 --- a/src/httpserver/create_webserver.hpp +++ b/src/httpserver/create_webserver.hpp @@ -39,7 +39,7 @@ class http_request; typedef const http_response(*render_ptr)(const http_request&); typedef bool(*validator_ptr)(const std::string&); -typedef void(*unescaper_ptr)(char*); +typedef void(*unescaper_ptr)(std::string&); typedef void(*log_access_ptr)(const std::string&); typedef void(*log_error_ptr)(const std::string&); diff --git a/src/httpserver/http_utils.hpp b/src/httpserver/http_utils.hpp index e2896d19..32c675de 100644 --- a/src/httpserver/http_utils.hpp +++ b/src/httpserver/http_utils.hpp @@ -352,7 +352,7 @@ void dump_arg_map(std::ostream &os, const std::string &prefix, * @return length of the resulting val (strlen(val) maybe * shorter afterwards due to elimination of escape sequences) */ -size_t http_unescape (char *val); +size_t http_unescape (std::string& val); const std::string load_file (const std::string& filename); diff --git a/src/httpserver/webserver.hpp b/src/httpserver/webserver.hpp index ee5610e4..0d9f8fa6 100644 --- a/src/httpserver/webserver.hpp +++ b/src/httpserver/webserver.hpp @@ -288,7 +288,7 @@ class webserver friend size_t unescaper_func(void * cls, struct MHD_Connection *c, char *s ); - friend size_t internal_unescaper(void * cls, char *s); + friend size_t internal_unescaper(void * cls, std::string& s); friend class http_response; }; diff --git a/src/string_utilities.cpp b/src/string_utilities.cpp index 30f28c73..15f3743c 100644 --- a/src/string_utilities.cpp +++ b/src/string_utilities.cpp @@ -107,12 +107,12 @@ const std::string regex_replace(const std::string& str, ); memcpy(&ns[substmatch[0].rm_so+replace_str.size()], - &str[substmatch[0].rm_eo], strlen(&str[substmatch[0].rm_eo]) + &str[substmatch[0].rm_eo], str.substr(substmatch[0].rm_eo).size() ); ns[substmatch[0].rm_so + replace_str.size() + - strlen(&str[substmatch[0].rm_eo]) + str.substr(substmatch[0].rm_eo).size() ] = 0; result = std::string((char*)ns); diff --git a/src/webserver.cpp b/src/webserver.cpp index 36ac00cf..8a6caaf8 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -96,7 +96,7 @@ void error_log(void*, const char*, va_list); void* uri_log(void*, const char*); void access_log(webserver*, string); size_t unescaper_func(void*, struct MHD_Connection*, char*); -size_t internal_unescaper(void*, char*); +size_t internal_unescaper(void*, std::string&); struct compare_value { @@ -494,22 +494,22 @@ int webserver::build_request_args ( ) { details::modded_request* mr = static_cast(cls); - char* value = (char*) ((arg_value == NULL) ? "" : arg_value); + std::string value = ((arg_value == NULL) ? "" : arg_value); { - char buf[strlen(key) + strlen(value) + 3]; + char buf[strlen(key) + value.size() + 3]; if(mr->dhr->querystring == "") { - snprintf(buf, sizeof buf, "?%s=%s", key, value); + snprintf(buf, sizeof buf, "?%s=%s", key, value.c_str()); mr->dhr->querystring = buf; } else { - snprintf(buf, sizeof buf, "&%s=%s", key, value); + snprintf(buf, sizeof buf, "&%s=%s", key, value.c_str()); mr->dhr->querystring += string(buf); } } - size_t size = internal_unescaper((void*) mr->ws, value); - mr->dhr->set_arg(key, string(value, size)); + internal_unescaper((void*) mr->ws, value); + mr->dhr->set_arg(key, value); return MHD_YES; } @@ -560,7 +560,7 @@ size_t unescaper_func(void * cls, struct MHD_Connection *c, char *s) return strlen(s); } -size_t internal_unescaper(void* cls, char* s) +size_t internal_unescaper(void* cls, std::string& s) { if(s[0] == 0) return 0; @@ -568,7 +568,7 @@ size_t internal_unescaper(void* cls, char* s) if(dws->unescaper != 0x0) { dws->unescaper(s); - return strlen(s); + return s.size(); } return http_unescape(s); @@ -658,15 +658,15 @@ int webserver::bodyfull_requests_answer_first_step( ( 0x0 != encoding && ((0 == strncasecmp ( - MHD_HTTP_POST_ENCODING_FORM_URLENCODED, + http_utils::http_post_encoding_form_urlencoded.c_str(), encoding, - strlen (MHD_HTTP_POST_ENCODING_FORM_URLENCODED) + http_utils::http_post_encoding_form_urlencoded.size() ) ) || (0 == strncasecmp ( - MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA, + http_utils::http_post_encoding_multipart_formdata.c_str(), encoding, - strlen (MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA) + http_utils::http_post_encoding_multipart_formdata.size() ))) ) ) @@ -966,8 +966,10 @@ int webserver::answer_to_connection(void* cls, MHD_Connection* connection, ); } - internal_unescaper((void*) static_cast(cls), (char*) url); - mr->standardized_url = new string(http_utils::standardize_url(url)); + std::string t_url = url; + + internal_unescaper((void*) static_cast(cls), t_url); + mr->standardized_url = new string(http_utils::standardize_url(t_url)); bool body = false; diff --git a/test/integ/deferred.cpp b/test/integ/deferred.cpp index 3e68c8ee..2db6fc0f 100644 --- a/test/integ/deferred.cpp +++ b/test/integ/deferred.cpp @@ -58,7 +58,7 @@ ssize_t test_callback (char* buf, size_t max) memset(buf, 0, max); strcat(buf, "test"); counter++; - return strlen(buf); + return std::string(buf).size(); } } diff --git a/test/unit/http_utils_test.cpp b/test/unit/http_utils_test.cpp index cc81ceb8..240bf5c8 100644 --- a/test/unit/http_utils_test.cpp +++ b/test/unit/http_utils_test.cpp @@ -47,32 +47,37 @@ LT_BEGIN_SUITE(http_utils_suite) LT_END_SUITE(http_utils_suite) LT_BEGIN_AUTO_TEST(http_utils_suite, unescape) - char* string_with_plus = (char*) malloc(6 * sizeof(char)); - sprintf(string_with_plus, "%s", "A%20B"); + char* with_plus = (char*) malloc(6 * sizeof(char)); + sprintf(with_plus, "%s", "A%20B"); + std::string string_with_plus = with_plus; int expected_size = http::http_unescape(string_with_plus); char* expected = (char*) malloc(4 * sizeof(char)); sprintf(expected, "%s", "A B"); - LT_CHECK_EQ(string(string_with_plus), string(expected)); + std::cout << "|||||" << string_with_plus << "||||" << std::endl; + std::cout << expected << std::endl; + + LT_CHECK_EQ(string_with_plus, string(expected)); LT_CHECK_EQ(expected_size, 3); - free(string_with_plus); + free(with_plus); free(expected); LT_END_AUTO_TEST(unescape) LT_BEGIN_AUTO_TEST(http_utils_suite, unescape_plus) - char* string_with_plus = (char*) malloc(6 * sizeof(char)); - sprintf(string_with_plus, "%s", "A+B"); + char* with_plus = (char*) malloc(6 * sizeof(char)); + sprintf(with_plus, "%s", "A+B"); + std::string string_with_plus = with_plus; int expected_size = http::http_unescape(string_with_plus); char* expected = (char*) malloc(4 * sizeof(char)); sprintf(expected, "%s", "A B"); - LT_CHECK_EQ(string(string_with_plus), string(expected)); + LT_CHECK_EQ(string_with_plus, string(expected)); LT_CHECK_EQ(expected_size, 3); - free(string_with_plus); + free(with_plus); free(expected); LT_END_AUTO_TEST(unescape_plus) From 1c194716653292e3d32d7b40a88b6c9aff6d1f62 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Mon, 7 Jan 2019 04:20:21 +0000 Subject: [PATCH 358/623] Eliminate usages of strlen --- src/webserver.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/webserver.cpp b/src/webserver.cpp index 8a6caaf8..c137ffc3 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -496,7 +496,7 @@ int webserver::build_request_args ( details::modded_request* mr = static_cast(cls); std::string value = ((arg_value == NULL) ? "" : arg_value); { - char buf[strlen(key) + value.size() + 3]; + char buf[std::string(key).size() + value.size() + 3]; if(mr->dhr->querystring == "") { snprintf(buf, sizeof buf, "?%s=%s", key, value.c_str()); @@ -557,7 +557,7 @@ size_t unescaper_func(void * cls, struct MHD_Connection *c, char *s) // IT IS DUE TO A BOGUS ON libmicrohttpd (V0.99) THAT PRODUCING A // STRING CONTAINING '\0' AFTER AN UNESCAPING, IS UNABLE TO PARSE // ARGS WITH get_connection_values FUNC OR lookup FUNC. - return strlen(s); + return std::string(s).size(); } size_t internal_unescaper(void* cls, std::string& s) From 24ccc0853911a5e9d56d5f6311881fbb2ffbb2cb Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 6 Jan 2019 22:43:23 -0800 Subject: [PATCH 359/623] Create CODE_OF_CONDUCT.md --- CODE_OF_CONDUCT.md | 76 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 CODE_OF_CONDUCT.md diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 00000000..5eb1c639 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,76 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, sex characteristics, gender identity and expression, +level of experience, education, socio-economic status, nationality, personal +appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or + advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at etr@zencoders.org. All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see +https://www.contributor-covenant.org/faq From 417eadd8625cacc31e97665615f32afc9d525038 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 6 Jan 2019 23:47:04 -0800 Subject: [PATCH 360/623] Update issue templates --- .github/ISSUE_TEMPLATE/bug_report.md | 56 ++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 00000000..c121c157 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,56 @@ +--- +name: Bug report +about: Create a report to help us improve +title: "[BUG] Title" +labels: bug +assignees: etr + +--- + + + +### Prerequisites + +* [ ] Put an X between the brackets on this line if you have checked that your issue isn't already filed: https://github.com/search?l=&q=repo%3Aetr%2Flibhttpserver&type=Issues + +### Description + +[Description of the issue] + +### Steps to Reproduce + +1. [First Step] +2. [Second Step] +3. [and so on...] + +**Expected behavior:** [What you expect to happen] + +**Actual behavior:** [What actually happens] + +**Reproduces how often:** [What percentage of the time does it reproduce?] + +### Versions + +* OS version (if on linux, the output of "uname -a") +* libhttpserver version (please specify whether compiled or packaged) +* libmicrohttpd version (please specify whether compiled or packaged) + +If you have problems during build: +* Compiler version +* autotools version + +### Additional Information + +Any additional information, configuration (especially build configuration flags if you compiled the libraries) or data that might be necessary to reproduce the issue. + +If you have problems during build, please attach your config.log and the full scope of your error from make. + +If you have problems at execution, please: +* attach the stacktrace in case of crash (a coredump would be even better). +* provide a main that reproduces the error. From 7291af4fb67fbab1ed535dcc9a2af34829301401 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Mon, 7 Jan 2019 00:08:21 -0800 Subject: [PATCH 361/623] fix wrapping --- src/http_utils.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/http_utils.cpp b/src/http_utils.cpp index b3521e82..446dd755 100644 --- a/src/http_utils.cpp +++ b/src/http_utils.cpp @@ -239,10 +239,7 @@ std::string http_utils::standardize_url(const std::string& url) return result; } -std::string get_ip_str( - const struct sockaddr *sa, - socklen_t maxlen -) +std::string get_ip_str(const struct sockaddr *sa, socklen_t maxlen) { if (!sa) throw std::invalid_argument("socket pointer is null"); From 247b1b7e509d913a2b34f919365e04fe8571fed8 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Mon, 7 Jan 2019 00:31:39 -0800 Subject: [PATCH 362/623] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 97f6cc63..84c0effe 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,7 @@ The libhttpserver (0.8.0) reference manual [![Build Status](https://travis-ci.org/etr/libhttpserver.png?branch=master)](https://travis-ci.org/etr/libhttpserver) [![codecov](https://codecov.io/gh/etr/libhttpserver/branch/master/graph/badge.svg)](https://codecov.io/gh/etr/libhttpserver) [![Codacy Badge](https://api.codacy.com/project/badge/Grade/5fa4bdc3815b4c10977f3badefedecd6)](https://www.codacy.com/app/etr/libhttpserver?utm_source=github.com&utm_medium=referral&utm_content=etr/libhttpserver&utm_campaign=Badge_Grade) +[![Gitter chat](https://badges.gitter.im/etr/libhttpserver.png)](https://gitter.im/libhttpserver/community) [![ko-fi](https://www.ko-fi.com/img/donate_sm.png)](https://ko-fi.com/F1F5HY8B) From bda16ffed3dbfc000891f4727892e51eab599716 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Mon, 7 Jan 2019 12:33:59 -0800 Subject: [PATCH 363/623] Update issue templates --- .github/ISSUE_TEMPLATE/feature_request.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 00000000..a9833cf0 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,23 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: feature-request +assignees: etr + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe why the feature or enhancement you are proposing fits the library.** +A clear and concise explanation of the reason it fits into the library's mission. + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. From 6eb14dbbb0150968efbd568bc1be86b509bcc7f9 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Mon, 7 Jan 2019 13:15:37 -0800 Subject: [PATCH 364/623] Created bug_fix.md template --- .github/PULL_REQUEST_TEMPLATE/bug_fix.md | 59 ++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 .github/PULL_REQUEST_TEMPLATE/bug_fix.md diff --git a/.github/PULL_REQUEST_TEMPLATE/bug_fix.md b/.github/PULL_REQUEST_TEMPLATE/bug_fix.md new file mode 100644 index 00000000..edcbbff7 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE/bug_fix.md @@ -0,0 +1,59 @@ +### Requirements for Contributing a Bug Fix + +* Fill out the template below. Any pull request that does not include enough information to be reviewed in a timely manner may be closed at the maintainers' discretion. +* The pull request must only fix an existing bug. To contribute other changes, you must use a different template. You can see all templates at https://github.com/etr/libhttpserver/tree/master/.github/PULL_REQUEST_TEMPLATE. +* The pull request must update the test suite to demonstrate the changed functionality. +* After you create the pull request, all status checks must be pass before a maintainer reviews your contribution. For more details, please see https://github.com/etr/libhttpserver/tree/master/CONTRIBUTING.md#pull-requests. + +### Identify the Bug + + + +### Description of the Change + + + +### Alternate Designs + + + +### Possible Drawbacks + + + +### Verification Process + + + +### Release Notes + + From 68c73ce182f9a555a8a4725251ee8738c0d59d9d Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Mon, 7 Jan 2019 13:18:30 -0800 Subject: [PATCH 365/623] Create performance_improvement.md --- .../performance_improvement.md | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 .github/PULL_REQUEST_TEMPLATE/performance_improvement.md diff --git a/.github/PULL_REQUEST_TEMPLATE/performance_improvement.md b/.github/PULL_REQUEST_TEMPLATE/performance_improvement.md new file mode 100644 index 00000000..82ea175f --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE/performance_improvement.md @@ -0,0 +1,55 @@ +### Requirements for Contributing a Performance Improvement + +* Fill out the template below. Any pull request that does not include enough information to be reviewed in a timely manner may be closed at the maintainers' discretion. +* The pull request must only affect performance of existing functionality. To contribute other changes, you must use a different template. You can see all templates at https://github.com/etr/libhttpserver/tree/master/.github/PULL_REQUEST_TEMPLATE. +* After you create the pull request, all status checks must be pass before a maintainer reviews your contribution. For more details, please see https://github.com/etr/libhttpserver/tree/master/CONTRIBUTING.md#pull-requests. + +### Description of the Change + + + +### Quantitative Performance Benefits + + + +### Possible Drawbacks + + + +### Verification Process + + + +### Applicable Issues + + + +### Release Notes + + From 5ee43f0f25dfe04ca24fa8ea30b1feaca3dd73e0 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Mon, 7 Jan 2019 13:21:06 -0800 Subject: [PATCH 366/623] Create documentation.md --- .../PULL_REQUEST_TEMPLATE/documentation.md | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 .github/PULL_REQUEST_TEMPLATE/documentation.md diff --git a/.github/PULL_REQUEST_TEMPLATE/documentation.md b/.github/PULL_REQUEST_TEMPLATE/documentation.md new file mode 100644 index 00000000..2fc5a29e --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE/documentation.md @@ -0,0 +1,30 @@ +### Requirements for Contributing Documentation + +* Fill out the template below. Any pull request that does not include enough information to be reviewed in a timely manner may be closed at the maintainers' discretion. +* The pull request must only contribute documentation (for example, markdown files or API docs). To contribute other changes, you must use a different template. You can see all templates at https://github.com/etr/libhttpserver/tree/master/.github/PULL_REQUEST_TEMPLATE. + +### Description of the Change + + + +### Release Notes + + From 5948ef5f5ed7e5c59e6a780a6ad630b0c0250438 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Mon, 7 Jan 2019 13:24:38 -0800 Subject: [PATCH 367/623] Create feature_change.md --- .../PULL_REQUEST_TEMPLATE/feature_change.md | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 .github/PULL_REQUEST_TEMPLATE/feature_change.md diff --git a/.github/PULL_REQUEST_TEMPLATE/feature_change.md b/.github/PULL_REQUEST_TEMPLATE/feature_change.md new file mode 100644 index 00000000..826d72cc --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE/feature_change.md @@ -0,0 +1,62 @@ +### Requirements for Adding, Changing, or Removing a Feature + +* Fill out the template below. Any pull request that does not include enough information to be reviewed in a timely manner may be closed at the maintainers' discretion. +* The pull request must contribute a change that has been endorsed by the maintainer team. See details in the template below. +* The pull request must update the test suite to exercise the updated functionality. +* After you create the pull request, all status checks must be pass before a maintainer reviews your contribution. For more details, please see https://github.com/etr/libhttpserver/tree/master/CONTRIBUTING.md#pull-requests. + +### Issue or RFC Endorsed by Maintainers + + + +### Description of the Change + + + +### Alternate Designs + + + +### Possible Drawbacks + + + +### Verification Process + + + +### Release Notes + + From 15e7f65cc08d72e301c7c09b9737b1b58d16575b Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Mon, 7 Jan 2019 13:25:52 -0800 Subject: [PATCH 368/623] Create PULL_REQUEST_TEMPLATE.md --- PULL_REQUEST_TEMPLATE.md | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 PULL_REQUEST_TEMPLATE.md diff --git a/PULL_REQUEST_TEMPLATE.md b/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 00000000..a4d74934 --- /dev/null +++ b/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,10 @@ +Hello there! Welcome. Please follow the steps below to tell us about your contribution. + +1. Copy the correct template for your contribution + - Are you fixing a bug? Copy the template from https://raw.githubusercontent.com/etr/libhttpserver/master/.github/PULL_REQUEST_TEMPLATE/bug_fix.md + - Are you improving performance? Copy the template https://raw.githubusercontent.com/etr/libhttpserver/master/.github/PULL_REQUEST_TEMPLATE/performance_improvement.md + - Are you updating documentation? Copy the template from https://raw.githubusercontent.com/etr/libhttpserver/master/.github/PULL_REQUEST_TEMPLATE/documentation.md + - Are you changing functionality? Copy the template from https://raw.githubusercontent.com/etr/libhttpserver/master/.github/PULL_REQUEST_TEMPLATE/feature_change.md +2. Replace this text with the contents of the template +3. Fill in all sections of the template +4. Click "Create pull request" From 6b1f7a7e3e3cd1edb294327551a3786949081c30 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Mon, 7 Jan 2019 13:34:59 -0800 Subject: [PATCH 369/623] Create CONTRIBUTING.md --- CONTRIBUTING.md | 225 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 225 insertions(+) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..83fcfc29 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,225 @@ +# Contributing to libhttpserver + +:+1::tada: First off, thanks for taking the time to contribute! :tada::+1: + +The following is a set of guidelines for contributing to libhttpserver. These are mostly guidelines, not rules. Use your best judgment, and feel free to propose changes to this document in a pull request. + +#### Table Of Contents + +[Code of Conduct](#code-of-conduct) + +[I don't want to read this whole thing, I just have a question!!!](#i-dont-want-to-read-this-whole-thing-i-just-have-a-question) + +[How Can I Contribute?](#how-can-i-contribute) + * [Reporting Bugs](#reporting-bugs) + * [Suggesting Enhancements](#suggesting-enhancements) + * [Your First Code Contribution](#your-first-code-contribution) + * [Pull Requests](#pull-requests) + +[Styleguides](#styleguides) + * [Git Commit Messages](#git-commit-messages) + * [Documentation Styleguide](#documentation-styleguide) + +[Additional Notes](#additional-notes) + * [Issue and Pull Request Labels](#issue-and-pull-request-labels) + +## Code of Conduct + +This project and everyone participating in it is governed by the [Code of Conduct](CODE_OF_CONDUCT.md). By participating, you are expected to uphold this code. Please report unacceptable behavior to [etr@zencoders.org](mailto:etr@zencoders.org). + +## I don't want to read this whole thing I just have a question!!! + +> **Note:** Please don't file an issue to ask a question. You'll get faster results by using the resources below. + +We have an official community board where the community chimes in with helpful advice if you have questions. + +* [libhttpserver on Gitter](https://gitter.im/libhttpserver/community) + +## How Can I Contribute? + +### Reporting Bugs + +This section guides you through submitting a bug report for libhttpserver. Following these guidelines helps maintainers and the community understand your report :pencil:, reproduce the behavior :computer: :computer:, and find related reports :mag_right:. + +Before creating bug reports, please check [this list](#before-submitting-a-bug-report) as you might find out that you don't need to create one. When you are creating a bug report, please [include as many details as possible](#how-do-i-submit-a-good-bug-report). Fill out [the required template](https://github.com/etr/libhttpserver/blob/master/.github/ISSUE_TEMPLATE/bug_report.md), the information it asks for helps us resolve issues faster. + +> **Note:** If you find a **Closed** issue that seems like it is the same thing that you're experiencing, open a new issue and include a link to the original issue in the body of your new one. + +#### Before Submitting A Bug Report + +* **Try to debug the problem** You might be able to find the cause of the problem and fix things yourself. Most importantly, check if you can reproduce the problem in the latest version of libhttpserver (head on github). +* **Perform a [cursory search](https://github.com/search?l=&q=repo%3Aetr%2Flibhttpserver&type=Issues)** to see if the problem has already been reported. If it has **and the issue is still open**, add a comment to the existing issue instead of opening a new one. + +#### How Do I Submit A (Good) Bug Report? + +Bugs are tracked as [GitHub issues](https://guides.github.com/features/issues/). After you followed the steps above, create an issue and provide the following information by filling in [the template](https://github.com/etr/libhttpserver/blob/master/.github/ISSUE_TEMPLATE/bug_report.md). + +Explain the problem and include additional details to help maintainers reproduce the problem: + +* **Use a clear and descriptive title** for the issue to identify the problem. +* **Describe the exact steps which reproduce the problem** in as many details as possible. When listing steps, **don't just say what you did, but explain how you did it**. +* **Provide specific examples to demonstrate the steps**. Include links to files or GitHub projects, or copy/pasteable snippets, which you use in those examples. If you're providing snippets in the issue, use [Markdown code blocks](https://help.github.com/articles/markdown-basics/#multiple-lines). +* **Describe the behavior you observed after following the steps** and point out what exactly is the problem with that behavior. +* **Explain which behavior you expected instead and why.** +* **If you're reporting a crash**, include a crash report with a stack trace from the operating system. Include these in the issue in a [code block](https://help.github.com/articles/markdown-basics/#multiple-lines), a [file attachment](https://help.github.com/articles/file-attachments-on-issues-and-pull-requests/), or put it in a [gist](https://gist.github.com/) and provide link to that gist. +* **Consider attaching a simple snipped reproducing the problem. ** +* **If the problem is related to performance or memory**, include a CPU profile capture with your report. + +Provide more context by answering these questions: + +* **Did the problem start happening recently** (e.g. after updating to a new version of libhttpserver) or was this always a problem? +* If the problem started happening recently, **can you reproduce the problem in an older version of libhttpserver?** What's the most recent version in which the problem doesn't happen? You can download older versions of libhttpserver from [the releases page](https://github.com/etr/libhttpserver/releases). +* **Can you reliably reproduce the issue?** If not, provide details about how often the problem happens and under which conditions it normally happens. + +Include details about your configuration and environment: + +* **Which version of libhttpserver are you using?** +* **What's the name and version of the OS you're using (e.g. "uname -a" on linux) **? +* **What's the version of libmicrohttpd that you have installed? ** +* **Have you installed the libraries (both libhttpserver and libmicrohttpd) manually or through package manager? ** +* **Which options did you use when compiling? ** +* **What compiler version and version of autotools did you use? ** + +### Feature Requests and Enhancements + +This section guides you through submitting an enhancement suggestion for libhttpserver, including completely new features and minor improvements to existing functionality. Following these guidelines helps maintainers and the community understand your suggestion and find related suggestions. + +Before creating enhancement suggestions, please check [this list](#before-submitting-an-enhancement-suggestion) as you might find out that you don't need to create one. When you are creating an enhancement suggestion, please [include as many details as possible](#how-do-i-submit-a-good-enhancement-suggestion). Fill in [the template](https://github.com/etr/libhttpserver/blob/master/.github/ISSUE_TEMPLATE/feature_request.md). + +#### Before Submitting An Enhancement Suggestion or a Feature Request + +* **Perform a [cursory search](https://github.com/search?l=&q=repo%3Aetr%2Flibhttpserver&type=Issues)** to see if the enhancement has already been suggested. If it has, add a comment to the existing issue instead of opening a new one. + +#### How Do I Submit A (Good) Feature Request / Enhancement Suggestion? + +Enhancement suggestions are tracked as [GitHub issues](https://guides.github.com/features/issues/). Create an issue on that repository and provide the following information: + +* **Use a clear and descriptive title** for the issue to identify the suggestion. +* **Provide a step-by-step description of the suggested enhancement** in as many details as possible. +* **Provide a specific example to demonstrate the new feature**. +* **Describe the current behavior** and **explain which behavior you expected instead** and why. +* **Describe which alternatives you have considered**. +* **Explain why this enhancement would be useful** to most users and **why it fits the mission of the library**. + +### Your First Code Contribution + +Unsure where to begin contributing to libhttpserver? You can start by looking through these `beginner` and `help-wanted` issues: + +* [Beginner issues][beginner] - issues which should only require a few lines of code, and a test or two. +* [Help wanted issues][help-wanted] - issues which should be a bit more involved than `beginner` issues. + +Both issue lists are sorted by total number of comments. While not perfect, number of comments is a reasonable proxy for impact a given change will have. + +### Pull Requests + +The process described here has several goals: + +- Maintain libhttpserver's quality +- Fix problems that are important to users +- Engage the community in working toward the best possible solution +- Enable a sustainable system for maintainers to review contributions + +Please follow these steps to have your contribution considered by the maintainers: + +1. Follow all instructions in [the template](https://github.com/etr/libhttpserver/blob/master/PULL_REQUEST_TEMPLATE.md) +2. Follow the [styleguides](#styleguides) +3. After you submit your pull request, verify that all [status checks](https://help.github.com/articles/about-status-checks/) are passing
What if the status checks are failing?If a status check is failing, and you believe that the failure is unrelated to your change, please leave a comment on the pull request explaining why you believe the failure is unrelated. A maintainer will re-run the status check for you. If we conclude that the failure was a false positive, then we will open an issue to track that problem with our status check suite.
+ +While the prerequisites above must be satisfied prior to having your pull request reviewed, the reviewer(s) may ask you to complete additional design work, tests, or other changes before your pull request can be ultimately accepted. + +## Styleguides + +### Git Commit Messages + +* Limit the first line to 80 characters or less. +* Add a concise description of what your change does. +* Reference issues and pull requests liberally after the first line. + +### Documentation Styleguide + +* Use [Markdown](https://daringfireball.net/projects/markdown). + +## Additional Notes + +### Issue and Pull Request Labels + +This section lists the labels we use to help us track and manage issues and pull requests. + +[GitHub search](https://help.github.com/articles/searching-issues/) makes it easy to use labels for finding groups of issues or pull requests you're interested in. We encourage you to read about [other search filters](https://help.github.com/articles/searching-issues/) which will help you write more focused queries. + +The labels are loosely grouped by their purpose, but it's not required that every issue have a label from every group or that an issue can't have more than one label from the same group. + +Please open an issue on `etr/libhttpserver` if you have suggestions for new labels. + +#### Type of Issue and Issue State + +| Label name | `etr/libhttpserver` :mag_right: | Description | +| --- | --- | --- | +| `feature-request` | [search][search-libhttpserver-repo-label-feature-request] | Feature requests or enhancements. | +| `bug` | [search][search-libhttpserver-repo-label-bug] | Confirmed bugs or reports that are very likely to be bugs. | +| `question` | [search][search-libhttpserver-repo-label-question] | Questions more than bug reports or feature requests (e.g. how do I do X). | +| `feedback` | [search][search-libhttpserver-repo-label-feedback] | General feedback more than bug reports or feature requests. | +| `help-wanted` | [search][search-libhttpserver-repo-label-help-wanted] | The maintainer would appreciate help from the community in resolving these issues. | +| `beginner` | [search][search-libhttpserver-repo-label-beginner] | Less complex issues which would be good first issues to work on for users who want to contribute to libhttpserver. | +| `more-information-needed` | [search][search-libhttpserver-repo-label-more-information-needed] | More information needs to be collected about these problems or feature requests (e.g. steps to reproduce). | +| `needs-reproduction` | [search][search-libhttpserver-repo-label-needs-reproduction] | Likely bugs, but haven't been reliably reproduced. | +| `blocked` | [search][search-libhttpserver-repo-label-blocked] | Issues blocked on other issues. | +| `duplicate` | [search][search-libhttpserver-repo-label-duplicate] | Issues which are duplicates of other issues, i.e. they have been reported before. | +| `wontfix` | [search][search-libhttpserver-repo-label-wontfix] | The maintainers have decided not to fix these issues for now, either because they're working as intended or for some other reason. | +| `invalid` | [search][search-libhttpserver-repo-label-invalid] | Issues which aren't valid (e.g. user errors). | + +#### Topic Categories + +| Label name | `etr/libhttpserver` :mag_right: | Description | +| --- | --- | --- | +| `windows` | [search][search-libhttpserver-repo-label-windows] | Related to Windows. | +| `linux` | [search][search-libhttpserver-repo-label-linux] | Related to Linux. | +| `mac` | [search][search-libhttpserver-repo-label-mac] | Related to macOS. | +| `documentation` | [search][search-libhttpserver-repo-label-documentation] | Related to any type of documentation. | +| `performance` | [search][search-libhttpserver-repo-label-performance] | Related to performance. | +| `security` | [search][search-libhttpserver-repo-label-security] | Related to security. | +| `api` | [search][search-libhttpserver-repo-label-api] | Related to libhttpserver's public APIs. | +| `git` | [search][search-libhttpserver-repo-label-git] | Related to Git functionality (e.g. problems with gitignore files or with showing the correct file status). | + +#### Pull Request Labels + +| Label name | `etr/libhttpserver` :mag_right: | Description | +| --- | --- | --- | +| `work-in-progress` | [search][search-libhttpserver-repo-label-work-in-progress] | Pull requests which are still being worked on, more changes will follow. | +| `needs-review` | [search][search-libhttpserver-repo-label-needs-review] | Pull requests which need code review, and approval from maintainers. | +| `under-review` | [search][search-libhttpserver-repo-label-under-review] | Pull requests being reviewed by maintainers. | +| `requires-changes` | [search][search-libhttpserver-repo-label-requires-changes] | Pull requests which need to be updated based on review comments and then reviewed again. | +| `needs-testing` | [search][search-libhttpserver-repo-label-needs-testing] | Pull requests which need manual testing. | + +[search-libhttpserver-repo-label-feature-request]: https://github.com/search?q=is%3Aopen+is%3Aissue+repo%3Aetr%23Flibhttpserver+label%3Afeature-request +[search-libhttpserver-repo-label-bug]: https://github.com/search?q=is%3Aopen+is%3Aissue+repo%3Aetr%23Flibhttpserver+label%3Abug +[search-libhttpserver-repo-label-question]: https://github.com/search?q=is%3Aopen+is%3Aissue+repo%3Aetr%23Flibhttpserver+label%3Aquestion +[search-libhttpserver-repo-label-feedback]: https://github.com/search?q=is%3Aopen+is%3Aissue+repo%3Aetr%23Flibhttpserver+label%3Afeedback +[search-libhttpserver-repo-label-help-wanted]: https://github.com/search?q=is%3Aopen+is%3Aissue+repo%3Aetr%23Flibhttpserver+label%3Ahelp-wanted +[search-libhttpserver-repo-label-beginner]: https://github.com/search?q=is%3Aopen+is%3Aissue+repo%3Aetr%23Flibhttpserver+label%3Abeginner +[search-libhttpserver-repo-label-more-information-needed]: https://github.com/search?q=is%3Aopen+is%3Aissue+repo%3Aetr%23Flibhttpserver+label%3Amore-information-needed +[search-libhttpserver-repo-label-needs-reproduction]: https://github.com/search?q=is%3Aopen+is%3Aissue+repo%3Aetr%23Flibhttpserver+label%3Aneeds-reproduction +[search-libhttpserver-repo-label-triage-help-needed]: https://github.com/search?q=is%3Aopen+is%3Aissue+repo%3Aetr%23Flibhttpserver+label%3Atriage-help-needed +[search-libhttpserver-repo-label-windows]: https://github.com/search?q=is%3Aopen+is%3Aissue+repo%3Aetr%23Flibhttpserver+label%3Awindows +[search-libhttpserver-repo-label-linux]: https://github.com/search?q=is%3Aopen+is%3Aissue+repo%3Aetr%23Flibhttpserver+label%3Alinux +[search-libhttpserver-repo-label-mac]: https://github.com/search?q=is%3Aopen+is%3Aissue+repo%3Aetr%23Flibhttpserver+label%3Amac +[search-libhttpserver-repo-label-documentation]: https://github.com/search?q=is%3Aopen+is%3Aissue+repo%3Aetr%23Flibhttpserver+label%3Adocumentation +[search-libhttpserver-repo-label-performance]: https://github.com/search?q=is%3Aopen+is%3Aissue+repo%3Aetr%23Flibhttpserver+label%3Aperformance +[search-libhttpserver-repo-label-security]: https://github.com/search?q=is%3Aopen+is%3Aissue+repo%3Aetr%23Flibhttpserver+label%3Asecurity +[search-libhttpserver-repo-label-api]: https://github.com/search?q=is%3Aopen+is%3Aissue+repo%3Aetr%23Flibhttpserver+label%3Aapi +[search-libhttpserver-repo-label-git]: https://github.com/search?q=is%3Aopen+is%3Aissue+repo%3Aetr%23Flibhttpserver+label%3Agit +[search-libhttpserver-repo-label-blocked]: https://github.com/search?q=is%3Aopen+is%3Aissue+repo%3Aetr%23Flibhttpserver+label%3Ablocked +[search-libhttpserver-repo-label-duplicate]: https://github.com/search?q=is%3Aopen+is%3Aissue+repo%3Aetr%23Flibhttpserver+label%3Aduplicate +[search-libhttpserver-repo-label-wontfix]: https://github.com/search?q=is%3Aopen+is%3Aissue+repo%3Aetr%23Flibhttpserver+label%3Awontfix +[search-libhttpserver-repo-label-invalid]: https://github.com/search?q=is%3Aopen+is%3Aissue+repo%3Aetr%23Flibhttpserver+label%3Ainvalid +[search-libhttpserver-repo-label-build-error]: https://github.com/search?q=is%3Aopen+is%3Aissue+repo%3Aetr%23Flibhttpserver+label%3Abuild-error +[search-libhttpserver-repo-label-installer]: https://github.com/search?q=is%3Aopen+is%3Aissue+repo%3Aetr%23Flibhttpserver+label%3Ainstaller +[search-libhttpserver-repo-label-deprecation-help]: https://github.com/search?q=is%3Aopen+is%3Aissue+repo%3Aetr%23Flibhttpserver+label%3Adeprecation-help +[search-libhttpserver-repo-label-work-in-progress]: https://github.com/search?q=is%3Aopen+is%3Apr+repo%3Aetr%23Flibhttpserver+label%3Awork-in-progress +[search-libhttpserver-repo-label-needs-review]: https://github.com/search?q=is%3Aopen+is%3Apr+repo%3Aetr%23Flibhttpserver+label%3Aneeds-review +[search-libhttpserver-repo-label-under-review]: https://github.com/search?q=is%3Aopen+is%3Apr+repo%3Aetr%23Flibhttpserver+label%3Aunder-review +[search-libhttpserver-repo-label-requires-changes]: https://github.com/search?q=is%3Aopen+is%3Apr+repo%3Aetr%23Flibhttpserver+label%3Arequires-changes +[search-libhttpserver-repo-label-needs-testing]: https://github.com/search?q=is%3Aopen+is%3Apr+repo%3Aetr%23Flibhttpserver+label%3Aneeds-testing + +[beginner]:https://github.com/search?utf8=%E2%9C%93&q=is%3Aopen+is%3Aissue+label%3Abeginner+label%3Ahelp-wanted+user%3Aetr+sort%3Acomments-desc +[help-wanted]:https://github.com/search?q=is%3Aopen+is%3Aissue+label%3Ahelp-wanted+user%3Aetr+sort%3Acomments-desc+-label%3Abeginner From 3e9a4b9bc927ff2dbebfad8c152b41d445c33c6e Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Mon, 7 Jan 2019 13:49:27 -0800 Subject: [PATCH 370/623] Update CONTRIBUTING.md --- CONTRIBUTING.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 83fcfc29..043ad528 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -175,6 +175,8 @@ Please open an issue on `etr/libhttpserver` if you have suggestions for new labe | `windows` | [search][search-libhttpserver-repo-label-windows] | Related to Windows. | | `linux` | [search][search-libhttpserver-repo-label-linux] | Related to Linux. | | `mac` | [search][search-libhttpserver-repo-label-mac] | Related to macOS. | +| `travis` | [search][search-libhttpserver-repo-label-travis] | Related to travis and CI in general. | +| `tests` | [search][search-libhttpserver-repo-label-tests] | Related to tests (add tests, fix tests, etc...). | | `documentation` | [search][search-libhttpserver-repo-label-documentation] | Related to any type of documentation. | | `performance` | [search][search-libhttpserver-repo-label-performance] | Related to performance. | | `security` | [search][search-libhttpserver-repo-label-security] | Related to security. | @@ -203,6 +205,8 @@ Please open an issue on `etr/libhttpserver` if you have suggestions for new labe [search-libhttpserver-repo-label-windows]: https://github.com/search?q=is%3Aopen+is%3Aissue+repo%3Aetr%23Flibhttpserver+label%3Awindows [search-libhttpserver-repo-label-linux]: https://github.com/search?q=is%3Aopen+is%3Aissue+repo%3Aetr%23Flibhttpserver+label%3Alinux [search-libhttpserver-repo-label-mac]: https://github.com/search?q=is%3Aopen+is%3Aissue+repo%3Aetr%23Flibhttpserver+label%3Amac +[search-libhttpserver-repo-label-travis]: https://github.com/search?q=is%3Aopen+is%3Aissue+repo%3Aetr%23Flibhttpserver+label%3Atravis +[search-libhttpserver-repo-label-tests]: https://github.com/search?q=is%3Aopen+is%3Aissue+repo%3Aetr%23Flibhttpserver+label%3Atests [search-libhttpserver-repo-label-documentation]: https://github.com/search?q=is%3Aopen+is%3Aissue+repo%3Aetr%23Flibhttpserver+label%3Adocumentation [search-libhttpserver-repo-label-performance]: https://github.com/search?q=is%3Aopen+is%3Aissue+repo%3Aetr%23Flibhttpserver+label%3Aperformance [search-libhttpserver-repo-label-security]: https://github.com/search?q=is%3Aopen+is%3Aissue+repo%3Aetr%23Flibhttpserver+label%3Asecurity From 3e7cbb475f93ae07a36d32e54abcca367aab40aa Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sat, 12 Jan 2019 01:39:52 +0000 Subject: [PATCH 371/623] Remove autodelete parameter from http_response (unused) --- src/http_response.cpp | 1 - src/httpserver/http_response.hpp | 7 ------- src/httpserver/http_response_builder.hpp | 11 ++--------- 3 files changed, 2 insertions(+), 17 deletions(-) diff --git a/src/http_response.cpp b/src/http_response.cpp index 37893476..6e68a3fc 100644 --- a/src/http_response.cpp +++ b/src/http_response.cpp @@ -39,7 +39,6 @@ class webserver; http_response::http_response(const http_response_builder& builder): content(builder._content_hook), response_code(builder._response_code), - autodelete(builder._autodelete), realm(builder._realm), opaque(builder._opaque), reload_nonce(builder._reload_nonce), diff --git a/src/httpserver/http_response.hpp b/src/httpserver/http_response.hpp index 8f1f66cf..12591b83 100644 --- a/src/httpserver/http_response.hpp +++ b/src/httpserver/http_response.hpp @@ -75,7 +75,6 @@ class http_response http_response(const http_response& b): content(b.content), response_code(b.response_code), - autodelete(b.autodelete), realm(b.realm), opaque(b.opaque), reload_nonce(b.reload_nonce), @@ -199,11 +198,6 @@ class http_response return 0; } - bool is_autodelete() const - { - return autodelete; - } - const std::vector& get_topics() const { return this->topics; @@ -217,7 +211,6 @@ class http_response std::string content; int response_code; - bool autodelete; std::string realm; std::string opaque; bool reload_nonce; diff --git a/src/httpserver/http_response_builder.hpp b/src/httpserver/http_response_builder.hpp index 4f047a16..600b8ca6 100644 --- a/src/httpserver/http_response_builder.hpp +++ b/src/httpserver/http_response_builder.hpp @@ -70,12 +70,10 @@ class http_response_builder explicit http_response_builder( const std::string& content_hook, int response_code = 200, - const std::string& content_type = "text/plain", - bool autodelete = true + const std::string& content_type = "text/plain" ): _content_hook(content_hook), _response_code(response_code), - _autodelete(autodelete), _realm(""), _opaque(""), _reload_nonce(false), @@ -96,12 +94,10 @@ class http_response_builder http_response_builder( const byte_string& content_hook, int response_code = 200, - const std::string& content_type = "text/plain", - bool autodelete = true + const std::string& content_type = "text/plain" ): _content_hook(std::string(content_hook.get_content_hook(), content_hook.get_content_length())), _response_code(response_code), - _autodelete(autodelete), _realm(""), _opaque(""), _reload_nonce(false), @@ -122,7 +118,6 @@ class http_response_builder http_response_builder(const http_response_builder& b): _content_hook(b._content_hook), _response_code(b._response_code), - _autodelete(b._autodelete), _realm(b._realm), _opaque(b._opaque), _reload_nonce(b._reload_nonce), @@ -143,7 +138,6 @@ class http_response_builder { _content_hook = b._content_hook; _response_code = b._response_code; - _autodelete = b._autodelete; _realm = b._realm; _opaque = b._opaque; _reload_nonce = b._reload_nonce; @@ -247,7 +241,6 @@ class http_response_builder private: std::string _content_hook; int _response_code; - bool _autodelete; std::string _realm; std::string _opaque; bool _reload_nonce; From f58e345e75c89df7d4f14b9865124bade99560cc Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sat, 12 Jan 2019 02:30:56 +0000 Subject: [PATCH 372/623] Removed the comet subsystem entirely. --- examples/comet.cpp | 66 --------- src/Makefile.am | 4 +- src/details/comet_manager.cpp | 106 -------------- src/http_response.cpp | 42 ------ src/httpserver/details/comet_manager.hpp | 73 ---------- src/httpserver/http_response.hpp | 4 - src/httpserver/http_response_builder.hpp | 20 --- src/httpserver/webserver.hpp | 13 -- src/webserver.cpp | 30 +--- test/Makefile.am | 3 +- test/integ/comet_tests.cpp | 168 ----------------------- 11 files changed, 4 insertions(+), 525 deletions(-) delete mode 100755 examples/comet.cpp delete mode 100644 src/details/comet_manager.cpp delete mode 100644 src/httpserver/details/comet_manager.hpp delete mode 100644 test/integ/comet_tests.cpp diff --git a/examples/comet.cpp b/examples/comet.cpp deleted file mode 100755 index 33eb3d7c..00000000 --- a/examples/comet.cpp +++ /dev/null @@ -1,66 +0,0 @@ -/* - This file is part of libhttpserver - Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 - USA -*/ - -#include -#include -#include - -using namespace httpserver; - -std::string topics_array[] = { "topic_1" }; -std::vector topics(topics_array, topics_array + sizeof(topics_array) / sizeof(std::string)); - -class comet_send_resource : public http_resource { - public: - const http_response render(const http_request& req) - { - return http_response_builder("Hi", 200).long_polling_send_response(topics_array[0]); - } -}; - -class comet_listen_resource : public http_resource { - public: - const http_response render(const http_request& req) - { - return http_response_builder("OK", 200).long_polling_receive_response(topics); - } -}; - -int main() -{ - //it is possible to create a webserver passing a great number of parameters. - //In this case we are just passing the port and the number of thread running. - webserver ws = create_webserver(8080).comet(); - - comet_send_resource csr; - comet_listen_resource clr; - //this way we are registering the hello_world_resource to answer for the endpoint - //"/hello". The requested method is called (if the request is a GET we call the render_GET - //method. In case that the specific render method is not implemented, the generic "render" - //method is called. - ws.register_resource("/send", &csr, true); - ws.register_resource("/listen", &clr, true); - - //This way we are putting the created webserver in listen. We pass true in order to have - //a blocking call; if we want the call to be non-blocking we can just pass false to the - //method. - ws.start(true); - return 0; -} diff --git a/src/Makefile.am b/src/Makefile.am index 962d34ad..3cc21f8a 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -19,8 +19,8 @@ AM_CPPFLAGS = -I../ -I$(srcdir)/httpserver/ METASOURCES = AUTO lib_LTLIBRARIES = libhttpserver.la -libhttpserver_la_SOURCES = string_utilities.cpp webserver.cpp http_utils.cpp http_request.cpp http_response.cpp http_resource.cpp details/comet_manager.cpp details/http_endpoint.cpp -noinst_HEADERS = httpserver/string_utilities.hpp httpserver/details/modded_request.hpp httpserver/details/http_response_ptr.hpp httpserver/details/comet_manager.hpp gettext.h +libhttpserver_la_SOURCES = string_utilities.cpp webserver.cpp http_utils.cpp http_request.cpp http_response.cpp http_resource.cpp details/http_endpoint.cpp +noinst_HEADERS = httpserver/string_utilities.hpp httpserver/details/modded_request.hpp httpserver/details/http_response_ptr.hpp gettext.h nobase_include_HEADERS = httpserver.hpp httpserver/create_webserver.hpp httpserver/webserver.hpp httpserver/http_utils.hpp httpserver/details/http_endpoint.hpp httpserver/http_request.hpp httpserver/http_response.hpp httpserver/http_resource.hpp httpserver/binders.hpp httpserver/http_response_builder.hpp AM_CXXFLAGS += -fPIC -Wall diff --git a/src/details/comet_manager.cpp b/src/details/comet_manager.cpp deleted file mode 100644 index 0a1fbd47..00000000 --- a/src/details/comet_manager.cpp +++ /dev/null @@ -1,106 +0,0 @@ -/* - This file is part of libhttpserver - Copyright (C) 2011-2019 Sebastiano Merlino - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 - USA -*/ - -#include -#include -#include "details/comet_manager.hpp" -#include - -using namespace std; - -namespace httpserver -{ - -namespace details -{ - -comet_manager::comet_manager() -{ -} - -comet_manager::~comet_manager() -{ -} - -void comet_manager::send_message_to_topic (const string& topic, const string& message) -{ - map >::const_iterator it = this->q_topics.find(topic); - if (it == this->q_topics.end()) return; - - //copying value guarantees we iterate on a copy. Even if the original set is modified we are safe and so we stay lock free. - const set connections = it->second; - - for (set::const_iterator c_it = connections.begin(); c_it != connections.end(); ++c_it) - { - map >::iterator message_queue_it = this->q_messages.find(*c_it); - if (message_queue_it == this->q_messages.end()) continue; - - message_queue_it->second.push_back(message); - - MHD_resume_connection(*c_it); - } -} - -void comet_manager::register_to_topics (const vector& topics, MHD_Connection* connection_id) -{ - for(vector::const_iterator it = topics.begin(); it != topics.end(); ++it) - { - this->q_topics[*it].insert(connection_id); // (1) Can this cause problems in concurrency with (2) ? - } - this->q_subscriptions.insert(make_pair(connection_id, set(topics.begin(), topics.end()))); - this->q_messages.insert(make_pair(connection_id, deque())); -} - -size_t comet_manager::read_message(MHD_Connection* connection_id, string& message) -{ - if(this->q_messages[connection_id].empty()) - { - MHD_suspend_connection(connection_id); - return 0; - } - - deque& t_deq = this->q_messages[connection_id]; - message.assign(t_deq.front()); - t_deq.pop_front(); - return message.size(); -} - -void comet_manager::complete_request(MHD_Connection* connection_id) -{ - this->q_messages.erase(connection_id); - - map >::iterator topics_it = this->q_subscriptions.find(connection_id); - if (topics_it == q_subscriptions.end()) return; - set topics = topics_it->second; - - for(set::const_iterator it = topics.begin(); it != topics.end(); ++it) - { - map >::iterator connections_it = this->q_topics.find(*it); - if (connections_it == this->q_topics.end()) continue; - - connections_it->second.erase(connection_id); - if (connections_it->second.size() == 0) this->q_topics.erase(*it); // (2) - } - q_subscriptions.erase(connection_id); -} - -} //details - -} //httpserver diff --git a/src/http_response.cpp b/src/http_response.cpp index 6e68a3fc..bbf9e81d 100644 --- a/src/http_response.cpp +++ b/src/http_response.cpp @@ -196,47 +196,6 @@ void http_response::decorate_response_deferred(MHD_Response* response) static_cast(this)->decorate_response(response); } -void http_response::get_raw_response_lp_receive( - MHD_Response** response, - webserver* ws -) -{ - this->ws = ws; - this->connection_id = this->underlying_connection; - - *response = MHD_create_response_from_callback(MHD_SIZE_UNKNOWN, 80, - &http_response::data_generator, (void*) this, NULL); - - ws->register_to_topics( - topics, - connection_id - ); -} - -ssize_t http_response::data_generator( - void* cls, - uint64_t pos, - char* buf, - size_t max -) -{ - http_response* _this = static_cast(cls); - - string message; - size_t size = _this->ws->read_message(_this->connection_id, message); - memcpy(buf, message.c_str(), size); - return size; -} - -void http_response::get_raw_response_lp_send( - MHD_Response** response, - webserver* ws -) -{ - http_response::get_raw_response_str(response, ws); - ws->send_message_to_topic(send_topic, content); -} - std::ostream &operator<< (std::ostream &os, const http_response &r) { os << "Response [response_code:" << r.response_code << "]" << std::endl; @@ -248,5 +207,4 @@ std::ostream &operator<< (std::ostream &os, const http_response &r) return os; } - }; diff --git a/src/httpserver/details/comet_manager.hpp b/src/httpserver/details/comet_manager.hpp deleted file mode 100644 index 067a4279..00000000 --- a/src/httpserver/details/comet_manager.hpp +++ /dev/null @@ -1,73 +0,0 @@ -/* - This file is part of libhttpserver - Copyright (C) 2011-2019 Sebastiano Merlino - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 - USA -*/ - -#if !defined (_HTTPSERVER_HPP_INSIDE_) && !defined (HTTPSERVER_COMPILATION) -#error "Only or can be included directly." -#endif - -#ifndef _COMET_MANAGER_HPP_ -#define _COMET_MANAGER_HPP_ - -#include -#include -#include -#include -#include -#include -#include "http_utils.hpp" - -namespace httpserver -{ - -class webserver; - -namespace details -{ - -class comet_manager -{ - private: - comet_manager(); - - ~comet_manager(); - - void send_message_to_topic(const std::string& topic, const std::string& message); - - void register_to_topics(const std::vector& topics, MHD_Connection* connection_id); - - size_t read_message(MHD_Connection* connection_id, std::string& message); - - void complete_request(MHD_Connection* connection_id); - - comet_manager(const comet_manager&) - { - } - - std::map > q_messages; - std::map > q_topics; - std::map > q_subscriptions; - friend class httpserver::webserver; -}; - -} //details - -} //httpserver - -#endif //_COMET_MANAGER_HPP_ diff --git a/src/httpserver/http_response.hpp b/src/httpserver/http_response.hpp index 12591b83..c55012ff 100644 --- a/src/httpserver/http_response.hpp +++ b/src/httpserver/http_response.hpp @@ -239,10 +239,6 @@ class http_response void get_raw_response_file(MHD_Response** res, webserver* ws = 0x0); void get_raw_response_switch_r(MHD_Response** res, webserver* ws = 0x0); - void get_raw_response_lp_receive(MHD_Response** res, - webserver* ws = 0x0); - - void get_raw_response_lp_send(MHD_Response** res, webserver* ws = 0x0); void get_raw_response_deferred(MHD_Response** res, webserver* ws = 0x0); void decorate_response_str(MHD_Response* res); void decorate_response_deferred(MHD_Response* res); diff --git a/src/httpserver/http_response_builder.hpp b/src/httpserver/http_response_builder.hpp index 600b8ca6..7755fc64 100644 --- a/src/httpserver/http_response_builder.hpp +++ b/src/httpserver/http_response_builder.hpp @@ -189,26 +189,6 @@ class http_response_builder return *this; } - http_response_builder& long_polling_receive_response( - const std::vector& topics, - int keepalive_secs = -1, - std::string keepalive_msg = "" - ) - { - _topics = topics; - _keepalive_secs = keepalive_secs; - _keepalive_msg = keepalive_msg; - _get_raw_response = &http_response::get_raw_response_lp_receive; - return *this; - } - - http_response_builder& long_polling_send_response(const std::string& send_topic) - { - _send_topic = send_topic; - _get_raw_response = &http_response::get_raw_response_lp_send; - return *this; - } - http_response_builder& deferred_response(cycle_callback_ptr cycle_callback) { _cycle_callback = cycle_callback; diff --git a/src/httpserver/webserver.hpp b/src/httpserver/webserver.hpp index 0d9f8fa6..1109bbc5 100644 --- a/src/httpserver/webserver.hpp +++ b/src/httpserver/webserver.hpp @@ -61,7 +61,6 @@ struct httpserver_ska; namespace details { struct daemon_item; struct modded_request; - class comet_manager; } /** @@ -110,16 +109,6 @@ class webserver void unban_ip(const std::string& ip); void disallow_ip(const std::string& ip); - void send_message_to_topic(const std::string& topic, - const std::string& message - ); - void register_to_topics(const std::vector& topics, - MHD_Connection* connection_id - ); - size_t read_message(MHD_Connection* connection_id, - std::string& message - ); - log_access_ptr get_access_logger() const { return this->log_access; @@ -202,8 +191,6 @@ class webserver std::vector daemons; std::vector threads; - details::comet_manager* internal_comet_manager; - static void* select(void* self); static void* cleaner(void* self); diff --git a/src/webserver.cpp b/src/webserver.cpp index c137ffc3..87a54d20 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -52,7 +52,6 @@ #include "details/http_endpoint.hpp" #include "string_utilities.hpp" #include "create_webserver.hpp" -#include "details/comet_manager.hpp" #include "webserver.hpp" #include "details/modded_request.hpp" @@ -176,8 +175,7 @@ webserver::webserver(const create_webserver& params): not_found_resource(params._not_found_resource), method_not_allowed_resource(params._method_not_allowed_resource), internal_error_resource(params._internal_error_resource), - next_to_choose(0), - internal_comet_manager(new details::comet_manager()) + next_to_choose(0) { ignore_sigpipe(); pthread_mutex_init(&mutexwait, NULL); @@ -191,7 +189,6 @@ webserver::~webserver() pthread_mutex_destroy(&mutexwait); pthread_rwlock_destroy(&runguard); pthread_cond_destroy(&mutexcond); - delete internal_comet_manager; } void webserver::sweet_kill() @@ -209,8 +206,6 @@ void webserver::request_completed ( details::modded_request* mr = static_cast(*con_cls); if (mr == 0x0) return; - if (mr->ws != 0x0) mr->ws->internal_comet_manager->complete_request(mr->dhrs->connection_id); - delete mr; mr = 0x0; } @@ -1016,27 +1011,4 @@ int webserver::answer_to_connection(void* cls, MHD_Connection* connection, return body ? static_cast(cls)->bodyfull_requests_answer_first_step(connection, mr) : static_cast(cls)->bodyless_requests_answer(connection, method, version, mr); } -void webserver::send_message_to_topic( - const std::string& topic, - const std::string& message -) -{ - internal_comet_manager->send_message_to_topic(topic, message); -} - -void webserver::register_to_topics( - const std::vector& topics, - MHD_Connection* connection_id -) -{ - internal_comet_manager->register_to_topics(topics, connection_id); -} - -size_t webserver::read_message(MHD_Connection* connection_id, - std::string& message -) -{ - return internal_comet_manager->read_message(connection_id, message); -} - }; diff --git a/test/Makefile.am b/test/Makefile.am index 30bc5000..253391bd 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -19,7 +19,7 @@ LDADD = $(top_builddir)/src/libhttpserver.la AM_CPPFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/src/httpserver/ METASOURCES = AUTO -check_PROGRAMS = basic http_utils threaded string_utilities http_endpoint ban_system ws_start_stop authentication comet_tests deferred +check_PROGRAMS = basic http_utils threaded string_utilities http_endpoint ban_system ws_start_stop authentication deferred MOSTLYCLEANFILES = *.gcda *.gcno *.gcov @@ -28,7 +28,6 @@ threaded_SOURCES = integ/threaded.cpp ban_system_SOURCES = integ/ban_system.cpp ws_start_stop_SOURCES = integ/ws_start_stop.cpp authentication_SOURCES = integ/authentication.cpp -comet_tests_SOURCES = integ/comet_tests.cpp deferred_SOURCES = integ/deferred.cpp http_utils_SOURCES = unit/http_utils_test.cpp string_utilities_SOURCES = unit/string_utilities_test.cpp diff --git a/test/integ/comet_tests.cpp b/test/integ/comet_tests.cpp deleted file mode 100644 index 26f04480..00000000 --- a/test/integ/comet_tests.cpp +++ /dev/null @@ -1,168 +0,0 @@ -/* - This file is part of libhttpserver - Copyright (C) 2011-2019 Sebastiano Merlino - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 - USA -*/ - -#if defined(__MINGW32__) || defined(__CYGWIN32__) -#define _WINDOWS -#undef _WIN32_WINNT -#define _WIN32_WINNT 0x600 -#include -#include -#else -#include -#endif - -#include "littletest.hpp" -#include -#include -#include -#include "httpserver.hpp" -#include -#include - -#define MY_OPAQUE "11733b200778ce33060f31c9af70a870ba96ddd4" - -using namespace std; -using namespace httpserver; - -struct closure_type -{ - std::string s; - int counter; -}; - -size_t writefunc_listener(void *ptr, size_t size, size_t nmemb, closure_type* cls) -{ - char* new_content = (char*) ptr; - if (cls->counter > 0) - { - return 0; - } - - cls->counter++; - std::string prefix = "RECEIVED: "; - cls->s.insert(0, prefix); - cls->s.append(new_content, size*nmemb); - return size*nmemb; -} - -size_t writefunc(void *ptr, size_t size, size_t nmemb, std::string* s) -{ - s->append((char*) ptr, size*nmemb); - return size*nmemb; -} - -class polling_resource : public httpserver::http_resource -{ - public: - const httpserver::http_response render_GET(const httpserver::http_request& req) - { - std::string topic = "interesting_topic"; - if (req.get_arg("action") == "receive") - { - std::vector topics; - topics.push_back(topic); - return httpserver::http_response_builder("interesting listener").long_polling_receive_response(topics); - } - else - { - return httpserver::http_response_builder("interesting message").long_polling_send_response(topic); - } - } -}; - -LT_BEGIN_SUITE(authentication_suite) - void set_up() - { - } - - void tear_down() - { - } -LT_END_SUITE(authentication_suite) - -void* listener(void* par) -{ - curl_global_init(CURL_GLOBAL_ALL); - - CURL *curl = curl_easy_init(); - curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base?action=receive"); - curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc_listener); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, (closure_type*) par); - curl_easy_perform(curl); - curl_easy_cleanup(curl); - - return 0x0; -} - -LT_BEGIN_AUTO_TEST(authentication_suite, comet) - webserver* ws = new webserver(create_webserver(8080).comet()); - - polling_resource* pr = new polling_resource(); - ws->register_resource("base", pr); - ws->start(false); - - closure_type cls; - cls.counter = 0; - - pthread_t tid; - pthread_create(&tid, NULL, listener, (void *) &cls); - - sleep(1); - - { - curl_global_init(CURL_GLOBAL_ALL); - std::string s; - CURL *curl = curl_easy_init(); - CURLcode res; - curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base?action=send"); - curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); - res = curl_easy_perform(curl); - LT_ASSERT_EQ(res, 0); - LT_CHECK_EQ(s, "interesting message"); - curl_easy_cleanup(curl); - } - - { - curl_global_init(CURL_GLOBAL_ALL); - std::string s; - CURL *curl = curl_easy_init(); - CURLcode res; - curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base?action=send"); - curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); - res = curl_easy_perform(curl); - LT_ASSERT_EQ(res, 0); - LT_CHECK_EQ(s, "interesting message"); - curl_easy_cleanup(curl); - } - - pthread_join(tid, NULL); - - LT_CHECK_EQ(cls.s, "RECEIVED: interesting message"); - //not stopping to avoid errors with pending connections -LT_END_AUTO_TEST(comet) - -LT_BEGIN_AUTO_TEST_ENV() - AUTORUN_TESTS() -LT_END_AUTO_TEST_ENV() From 40e5f6d59676f74cfb400033087867f596a207f0 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sat, 12 Jan 2019 02:35:27 +0000 Subject: [PATCH 373/623] comet_enabled renamed into deferred_enabled --- src/httpserver/create_webserver.hpp | 14 +++++++------- src/httpserver/webserver.hpp | 2 +- src/webserver.cpp | 4 ++-- test/integ/ws_start_stop.cpp | 4 ++-- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/httpserver/create_webserver.hpp b/src/httpserver/create_webserver.hpp index e50ff842..502786af 100644 --- a/src/httpserver/create_webserver.hpp +++ b/src/httpserver/create_webserver.hpp @@ -79,7 +79,7 @@ class create_webserver _regex_checking(true), _ban_system_enabled(true), _post_process_enabled(true), - _comet_enabled(false), + _deferred_enabled(false), _single_resource(false), _not_found_resource(0x0), _method_not_allowed_resource(0x0), @@ -120,7 +120,7 @@ class create_webserver _regex_checking(true), _ban_system_enabled(true), _post_process_enabled(true), - _comet_enabled(false), + _deferred_enabled(false), _single_resource(false), _not_found_resource(0x0), _method_not_allowed_resource(0x0), @@ -264,13 +264,13 @@ class create_webserver { _digest_auth_enabled = false; return *this; } - create_webserver& comet() + create_webserver& deferred() { - _comet_enabled = true; return *this; + _deferred_enabled = true; return *this; } - create_webserver& no_comet() + create_webserver& no_deferred() { - _comet_enabled = false; return *this; + _deferred_enabled = false; return *this; } create_webserver& regex_checking() { @@ -351,7 +351,7 @@ class create_webserver bool _regex_checking; bool _ban_system_enabled; bool _post_process_enabled; - bool _comet_enabled; + bool _deferred_enabled; bool _single_resource; render_ptr _not_found_resource; render_ptr _method_not_allowed_resource; diff --git a/src/httpserver/webserver.hpp b/src/httpserver/webserver.hpp index 1109bbc5..6b931bde 100644 --- a/src/httpserver/webserver.hpp +++ b/src/httpserver/webserver.hpp @@ -173,7 +173,7 @@ class webserver const bool regex_checking; const bool ban_system_enabled; const bool post_process_enabled; - const bool comet_enabled; + const bool deferred_enabled; bool single_resource; pthread_mutex_t mutexwait; pthread_rwlock_t runguard; diff --git a/src/webserver.cpp b/src/webserver.cpp index 87a54d20..b86b32a3 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -170,7 +170,7 @@ webserver::webserver(const create_webserver& params): regex_checking(params._regex_checking), ban_system_enabled(params._ban_system_enabled), post_process_enabled(params._post_process_enabled), - comet_enabled(params._comet_enabled), + deferred_enabled(params._deferred_enabled), single_resource(params._single_resource), not_found_resource(params._not_found_resource), method_not_allowed_resource(params._method_not_allowed_resource), @@ -324,7 +324,7 @@ bool webserver::start(bool blocking) start_conf |= MHD_USE_DEBUG; if(pedantic) start_conf |= MHD_USE_PEDANTIC_CHECKS; - if(comet_enabled) + if(deferred_enabled) start_conf |= MHD_USE_SUSPEND_RESUME; #ifdef USE_FASTOPEN diff --git a/test/integ/ws_start_stop.cpp b/test/integ/ws_start_stop.cpp index 25d085f1..545a0516 100644 --- a/test/integ/ws_start_stop.cpp +++ b/test/integ/ws_start_stop.cpp @@ -188,7 +188,7 @@ LT_BEGIN_AUTO_TEST(ws_start_stop_suite, disable_options) .no_pedantic() .no_basic_auth() .no_digest_auth() - .no_comet() + .no_deferred() .no_regex_checking() .no_ban_system() .no_post_process(); @@ -216,7 +216,7 @@ LT_BEGIN_AUTO_TEST(ws_start_stop_suite, enable_options) webserver ws = create_webserver(8080) .debug() .pedantic() - .comet() + .deferred() .regex_checking() .ban_system() .post_process(); From afa67ef4fe5815def68dfe8ec07c538ad4412d52 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sat, 12 Jan 2019 08:53:06 +0000 Subject: [PATCH 374/623] Normalized structure of http_response. Now it has to use pointers to make use of polymorphism. Used shared_ptr to guarantee memory management. Removed internal smart pointer implementation. Removed internal binder implementation. --- ChangeLog | 8 + configure.ac | 2 +- examples/hello_world.cpp | 6 +- examples/service.cpp | 106 +++--- src/Makefile.am | 6 +- src/basic_auth_fail_response.cpp | 37 ++ src/deferred_response.cpp | 71 ++++ src/digest_auth_fail_response.cpp | 39 ++ src/file_response.cpp | 47 +++ src/http_request.cpp | 1 + src/http_resource.cpp | 6 +- src/http_response.cpp | 137 +------ src/http_utils.cpp | 1 + src/httpserver.hpp | 8 +- src/httpserver/basic_auth_fail_response.hpp | 80 ++++ src/httpserver/binders.hpp | 374 ------------------- src/httpserver/create_webserver.hpp | 2 +- src/httpserver/deferred_response.hpp | 86 +++++ src/httpserver/details/http_response_ptr.hpp | 125 ------- src/httpserver/details/modded_request.hpp | 8 +- src/httpserver/digest_auth_fail_response.hpp | 93 +++++ src/httpserver/file_response.hpp | 79 ++++ src/httpserver/http_resource.hpp | 23 +- src/httpserver/http_response.hpp | 164 ++------ src/httpserver/http_response_builder.hpp | 244 ------------ src/httpserver/http_utils.hpp | 3 + src/httpserver/string_response.hpp | 79 ++++ src/httpserver/webserver.hpp | 6 +- src/string_response.cpp | 38 ++ src/webserver.cpp | 51 ++- test/integ/authentication.cpp | 16 +- test/integ/ban_system.cpp | 4 +- test/integ/basic.cpp | 104 +++--- test/integ/deferred.cpp | 6 +- test/integ/threaded.cpp | 4 +- test/integ/ws_start_stop.cpp | 14 +- 36 files changed, 887 insertions(+), 1191 deletions(-) create mode 100644 src/basic_auth_fail_response.cpp create mode 100644 src/deferred_response.cpp create mode 100644 src/digest_auth_fail_response.cpp create mode 100644 src/file_response.cpp create mode 100644 src/httpserver/basic_auth_fail_response.hpp delete mode 100644 src/httpserver/binders.hpp create mode 100644 src/httpserver/deferred_response.hpp delete mode 100644 src/httpserver/details/http_response_ptr.hpp create mode 100644 src/httpserver/digest_auth_fail_response.hpp create mode 100644 src/httpserver/file_response.hpp delete mode 100644 src/httpserver/http_response_builder.hpp create mode 100644 src/httpserver/string_response.hpp create mode 100644 src/string_response.cpp diff --git a/ChangeLog b/ChangeLog index 38eb9bd9..e7adb3b0 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +Sat Jan 12 00:51:00 2018 -0800 + Removed the support for integrated COMET logic. + Removed the support for caching logic. + Added integ tests. + Changed http_resource interface to use shared_ptr. + Improved interface of the http_response object. + Deprecated http_response_builder object. + Thu Dec 26 10:00:30 2018 -0800 Fixed IPV6 parsing logic. Added tests to support IP parsing, URL parsing and utilities diff --git a/configure.ac b/configure.ac index b84c1d53..bc33cae3 100644 --- a/configure.ac +++ b/configure.ac @@ -21,7 +21,7 @@ AC_PREREQ(2.57) m4_define([libhttpserver_MAJOR_VERSION],[0])dnl -m4_define([libhttpserver_MINOR_VERSION],[16])dnl +m4_define([libhttpserver_MINOR_VERSION],[17])dnl m4_define([libhttpserver_REVISION],[0])dnl m4_define([libhttpserver_PKG_VERSION],[libhttpserver_MAJOR_VERSION.libhttpserver_MINOR_VERSION.libhttpserver_REVISION])dnl m4_define([libhttpserver_LDF_VERSION],[libhttpserver_MAJOR_VERSION:libhttpserver_MINOR_VERSION:libhttpserver_REVISION])dnl diff --git a/examples/hello_world.cpp b/examples/hello_world.cpp index 9a0a4e4c..f9675b32 100755 --- a/examples/hello_world.cpp +++ b/examples/hello_world.cpp @@ -25,13 +25,13 @@ using namespace httpserver; class hello_world_resource : public http_resource { public: - const http_response render(const http_request&); + const std::shared_ptr render(const http_request&); void set_some_data(const std::string &s) {data = s;} std::string data; }; //using the render method you are able to catch each type of request you receive -const http_response hello_world_resource::render(const http_request& req) +const std::shared_ptr hello_world_resource::render(const http_request& req) { //it is possible to store data inside the resource object that can be altered //through the requests @@ -42,7 +42,7 @@ const http_response hello_world_resource::render(const http_request& req) //it is possible to send a response initializing an http_string_response //that reads the content to send in response from a string. - return http_response_builder("Hello World!!!", 200).string_response(); + return std::shared_ptr(new string_response("Hello World!!!", 200)); } int main() diff --git a/examples/service.cpp b/examples/service.cpp index 48ea39f9..b2d365fc 100644 --- a/examples/service.cpp +++ b/examples/service.cpp @@ -32,15 +32,15 @@ class service_resource: public http_resource { service_resource(); ~service_resource(); - - const http_response render_GET(const http_request &req); - const http_response render_PUT(const http_request &req); - const http_response render_POST(const http_request &req); - const http_response render(const http_request &req); - const http_response render_HEAD(const http_request &req); - const http_response render_OPTIONS(const http_request &req); - const http_response render_CONNECT(const http_request &req); - const http_response render_DELETE(const http_request &req); + + const std::shared_ptr render_GET(const http_request &req); + const std::shared_ptr render_PUT(const http_request &req); + const std::shared_ptr render_POST(const http_request &req); + const std::shared_ptr render(const http_request &req); + const std::shared_ptr render_HEAD(const http_request &req); + const std::shared_ptr render_OPTIONS(const http_request &req); + const std::shared_ptr render_CONNECT(const http_request &req); + const std::shared_ptr render_DELETE(const http_request &req); private: @@ -53,120 +53,118 @@ service_resource::service_resource() service_resource::~service_resource() {} -const http_response +const std::shared_ptr service_resource::render_GET(const http_request &req) { std::cout << "service_resource::render_GET()" << std::endl; if (verbose) std::cout << req; + string_response* res = new string_response("GET response", 200); - http_response res(http_response_builder("GET response", 200).string_response()); - - if (verbose) std::cout << res; - - return res; + if (verbose) std::cout << *res; + return std::shared_ptr(res); } -const http_response +const std::shared_ptr service_resource::render_PUT(const http_request &req) { - std::cout << "service_resource::render_PUT()" << std::endl; + std::cout << "service_resource::render_PUT()" << std::endl; if (verbose) std::cout << req; - - http_response res(http_response_builder("PUT response", 200).string_response()); - if (verbose) std::cout << res; + string_response* res = new string_response("PUT response", 200); - return res; + if (verbose) std::cout << *res; + + return std::shared_ptr(res); } -const http_response +const std::shared_ptr service_resource::render_POST(const http_request &req) { - std::cout << "service_resource::render_POST()" << std::endl; + std::cout << "service_resource::render_POST()" << std::endl; if (verbose) std::cout << req; - - http_response res(http_response_builder("POST response", 200).string_response()); - if (verbose) std::cout << res; + string_response* res = new string_response("POST response", 200); + + if (verbose) std::cout << *res; - return res; + return std::shared_ptr(res); } -const http_response +const std::shared_ptr service_resource::render(const http_request &req) { - std::cout << "service_resource::render()" << std::endl; + std::cout << "service_resource::render()" << std::endl; if (verbose) std::cout << req; - http_response res(http_response_builder("generic response", 200).string_response()); + string_response* res = new string_response("generic response", 200); - if (verbose) std::cout << res; + if (verbose) std::cout << *res; - return res; + return std::shared_ptr(res); } -const http_response +const std::shared_ptr service_resource::render_HEAD(const http_request &req) { std::cout << "service_resource::render_HEAD()" << std::endl; if (verbose) std::cout << req; - - http_response res(http_response_builder("HEAD response", 200).string_response()); - if (verbose) std::cout << res; + string_response* res = new string_response("HEAD response", 200); - return res; + if (verbose) std::cout << *res; + + return std::shared_ptr(res); } -const http_response +const std::shared_ptr service_resource::render_OPTIONS(const http_request &req) { std::cout << "service_resource::render_OPTIONS()" << std::endl; if (verbose) std::cout << req; - - http_response res(http_response_builder("OPTIONS response", 200).string_response()); - if (verbose) std::cout << res; + string_response* res = new string_response("OPTIONS response", 200); + + if (verbose) std::cout << *res; - return res; + return std::shared_ptr(res); } -const http_response +const std::shared_ptr service_resource::render_CONNECT(const http_request &req) { - std::cout << "service_resource::render_CONNECT()" << std::endl; + std::cout << "service_resource::render_CONNECT()" << std::endl; if (verbose) std::cout << req; - - http_response res(http_response_builder("CONNECT response", 200).string_response()); - if (verbose) std::cout << res; + string_response* res = new string_response("CONNECT response", 200); - return res; + if (verbose) std::cout << *res; + + return std::shared_ptr(res); } -const http_response +const std::shared_ptr service_resource::render_DELETE(const http_request &req) { - std::cout << "service_resource::render_DELETE()" << std::endl; + std::cout << "service_resource::render_DELETE()" << std::endl; if (verbose) std::cout << req; - - http_response res(http_response_builder("DELETE response", 200).string_response()); - if (verbose) std::cout << res; + string_response* res = new string_response("DELETE response", 200); + + if (verbose) std::cout << *res; - return res; + return std::shared_ptr(res); } void usage() diff --git a/src/Makefile.am b/src/Makefile.am index 3cc21f8a..888abce4 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -19,9 +19,9 @@ AM_CPPFLAGS = -I../ -I$(srcdir)/httpserver/ METASOURCES = AUTO lib_LTLIBRARIES = libhttpserver.la -libhttpserver_la_SOURCES = string_utilities.cpp webserver.cpp http_utils.cpp http_request.cpp http_response.cpp http_resource.cpp details/http_endpoint.cpp -noinst_HEADERS = httpserver/string_utilities.hpp httpserver/details/modded_request.hpp httpserver/details/http_response_ptr.hpp gettext.h -nobase_include_HEADERS = httpserver.hpp httpserver/create_webserver.hpp httpserver/webserver.hpp httpserver/http_utils.hpp httpserver/details/http_endpoint.hpp httpserver/http_request.hpp httpserver/http_response.hpp httpserver/http_resource.hpp httpserver/binders.hpp httpserver/http_response_builder.hpp +libhttpserver_la_SOURCES = string_utilities.cpp webserver.cpp http_utils.cpp http_request.cpp http_response.cpp string_response.cpp basic_auth_fail_response.cpp digest_auth_fail_response.cpp deferred_response.cpp file_response.cpp http_resource.cpp details/http_endpoint.cpp +noinst_HEADERS = httpserver/string_utilities.hpp httpserver/details/modded_request.hpp gettext.h +nobase_include_HEADERS = httpserver.hpp httpserver/create_webserver.hpp httpserver/webserver.hpp httpserver/http_utils.hpp httpserver/details/http_endpoint.hpp httpserver/http_request.hpp httpserver/http_response.hpp httpserver/http_resource.hpp httpserver/string_response.hpp httpserver/basic_auth_fail_response.hpp httpserver/digest_auth_fail_response.hpp httpserver/deferred_response.hpp httpserver/file_response.hpp AM_CXXFLAGS += -fPIC -Wall diff --git a/src/basic_auth_fail_response.cpp b/src/basic_auth_fail_response.cpp new file mode 100644 index 00000000..b49dcc16 --- /dev/null +++ b/src/basic_auth_fail_response.cpp @@ -0,0 +1,37 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011-2019 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#include "basic_auth_fail_response.hpp" + +using namespace std; + +namespace httpserver +{ + +int basic_auth_fail_response::enqueue_response(MHD_Connection* connection, MHD_Response* response) +{ + return MHD_queue_basic_auth_fail_response( + connection, + realm.c_str(), + response + ); +} + +} diff --git a/src/deferred_response.cpp b/src/deferred_response.cpp new file mode 100644 index 00000000..fe17cb29 --- /dev/null +++ b/src/deferred_response.cpp @@ -0,0 +1,71 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011-2019 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#include "deferred_response.hpp" + +using namespace std; + +namespace httpserver +{ + +namespace details +{ + +ssize_t cb(void* cls, uint64_t pos, char* buf, size_t max) +{ + ssize_t val = static_cast(cls)->cycle_callback(buf, max); + if(val == -1) + { + static_cast(cls)->completed = true; + } + + return val; +} + +} + +MHD_Response* deferred_response::get_raw_response() +{ + if(!completed) + { + return MHD_create_response_from_callback( + MHD_SIZE_UNKNOWN, + 1024, + &details::cb, + this, + NULL + ); + } + else + { + return static_cast(this)->get_raw_response(); + } +} + +void deferred_response::decorate_response(MHD_Response* response) +{ + if(completed) + { + static_cast(this)->decorate_response(response); + } +} + +} + diff --git a/src/digest_auth_fail_response.cpp b/src/digest_auth_fail_response.cpp new file mode 100644 index 00000000..cc2c6201 --- /dev/null +++ b/src/digest_auth_fail_response.cpp @@ -0,0 +1,39 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011-2019 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#include "digest_auth_fail_response.hpp" + +using namespace std; + +namespace httpserver +{ + +int digest_auth_fail_response::enqueue_response(MHD_Connection* connection, MHD_Response* response) +{ + return MHD_queue_auth_fail_response( + connection, + realm.c_str(), + opaque.c_str(), + response, + reload_nonce ? MHD_YES : MHD_NO + ); +} + +} diff --git a/src/file_response.cpp b/src/file_response.cpp new file mode 100644 index 00000000..7de47b1e --- /dev/null +++ b/src/file_response.cpp @@ -0,0 +1,47 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011-2019 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#include +#include "file_response.hpp" + +using namespace std; + +namespace httpserver +{ + +MHD_Response* file_response::get_raw_response() +{ + int fd = open(filename.c_str(), O_RDONLY); + size_t size = lseek(fd, 0, SEEK_END); + if(size) + { + return MHD_create_response_from_fd(size, fd); + } + else + { + return MHD_create_response_from_buffer( + 0, + (void*) "", + MHD_RESPMEM_PERSISTENT + ); + } +} + +} diff --git a/src/http_request.cpp b/src/http_request.cpp index bca1012d..dc1127be 100644 --- a/src/http_request.cpp +++ b/src/http_request.cpp @@ -50,6 +50,7 @@ bool http_request::check_digest_auth( password.c_str(), nonce_timeout ); + if(val == MHD_INVALID_NONCE) { reload_nonce = true; diff --git a/src/http_resource.cpp b/src/http_resource.cpp index 19c3279f..a9ded860 100644 --- a/src/http_resource.cpp +++ b/src/http_resource.cpp @@ -26,7 +26,7 @@ #include "http_response.hpp" #include "webserver.hpp" #include "string_utilities.hpp" -#include "http_response_builder.hpp" +#include "string_response.hpp" using namespace std; @@ -48,9 +48,9 @@ void resource_init(map& allowed_methods) namespace details { -http_response empty_render(const http_request& r) +shared_ptr empty_render(const http_request& r) { - return http_response_builder("", 200).string_response(); + return shared_ptr(new string_response()); } }; diff --git a/src/http_response.cpp b/src/http_response.cpp index bbf9e81d..35b83d50 100644 --- a/src/http_response.cpp +++ b/src/http_response.cpp @@ -27,60 +27,18 @@ #include "http_utils.hpp" #include "webserver.hpp" #include "http_response.hpp" -#include "http_response_builder.hpp" using namespace std; namespace httpserver { -class webserver; - -http_response::http_response(const http_response_builder& builder): - content(builder._content_hook), - response_code(builder._response_code), - realm(builder._realm), - opaque(builder._opaque), - reload_nonce(builder._reload_nonce), - fp(-1), - filename(builder._content_hook), - headers(builder._headers), - footers(builder._footers), - cookies(builder._cookies), - topics(builder._topics), - keepalive_secs(builder._keepalive_secs), - keepalive_msg(builder._keepalive_msg), - send_topic(builder._send_topic), - underlying_connection(0x0), - cycle_callback(builder._cycle_callback), - get_raw_response(this, builder._get_raw_response), - decorate_response(this, builder._decorate_response), - enqueue_response(this, builder._enqueue_response), - completed(false), - ws(0x0), - connection_id(0x0), - _get_raw_response(builder._get_raw_response), - _decorate_response(builder._decorate_response), - _enqueue_response(builder._enqueue_response) +MHD_Response* http_response::get_raw_response() { + return MHD_create_response_from_buffer(0, (void*) "", MHD_RESPMEM_PERSISTENT); } -http_response::~http_response() -{ -} - -//RESPONSE -void http_response::get_raw_response_str(MHD_Response** response, webserver* ws) -{ - size_t size = &(*content.end()) - &(*content.begin()); - *response = MHD_create_response_from_buffer( - size, - (void*) content.c_str(), - MHD_RESPMEM_PERSISTENT - ); -} - -void http_response::decorate_response_str(MHD_Response* response) +void http_response::decorate_response(MHD_Response* response) { map::iterator it; @@ -105,95 +63,14 @@ void http_response::decorate_response_str(MHD_Response* response) ); } -int http_response::enqueue_response_str( - MHD_Connection* connection, - MHD_Response* response -) +int http_response::enqueue_response(MHD_Connection* connection, MHD_Response* response) { return MHD_queue_response(connection, response_code, response); } -int http_response::enqueue_response_basic( - MHD_Connection* connection, - MHD_Response* response -) -{ - return MHD_queue_basic_auth_fail_response( - connection, - realm.c_str(), - response - ); -} - -int http_response::enqueue_response_digest( - MHD_Connection* connection, - MHD_Response* response -) -{ - return MHD_queue_auth_fail_response( - connection, - realm.c_str(), - opaque.c_str(), - response, - reload_nonce ? MHD_YES : MHD_NO - ); -} - -void http_response::get_raw_response_file( - MHD_Response** response, - webserver* ws -) -{ - int fd = open(filename.c_str(), O_RDONLY); - size_t size = lseek(fd, 0, SEEK_END); - if(size) - { - *response = MHD_create_response_from_fd(size, fd); - } - else - { - *response = MHD_create_response_from_buffer( - 0, - (void*) "", - MHD_RESPMEM_PERSISTENT - ); - } -} - -namespace details -{ - -ssize_t cb(void* cls, uint64_t pos, char* buf, size_t max) -{ - ssize_t val = static_cast(cls)->cycle_callback(buf, max); - if(val == -1) - static_cast(cls)->completed = true; - return val; -} - -} - -void http_response::get_raw_response_deferred( - MHD_Response** response, - webserver* ws -) +void http_response::shoutCAST() { - if(!completed) - *response = MHD_create_response_from_callback( - MHD_SIZE_UNKNOWN, - 1024, - &details::cb, - this, - NULL - ); - else - static_cast(this)->get_raw_response(response, ws); -} - -void http_response::decorate_response_deferred(MHD_Response* response) -{ - if(completed) - static_cast(this)->decorate_response(response); + this->response_code |= http::http_utils::shoutcast_response; } std::ostream &operator<< (std::ostream &os, const http_response &r) @@ -207,4 +84,4 @@ std::ostream &operator<< (std::ostream &os, const http_response &r) return os; } -}; +} diff --git a/src/http_utils.cpp b/src/http_utils.cpp index 446dd755..62fdbba0 100644 --- a/src/http_utils.cpp +++ b/src/http_utils.cpp @@ -211,6 +211,7 @@ const std::string http_utils::http_post_encoding_form_urlencoded = const std::string http_utils::http_post_encoding_multipart_formdata = MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA; +const std::string http_utils::text_plain = "text/plain"; std::vector http_utils::tokenize_url( const std::string& str, diff --git a/src/httpserver.hpp b/src/httpserver.hpp index 2cfbb6cb..2d0e5b0e 100644 --- a/src/httpserver.hpp +++ b/src/httpserver.hpp @@ -26,7 +26,13 @@ #include "httpserver/http_utils.hpp" #include "httpserver/http_resource.hpp" #include "httpserver/http_response.hpp" -#include "httpserver/http_response_builder.hpp" + +#include "httpserver/string_response.hpp" +#include "httpserver/basic_auth_fail_response.hpp" +#include "httpserver/digest_auth_fail_response.hpp" +#include "httpserver/deferred_response.hpp" +#include "httpserver/file_response.hpp" + #include "httpserver/http_request.hpp" #include "httpserver/webserver.hpp" diff --git a/src/httpserver/basic_auth_fail_response.hpp b/src/httpserver/basic_auth_fail_response.hpp new file mode 100644 index 00000000..a7c7b418 --- /dev/null +++ b/src/httpserver/basic_auth_fail_response.hpp @@ -0,0 +1,80 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011-2019 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#if !defined (_HTTPSERVER_HPP_INSIDE_) && !defined (HTTPSERVER_COMPILATION) +#error "Only or can be included directly." +#endif + +#ifndef _BASIC_AUTH_FAIL_RESPONSE_HPP_ +#define _BASIC_AUTH_FAIL_RESPONSE_HPP_ + +#include "httpserver/string_response.hpp" + +namespace httpserver +{ + +class basic_auth_fail_response : public string_response +{ + public: + basic_auth_fail_response(): + string_response(), + realm("") + { + } + + explicit basic_auth_fail_response( + const std::string& content, + const std::string& realm = "", + int response_code = http::http_utils::http_ok, + const std::string& content_type = http::http_utils::text_plain + ): + string_response(content, response_code, content_type), + realm(realm) + { + } + + basic_auth_fail_response(const basic_auth_fail_response& other): + string_response(other), + realm(other.realm) + { + } + + basic_auth_fail_response& operator=(const basic_auth_fail_response& b) + { + if (this == &b) return *this; + + (string_response&) (*this) = b; + this->realm = b.realm; + + return *this; + } + + ~basic_auth_fail_response() + { + } + + int enqueue_response(MHD_Connection* connection, MHD_Response* response); + + private: + std::string realm; +}; + +} +#endif // _BASIC_AUTH_FAIL_RESPONSE_HPP_ diff --git a/src/httpserver/binders.hpp b/src/httpserver/binders.hpp deleted file mode 100644 index 83bae04b..00000000 --- a/src/httpserver/binders.hpp +++ /dev/null @@ -1,374 +0,0 @@ -/* - This file is part of libhttpserver - Copyright (C) 2011-2019 Sebastiano Merlino - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 - USA -*/ - -#if !defined (_HTTPSERVER_HPP_INSIDE_) && !defined (HTTPSERVER_COMPILATION) -#error "Only or can be included directly." -#endif - -#ifndef _BINDERS_HPP_ -#define _BINDERS_HPP_ - -namespace httpserver { -namespace details -{ - namespace binders - { - class generic_class; - const int MEMFUNC_SIZE = sizeof(void (generic_class::*)()); - - template - struct converter - { - template - inline static generic_class* convert( - X* pmem, - func_type func, - generic_mem_func_type &bound - ) - { - return 0; - } - }; - - template<> - struct converter - { - template - inline static generic_class* convert( - X* pmem, - func_type func, - generic_mem_func_type &bound - ) - { - bound = reinterpret_cast(func); - return reinterpret_cast(pmem); - } - }; - - template - class binder - { - private: - typedef void (generic_class::*generic_mem_fun)(); - typedef void (*generic_mem_ptr)(); - - generic_class *pmem; - generic_mem_fun _pfunc; - generic_mem_ptr _spfunc; - - public: - binder() - { - } - - binder(const binder& o): - pmem(o.pmem), - _pfunc(o._pfunc), - _spfunc(o._spfunc) - { - } - - template - binder(X* pmem, Y fun): - pmem(converter::convert(pmem, fun, _pfunc)), - _spfunc(0) - { - } - - template - binder(DC* pp, parent_invoker invoker, static_function fun): - pmem(converter::convert(pp, invoker, _pfunc)), - _spfunc(reinterpret_cast(fun)) - { - } - - inline generic_class* exec() const - { - return pmem; - } - inline generic_mem get_mem_ptr() const - { - return reinterpret_cast(_pfunc); - } - inline void_static_function get_static_func() const - { - return reinterpret_cast(_spfunc); - } - }; - - template - class functor_zero - { - private: - typedef RET_TYPE (*static_function)(); - typedef RET_TYPE (*void_static_function)(); - typedef RET_TYPE (generic_class::*generic_mem)(); - typedef binder binder_type; - binder_type _binder; - - RET_TYPE exec_static() const - { - return (*(_binder.get_static_func()))(); - } - - functor_zero& operator=(const functor_zero&) - { - return *this; - } - public: - typedef functor_zero type; - functor_zero() { } - - template - functor_zero(Y* pmem, RET_TYPE(X::*func)()): - _binder(reinterpret_cast(pmem), func) - { - } - - template - functor_zero(Y* pmem, RET_TYPE(X::*func)() const ): - _binder(reinterpret_cast(pmem), func) - { - } - - functor_zero(RET_TYPE(*func)() ): - _binder(this, &functor_zero::exec_static, func) - { - } - - RET_TYPE operator() () const - { - return (_binder.exec()->*(_binder.get_mem_ptr()))(); - } - }; - - template - class functor_one - { - private: - typedef RET_TYPE (*static_function)(PAR1 p1); - typedef RET_TYPE (*void_static_function)(PAR1 p1); - typedef RET_TYPE (generic_class::*generic_mem)(PAR1 p1); - typedef binder binder_type; - binder_type _binder; - - RET_TYPE exec_static(PAR1 p1) const - { - return (*(_binder.get_static_func()))(p1); - } - - functor_one& operator=(const functor_one&) - { - return *this; - } - public: - typedef functor_one type; - functor_one() { } - - template - functor_one(Y* pmem, RET_TYPE(X::*func)(PAR1 p1) ): - _binder(reinterpret_cast(pmem), func) - { - } - - template - functor_one(Y* pmem, RET_TYPE(X::*func)(PAR1 p1) const ): - _binder(reinterpret_cast(pmem), func) - { - } - - functor_one(RET_TYPE(*func)(PAR1 p1) ): - _binder(this, &functor_one::exec_static, func) - { - } - - RET_TYPE operator() (PAR1 p1) const - { - return (_binder.exec()->*(_binder.get_mem_ptr()))(p1); - } - }; - - template - class functor_two - { - private: - typedef RET_TYPE (*static_function)(PAR1 p1, PAR2 p2); - typedef RET_TYPE (*void_static_function)(PAR1 p1, PAR2 p2); - - typedef RET_TYPE - (generic_class::*generic_mem)(PAR1 p1, PAR2 p2); - - typedef binder< - generic_mem, static_function, void_static_function - > binder_type; - - binder_type _binder; - - RET_TYPE exec_static(PAR1 p1, PAR2 p2) const - { - return (*(_binder.get_static_func()))(p1, p2); - } - public: - typedef functor_two type; - functor_two() { } - - functor_two(const functor_two& o): - _binder(o._binder) - { - } - - template - functor_two(Y* pmem, RET_TYPE(X::*func)(PAR1 p1, PAR2 p2) const ): - _binder(reinterpret_cast(pmem), func) - { - } - - template - functor_two(Y* pmem, RET_TYPE(X::*func)(PAR1 p1, PAR2 p2) ): - _binder(reinterpret_cast(pmem), func) - { - } - - functor_two(RET_TYPE(*func)(PAR1 p1, PAR2 p2) ): - _binder(this, &functor_two::exec_static, func) - { - } - - RET_TYPE operator() (PAR1 p1, PAR2 p2) const - { - return (_binder.exec()->*(_binder.get_mem_ptr()))(p1, p2); - } - }; - - template - class functor_three - { - private: - typedef RET_TYPE (*static_function)(PAR1 p1, PAR2 p2, PAR3 p3); - typedef RET_TYPE (*void_static_function)(PAR1 p1, PAR2 p2, PAR3 p3); - - typedef RET_TYPE - (generic_class::*generic_mem)(PAR1 p1, PAR2 p2, PAR3 p3); - - typedef binder< - generic_mem, static_function, void_static_function - > binder_type; - - binder_type _binder; - - RET_TYPE exec_static(PAR1 p1, PAR2 p2, PAR3 p3) const - { - return (*(_binder.get_static_func()))(p1, p2, p3); - } - public: - typedef functor_three type; - functor_three() { } - - functor_three(const functor_three& o): - _binder(o._binder) - { - } - - template - functor_three(Y* pmem, RET_TYPE(X::*func)(PAR1 p1, PAR2 p2, PAR3 p3) const ): - _binder(reinterpret_cast(pmem), func) - { - } - - template - functor_three(Y* pmem, RET_TYPE(X::*func)(PAR1 p1, PAR2 p2, PAR3 p3) ): - _binder(reinterpret_cast(pmem), func) - { - } - - functor_three(RET_TYPE(*func)(PAR1 p1, PAR2 p2, PAR3 p3) ): - _binder(this, &functor_three::exec_static, func) - { - } - - RET_TYPE operator() (PAR1 p1, PAR2 p2, PAR3 p3) const - { - return (_binder.exec()->*(_binder.get_mem_ptr()))(p1, p2, p3); - } - }; - - template - class functor_four - { - private: - typedef RET_TYPE (*static_function)(PAR1 p1, PAR2 p2, PAR3 p3, PAR4 p4); - typedef RET_TYPE (*void_static_function)(PAR1 p1, PAR2 p2, PAR3 p3, PAR4 p4); - - typedef RET_TYPE - (generic_class::*generic_mem)(PAR1 p1, PAR2 p2, PAR3 p3, PAR4 p4); - - typedef binder< - generic_mem, static_function, void_static_function - > binder_type; - - binder_type _binder; - - RET_TYPE exec_static(PAR1 p1, PAR2 p2, PAR3 p3, PAR4 p4) const - { - return (*(_binder.get_static_func()))(p1, p2, p3, p4); - } - public: - typedef functor_four type; - functor_four() { } - - functor_four(const functor_four& o): - _binder(o._binder) - { - } - - template - functor_four(Y* pmem, RET_TYPE(X::*func)(PAR1 p1, PAR2 p2, PAR3 p3, PAR4 p4) const ): - _binder(reinterpret_cast(pmem), func) - { - } - - template - functor_four(Y* pmem, RET_TYPE(X::*func)(PAR1 p1, PAR2 p2, PAR3 p3, PAR4 p4) ): - _binder(reinterpret_cast(pmem), func) - { - } - - functor_four(RET_TYPE(*func)(PAR1 p1, PAR2 p2, PAR3 p3, PAR4 p4) ): - _binder(this, &functor_four::exec_static, func) - { - } - - RET_TYPE operator() (PAR1 p1, PAR2 p2, PAR3 p3, PAR4 p4) const - { - return (_binder.exec()->*(_binder.get_mem_ptr()))(p1, p2, p3, p4); - } - }; - } -}} - -#endif diff --git a/src/httpserver/create_webserver.hpp b/src/httpserver/create_webserver.hpp index 502786af..c0ecb189 100644 --- a/src/httpserver/create_webserver.hpp +++ b/src/httpserver/create_webserver.hpp @@ -37,7 +37,7 @@ namespace httpserver { class webserver; class http_request; -typedef const http_response(*render_ptr)(const http_request&); +typedef const std::shared_ptr(*render_ptr)(const http_request&); typedef bool(*validator_ptr)(const std::string&); typedef void(*unescaper_ptr)(std::string&); typedef void(*log_access_ptr)(const std::string&); diff --git a/src/httpserver/deferred_response.hpp b/src/httpserver/deferred_response.hpp new file mode 100644 index 00000000..ab46a306 --- /dev/null +++ b/src/httpserver/deferred_response.hpp @@ -0,0 +1,86 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011-2019 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#if !defined (_HTTPSERVER_HPP_INSIDE_) && !defined (HTTPSERVER_COMPILATION) +#error "Only or can be included directly." +#endif + +#ifndef _DEFERRED_RESPONSE_HPP_ +#define _DEFERRED_RESPONSE_HPP_ + +#include "httpserver/string_response.hpp" + +namespace httpserver +{ + +namespace details +{ + ssize_t cb(void*, uint64_t, char*, size_t); +}; + +typedef ssize_t(*cycle_callback_ptr)(char*, size_t); + +class deferred_response : public string_response +{ + public: + explicit deferred_response( + cycle_callback_ptr cycle_callback, + const std::string& content = "", + int response_code = http::http_utils::http_ok, + const std::string& content_type = http::http_utils::text_plain + ): + string_response(content, response_code, content_type), + cycle_callback(cycle_callback), + completed(false) + { + } + + deferred_response(const deferred_response& other): + string_response(other), + cycle_callback(other.cycle_callback), + completed(other.completed) + { + } + + deferred_response& operator=(const deferred_response& b) + { + if (this == &b) return *this; + + (string_response&) (*this) = b; + this->cycle_callback = b.cycle_callback; + + return *this; + } + + ~deferred_response() + { + } + + MHD_Response* get_raw_response(); + void decorate_response(MHD_Response* response); + private: + cycle_callback_ptr cycle_callback; + bool completed; + + friend ssize_t details::cb(void* cls, uint64_t pos, char* buf, size_t max); +}; + +} +#endif // _DEFERRED_RESPONSE_HPP_ diff --git a/src/httpserver/details/http_response_ptr.hpp b/src/httpserver/details/http_response_ptr.hpp deleted file mode 100644 index 20059cdd..00000000 --- a/src/httpserver/details/http_response_ptr.hpp +++ /dev/null @@ -1,125 +0,0 @@ -/* - This file is part of libhttpserver - Copyright (C) 2011-2019 Sebastiano Merlino - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 - USA -*/ - -#if !defined (_HTTPSERVER_HPP_INSIDE_) && !defined (HTTPSERVER_COMPILATION) -#error "Only or can be included directly." -#endif - -#ifndef _HTTP_RESPONSE_PTR_HPP_ -#define _HTTP_RESPONSE_PTR_HPP_ - -#include "http_response.hpp" - -#if defined(__CLANG_ATOMICS) - -#define atomic_increment(object) \ - __c11_atomicadd_fetch(object, 1, __ATOMIC_RELAXED) - -#define atomic_decrement(object) \ - __c11_atomic_sub_fetch(object, 1, __ATOMIC_ACQ_REL) - -#elif defined(__GNUC_ATOMICS) - -#define atomic_increment(object) \ - __atomic_add_fetch(object, 1, __ATOMIC_RELAXED) - -#define atomic_decrement(object) \ - __atomic_sub_fetch(object, 1, __ATOMIC_ACQ_REL) - -#else - -#define atomic_increment(object) \ - __sync_add_and_fetch(object, 1) - -#define atomic_decrement(object) \ - __sync_sub_and_fetch(object, 1) - -#endif - -namespace httpserver -{ - -class webserver; - -namespace details -{ - -struct http_response_ptr -{ - public: - http_response_ptr(http_response* res = 0x0): - res(res) - { - num_references = new int(1); - } - - http_response_ptr(const http_response_ptr& b): - res(b.res), - num_references(b.num_references) - { - atomic_increment(b.num_references); - } - - ~http_response_ptr() - { - if (atomic_decrement(num_references) != 0) return; - - if (res != 0x0) delete res; - if (num_references != 0x0) delete num_references; - - res = 0x0; - num_references = 0x0; - } - - http_response_ptr& operator=(http_response_ptr b) - { - using std::swap; - - swap(this->num_references, b.num_references); - swap(this->res, b.res); - - return *this; - } - - http_response& operator* () - { - return *res; - } - - http_response* operator-> () - { - return res; - } - - http_response* ptr() - { - return res; - } - - private: - http_response* res; - int* num_references; - friend class ::httpserver::webserver; -}; - -} //details -} //httpserver - -#endif //_HTTP_RESPONSE_PTR_HPP_ diff --git a/src/httpserver/details/modded_request.hpp b/src/httpserver/details/modded_request.hpp index a567250c..74bb79df 100644 --- a/src/httpserver/details/modded_request.hpp +++ b/src/httpserver/details/modded_request.hpp @@ -25,9 +25,6 @@ #ifndef _MODDED_REQUEST_HPP_ #define _MODDED_REQUEST_HPP_ -#include "binders.hpp" -#include "details/http_response_ptr.hpp" - namespace httpserver { @@ -41,10 +38,10 @@ struct modded_request std::string* standardized_url; webserver* ws; - const http_response (httpserver::http_resource::*callback)(const httpserver::http_request&); + const std::shared_ptr (httpserver::http_resource::*callback)(const httpserver::http_request&); http_request* dhr; - http_response_ptr dhrs; + std::shared_ptr dhrs; bool second; modded_request(): @@ -53,7 +50,6 @@ struct modded_request standardized_url(0x0), ws(0x0), dhr(0x0), - dhrs(0x0), second(false) { } diff --git a/src/httpserver/digest_auth_fail_response.hpp b/src/httpserver/digest_auth_fail_response.hpp new file mode 100644 index 00000000..2b67d268 --- /dev/null +++ b/src/httpserver/digest_auth_fail_response.hpp @@ -0,0 +1,93 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011-2019 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#if !defined (_HTTPSERVER_HPP_INSIDE_) && !defined (HTTPSERVER_COMPILATION) +#error "Only or can be included directly." +#endif + +#ifndef _DIGEST_AUTH_FAIL_RESPONSE_HPP_ +#define _DIGEST_AUTH_FAIL_RESPONSE_HPP_ + +#include "httpserver/string_response.hpp" + +namespace httpserver +{ + +class digest_auth_fail_response : public string_response +{ + public: + digest_auth_fail_response(): + string_response(), + realm(""), + opaque(""), + reload_nonce(false) + { + } + + digest_auth_fail_response( + const std::string& content, + const std::string& realm = "", + const std::string& opaque = "", + bool reload_nonce = false, + int response_code = http::http_utils::http_ok, + const std::string& content_type = http::http_utils::text_plain + ): + string_response(content, response_code, content_type), + realm(realm), + opaque(opaque), + reload_nonce(reload_nonce) + { + } + + digest_auth_fail_response(const digest_auth_fail_response& other): + string_response(other), + realm(other.realm), + opaque(other.opaque), + reload_nonce(other.reload_nonce) + { + } + + digest_auth_fail_response& operator=(const digest_auth_fail_response& b) + { + if (this == &b) return *this; + + (string_response&) (*this) = b; + this->realm = b.realm; + this->opaque = b.opaque; + this->reload_nonce = b.reload_nonce; + + return *this; + } + + ~digest_auth_fail_response() + { + } + + int enqueue_response(MHD_Connection* connection, MHD_Response* response); + + private: + std::string realm; + std::string opaque; + bool reload_nonce; +}; + +} + +#endif // _DIGEST_AUTH_FAIL_RESPONSE_HPP_ diff --git a/src/httpserver/file_response.hpp b/src/httpserver/file_response.hpp new file mode 100644 index 00000000..9f7a3977 --- /dev/null +++ b/src/httpserver/file_response.hpp @@ -0,0 +1,79 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011-2019 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#if !defined (_HTTPSERVER_HPP_INSIDE_) && !defined (HTTPSERVER_COMPILATION) +#error "Only or can be included directly." +#endif + +#ifndef _FILE_RESPONSE_HPP_ +#define _FILE_RESPONSE_HPP_ + +#include "httpserver/http_response.hpp" + +namespace httpserver +{ + +class file_response : public http_response +{ + public: + file_response(): + http_response(), + filename("") + { + } + + explicit file_response( + const std::string& filename, + int response_code = http::http_utils::http_ok, + const std::string& content_type = http::http_utils::text_plain + ): + http_response(response_code, content_type), + filename(filename) + { + } + + file_response(const file_response& other): + http_response(other), + filename(other.filename) + { + } + + file_response& operator=(const file_response& b) + { + if (this == &b) return *this; + + (http_response&) (*this) = b; + this->filename = b.filename; + + return *this; + } + + ~file_response() + { + } + + MHD_Response* get_raw_response(); + + private: + std::string filename; +}; + +} +#endif // _FILE_RESPONSE_HPP_ diff --git a/src/httpserver/http_resource.hpp b/src/httpserver/http_resource.hpp index 90671d18..0460f92b 100644 --- a/src/httpserver/http_resource.hpp +++ b/src/httpserver/http_resource.hpp @@ -26,6 +26,7 @@ #define _http_resource_hpp_ #include #include +#include #ifdef DEBUG #include @@ -42,7 +43,7 @@ class http_request; namespace details { -http_response empty_render(const http_request& r); +std::shared_ptr empty_render(const http_request& r); }; @@ -68,7 +69,7 @@ class http_resource * @param req Request passed through http * @return A http_response object **/ - virtual const http_response render(const http_request& req) + virtual const std::shared_ptr render(const http_request& req) { return details::empty_render(req); } @@ -77,7 +78,7 @@ class http_resource * @param req Request passed through http * @return A http_response object **/ - virtual const http_response render_GET(const http_request& req) + virtual const std::shared_ptr render_GET(const http_request& req) { return render(req); } @@ -86,7 +87,7 @@ class http_resource * @param req Request passed through http * @return A http_response object **/ - virtual const http_response render_POST(const http_request& req) + virtual const std::shared_ptr render_POST(const http_request& req) { return render(req); } @@ -95,7 +96,7 @@ class http_resource * @param req Request passed through http * @return A http_response object **/ - virtual const http_response render_PUT(const http_request& req) + virtual const std::shared_ptr render_PUT(const http_request& req) { return render(req); } @@ -104,7 +105,7 @@ class http_resource * @param req Request passed through http * @return A http_response object **/ - virtual const http_response render_HEAD(const http_request& req) + virtual const std::shared_ptr render_HEAD(const http_request& req) { return render(req); } @@ -113,7 +114,7 @@ class http_resource * @param req Request passed through http * @return A http_response object **/ - virtual const http_response render_DELETE(const http_request& req) + virtual const std::shared_ptr render_DELETE(const http_request& req) { return render(req); } @@ -122,7 +123,7 @@ class http_resource * @param req Request passed through http * @return A http_response object **/ - virtual const http_response render_TRACE(const http_request& req) + virtual const std::shared_ptr render_TRACE(const http_request& req) { return render(req); } @@ -131,7 +132,7 @@ class http_resource * @param req Request passed through http * @return A http_response object **/ - virtual const http_response render_OPTIONS(const http_request& req) + virtual const std::shared_ptr render_OPTIONS(const http_request& req) { return render(req); } @@ -140,7 +141,7 @@ class http_resource * @param req Request passed through http * @return A http_response object **/ - virtual const http_response render_CONNECT(const http_request& req) + virtual const std::shared_ptr render_CONNECT(const http_request& req) { return render(req); } @@ -211,7 +212,7 @@ class http_resource **/ http_resource(const http_resource& b) : allowed_methods(b.allowed_methods) { } - http_resource& operator = (const http_resource& b) + http_resource& operator=(const http_resource& b) { allowed_methods = b.allowed_methods; return (*this); diff --git a/src/httpserver/http_response.hpp b/src/httpserver/http_response.hpp index c55012ff..0846878f 100644 --- a/src/httpserver/http_response.hpp +++ b/src/httpserver/http_response.hpp @@ -30,9 +30,7 @@ #include #include #include -#include -#include "httpserver/binders.hpp" #include "httpserver/http_utils.hpp" struct MHD_Connection; @@ -41,84 +39,49 @@ struct MHD_Response; namespace httpserver { -class webserver; -class http_response_builder; - -namespace details -{ - struct http_response_ptr; - ssize_t cb(void*, uint64_t, char*, size_t); -}; - -class bad_caching_attempt: public std::exception -{ - virtual const char* what() const throw() - { - return "You cannot pass ce = 0x0 without key!"; - } -}; - -typedef ssize_t(*cycle_callback_ptr)(char*, size_t); - /** * Class representing an abstraction for an Http Response. It is used from classes using these apis to send information through http protocol. **/ class http_response { public: - http_response(const http_response_builder& builder); + http_response(): + response_code(-1) + { + } + + explicit http_response(int response_code, const std::string& content_type): + response_code(response_code) + { + this->headers[http::http_utils::http_header_content_type] = content_type; + } /** * Copy constructor * @param b The http_response object to copy attributes value from. **/ http_response(const http_response& b): - content(b.content), response_code(b.response_code), - realm(b.realm), - opaque(b.opaque), - reload_nonce(b.reload_nonce), - fp(b.fp), - filename(b.filename), headers(b.headers), footers(b.footers), - cookies(b.cookies), - topics(b.topics), - keepalive_secs(b.keepalive_secs), - keepalive_msg(b.keepalive_msg), - send_topic(b.send_topic), - underlying_connection(b.underlying_connection), - cycle_callback(b.cycle_callback), - get_raw_response(this, b._get_raw_response), - decorate_response(this, b._decorate_response), - enqueue_response(this, b._enqueue_response), - completed(b.completed), - ws(b.ws), - connection_id(b.connection_id), - _get_raw_response(b._get_raw_response), - _decorate_response(b._decorate_response), - _enqueue_response(b._enqueue_response) + cookies(b.cookies) { } - http_response(): - response_code(-1), - fp(-1), - underlying_connection(0x0), - completed(false), - ws(0x0), - connection_id(0x0) + http_response& operator=(const http_response& b) { + if (this == &b) return *this; + + this->response_code = b.response_code; + this->headers = b.headers; + this->footers = b.footers; + this->cookies = b.cookies; + + return *this; } - ~http_response(); - /** - * Method used to get the content from the response. - * @return the content in string form - **/ - const std::string& get_content() + virtual ~http_response() { - return this->content; } /** @@ -178,95 +141,36 @@ class http_response return this->response_code; } - const std::string& get_realm() const - { - return this->realm; - } - - const std::string& get_opaque() const - { - return this->opaque; - } - - bool need_nonce_reload() const + void with_header(const std::string& key, const std::string& value) { - return this->reload_nonce; + headers[key] = value; } - int get_switch_callback() const + void with_footer(const std::string& key, const std::string& value) { - return 0; + footers[key] = value; } - const std::vector& get_topics() const + void with_cookie(const std::string& key, const std::string& value) { - return this->topics; + cookies[key] = value; } - protected: - typedef details::binders::functor_two get_raw_response_t; - typedef details::binders::functor_one decorate_response_t; + void shoutCAST(); - typedef details::binders::functor_two enqueue_response_t; + virtual MHD_Response* get_raw_response(); + virtual void decorate_response(MHD_Response* response); + virtual int enqueue_response(MHD_Connection* connection, MHD_Response* response); + protected: std::string content; int response_code; - std::string realm; - std::string opaque; - bool reload_nonce; - int fp; - std::string filename; + std::map headers; std::map footers; std::map cookies; - std::vector topics; - int keepalive_secs; - std::string keepalive_msg; - std::string send_topic; - struct MHD_Connection* underlying_connection; - cycle_callback_ptr cycle_callback; - - const get_raw_response_t get_raw_response; - const decorate_response_t decorate_response; - const enqueue_response_t enqueue_response; - - bool completed; - - webserver* ws; - MHD_Connection* connection_id; - - void get_raw_response_str(MHD_Response** res, webserver* ws = 0x0); - void get_raw_response_file(MHD_Response** res, webserver* ws = 0x0); - void get_raw_response_switch_r(MHD_Response** res, webserver* ws = 0x0); - - void get_raw_response_deferred(MHD_Response** res, webserver* ws = 0x0); - void decorate_response_str(MHD_Response* res); - void decorate_response_deferred(MHD_Response* res); - int enqueue_response_str(MHD_Connection* connection, MHD_Response* res); - - int enqueue_response_basic(MHD_Connection* connection, - MHD_Response* res - ); - - int enqueue_response_digest(MHD_Connection* connection, - MHD_Response* res - ); - - friend class webserver; - friend struct details::http_response_ptr; - friend class http_response_builder; - friend void clone_response(const http_response& hr, http_response** dhr); - friend ssize_t details::cb(void* cls, uint64_t pos, char* buf, size_t max); - friend std::ostream &operator<< (std::ostream &os, const http_response &r); - private: - http_response& operator=(const http_response& b); - - static ssize_t data_generator (void* cls, uint64_t pos, char* buf, size_t max); - - void (http_response::*_get_raw_response)(MHD_Response**, webserver*); - void (http_response::*_decorate_response)(MHD_Response*); - int (http_response::*_enqueue_response)(MHD_Connection*, MHD_Response*); + friend std::ostream &operator<< (std::ostream &os, const http_response &r); }; std::ostream &operator<< (std::ostream &os, const http_response &r); diff --git a/src/httpserver/http_response_builder.hpp b/src/httpserver/http_response_builder.hpp deleted file mode 100644 index 7755fc64..00000000 --- a/src/httpserver/http_response_builder.hpp +++ /dev/null @@ -1,244 +0,0 @@ -/* - This file is part of libhttpserver - Copyright (C) 2011-2019 Sebastiano Merlino - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 - USA -*/ - -#if !defined (_HTTPSERVER_HPP_INSIDE_) && !defined (HTTPSERVER_COMPILATION) -#error "Only or can be included directly." -#endif - -#ifndef _HTTP_RESPONSE_BUILDER_HPP_ -#define _HTTP_RESPONSE_BUILDER_HPP_ -#include -#include -#include "httpserver/http_response.hpp" - -struct MHD_Connection; - -namespace httpserver -{ - -class webserver; - -namespace http -{ - class header_comparator; -}; - -struct byte_string -{ - public: - byte_string(const char* content_hook, size_t content_length): - content_hook(content_hook), - content_length(content_length) - { - } - - const char* get_content_hook() const - { - return content_hook; - } - - size_t get_content_length() const - { - return content_length; - } - - private: - const char* content_hook; - size_t content_length; -}; - -class http_response_builder -{ - public: - explicit http_response_builder( - const std::string& content_hook, - int response_code = 200, - const std::string& content_type = "text/plain" - ): - _content_hook(content_hook), - _response_code(response_code), - _realm(""), - _opaque(""), - _reload_nonce(false), - _headers(std::map()), - _footers(std::map()), - _cookies(std::map()), - _topics(std::vector()), - _keepalive_secs(-1), - _keepalive_msg(""), - _send_topic(""), - _get_raw_response(&http_response::get_raw_response_str), - _decorate_response(&http_response::decorate_response_str), - _enqueue_response(&http_response::enqueue_response_str) - { - _headers[http::http_utils::http_header_content_type] = content_type; - } - - http_response_builder( - const byte_string& content_hook, - int response_code = 200, - const std::string& content_type = "text/plain" - ): - _content_hook(std::string(content_hook.get_content_hook(), content_hook.get_content_length())), - _response_code(response_code), - _realm(""), - _opaque(""), - _reload_nonce(false), - _headers(std::map()), - _footers(std::map()), - _cookies(std::map()), - _topics(std::vector()), - _keepalive_secs(-1), - _keepalive_msg(""), - _send_topic(""), - _get_raw_response(&http_response::get_raw_response_str), - _decorate_response(&http_response::decorate_response_str), - _enqueue_response(&http_response::enqueue_response_str) - { - _headers[http::http_utils::http_header_content_type] = content_type; - } - - http_response_builder(const http_response_builder& b): - _content_hook(b._content_hook), - _response_code(b._response_code), - _realm(b._realm), - _opaque(b._opaque), - _reload_nonce(b._reload_nonce), - _headers(b._headers), - _footers(b._footers), - _cookies(b._cookies), - _topics(b._topics), - _keepalive_secs(b._keepalive_secs), - _keepalive_msg(b._keepalive_msg), - _send_topic(b._send_topic), - _get_raw_response(b._get_raw_response), - _decorate_response(b._decorate_response), - _enqueue_response(b._enqueue_response) - { - } - - http_response_builder& operator=(const http_response_builder& b) - { - _content_hook = b._content_hook; - _response_code = b._response_code; - _realm = b._realm; - _opaque = b._opaque; - _reload_nonce = b._reload_nonce; - _headers = b._headers; - _footers = b._footers; - _cookies = b._cookies; - _topics = b._topics; - _keepalive_secs = b._keepalive_secs; - _keepalive_msg = b._keepalive_msg; - _send_topic = b._send_topic; - _get_raw_response = b._get_raw_response; - _decorate_response = b._decorate_response; - _enqueue_response = b._enqueue_response; - return *this; - } - - ~http_response_builder() - { - } - - http_response_builder& string_response() - { - return *this; - } - - http_response_builder& file_response() - { - _get_raw_response = &http_response::get_raw_response_file; - return *this; - } - - http_response_builder& basic_auth_fail_response(const std::string& realm = "") - { - _realm = realm; - _enqueue_response = &http_response::enqueue_response_basic; - return *this; - } - - http_response_builder& digest_auth_fail_response( - const std::string& realm = "", - const std::string& opaque = "", - bool reload_nonce = false - ) - { - _realm = realm; - _opaque = opaque; - _reload_nonce = reload_nonce; - _enqueue_response = &http_response::enqueue_response_digest; - return *this; - } - - http_response_builder& deferred_response(cycle_callback_ptr cycle_callback) - { - _cycle_callback = cycle_callback; - _get_raw_response = &http_response::get_raw_response_deferred; - _decorate_response = &http_response::decorate_response_deferred; - return *this; - } - - http_response_builder& shoutCAST_response() - { - _response_code |= http::http_utils::shoutcast_response; - return *this; - } - - http_response_builder& with_header(const std::string& key, const std::string& value) - { - _headers[key] = value; return *this; - } - - http_response_builder& with_footer(const std::string& key, const std::string& value) - { - _footers[key] = value; return *this; - } - - http_response_builder& with_cookie(const std::string& key, const std::string& value) - { - _cookies[key] = value; return *this; - } - - private: - std::string _content_hook; - int _response_code; - std::string _realm; - std::string _opaque; - bool _reload_nonce; - std::map _headers; - std::map _footers; - std::map _cookies; - std::vector _topics; - int _keepalive_secs; - std::string _keepalive_msg; - std::string _send_topic; - cycle_callback_ptr _cycle_callback; - - void (http_response::*_get_raw_response)(MHD_Response**, webserver*); - void (http_response::*_decorate_response)(MHD_Response*); - int (http_response::*_enqueue_response)(MHD_Connection*, MHD_Response*); - - friend class http_response; -}; - -}; -#endif //_HTTP_RESPONSE_BUILDER_HPP_ diff --git a/src/httpserver/http_utils.hpp b/src/httpserver/http_utils.hpp index 32c675de..f9608f6f 100644 --- a/src/httpserver/http_utils.hpp +++ b/src/httpserver/http_utils.hpp @@ -224,6 +224,9 @@ class http_utils static const std::string http_post_encoding_form_urlencoded; static const std::string http_post_encoding_multipart_formdata; + + static const std::string text_plain; + static std::vector tokenize_url(const std::string&, const char separator = '/' ); diff --git a/src/httpserver/string_response.hpp b/src/httpserver/string_response.hpp new file mode 100644 index 00000000..c0177a74 --- /dev/null +++ b/src/httpserver/string_response.hpp @@ -0,0 +1,79 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011-2019 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#if !defined (_HTTPSERVER_HPP_INSIDE_) && !defined (HTTPSERVER_COMPILATION) +#error "Only or can be included directly." +#endif + +#ifndef _STRING_RESPONSE_HPP_ +#define _STRING_RESPONSE_HPP_ + +#include "httpserver/http_response.hpp" + +namespace httpserver +{ + +class string_response : public http_response +{ + public: + string_response(): + http_response(), + content("") + { + } + + explicit string_response( + const std::string& content, + int response_code = http::http_utils::http_ok, + const std::string& content_type = http::http_utils::text_plain + ): + http_response(response_code, content_type), + content(content) + { + } + + string_response(const string_response& other): + http_response(other), + content(other.content) + { + } + + string_response& operator=(const string_response& b) + { + if (this == &b) return *this; + + (http_response&) (*this) = b; + this->content = b.content; + + return *this; + } + + ~string_response() + { + } + + MHD_Response* get_raw_response(); + + private: + std::string content; +}; + +} +#endif // _STRING_RESPONSE_HPP_ diff --git a/src/httpserver/webserver.hpp b/src/httpserver/webserver.hpp index 6b931bde..793d8d79 100644 --- a/src/httpserver/webserver.hpp +++ b/src/httpserver/webserver.hpp @@ -194,9 +194,9 @@ class webserver static void* select(void* self); static void* cleaner(void* self); - const http_response method_not_allowed_page(details::modded_request* mr) const; - const http_response internal_error_page(details::modded_request* mr, bool force_our = false) const; - const http_response not_found_page(details::modded_request* mr) const; + const std::shared_ptr method_not_allowed_page(details::modded_request* mr) const; + const std::shared_ptr internal_error_page(details::modded_request* mr, bool force_our = false) const; + const std::shared_ptr not_found_page(details::modded_request* mr) const; static void request_completed(void *cls, struct MHD_Connection *connection, void **con_cls, diff --git a/src/string_response.cpp b/src/string_response.cpp new file mode 100644 index 00000000..ad0a1a6c --- /dev/null +++ b/src/string_response.cpp @@ -0,0 +1,38 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011-2019 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#include "string_response.hpp" + +using namespace std; + +namespace httpserver +{ + +MHD_Response* string_response::get_raw_response() +{ + size_t size = &(*content.end()) - &(*content.begin()); + return MHD_create_response_from_buffer( + size, + (void*) content.c_str(), + MHD_RESPMEM_PERSISTENT + ); +} + +} diff --git a/src/webserver.cpp b/src/webserver.cpp index b86b32a3..bb773def 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -47,8 +47,8 @@ #include "http_utils.hpp" #include "http_resource.hpp" #include "http_response.hpp" +#include "string_response.hpp" #include "http_request.hpp" -#include "http_response_builder.hpp" #include "details/http_endpoint.hpp" #include "string_utilities.hpp" #include "create_webserver.hpp" @@ -61,8 +61,6 @@ #define SOCK_CLOEXEC 02000000 #endif -#define NEW_OR_MOVE(TYPE, VALUE) new TYPE(VALUE) - using namespace std; namespace httpserver @@ -587,7 +585,7 @@ void webserver::upgrade_handler (void *cls, struct MHD_Connection* connection, { } -const http_response webserver::not_found_page(details::modded_request* mr) const +const std::shared_ptr webserver::not_found_page(details::modded_request* mr) const { if(not_found_resource != 0x0) { @@ -595,11 +593,11 @@ const http_response webserver::not_found_page(details::modded_request* mr) const } else { - return http_response_builder(NOT_FOUND_ERROR, http_utils::http_not_found).string_response(); + return std::shared_ptr(new string_response(NOT_FOUND_ERROR, http_utils::http_not_found)); } } -const http_response webserver::method_not_allowed_page(details::modded_request* mr) const +const std::shared_ptr webserver::method_not_allowed_page(details::modded_request* mr) const { if(method_not_allowed_resource != 0x0) { @@ -607,11 +605,11 @@ const http_response webserver::method_not_allowed_page(details::modded_request* } else { - return http_response_builder(METHOD_ERROR, http_utils::http_method_not_allowed).string_response(); + return std::shared_ptr(new string_response(METHOD_ERROR, http_utils::http_method_not_allowed)); } } -const http_response webserver::internal_error_page(details::modded_request* mr, bool force_our) const +const std::shared_ptr webserver::internal_error_page(details::modded_request* mr, bool force_our) const { if(internal_error_resource != 0x0 && !force_our) { @@ -619,8 +617,7 @@ const http_response webserver::internal_error_page(details::modded_request* mr, } else { - http_response hr = http_response_builder(GENERIC_ERROR, http_utils::http_internal_server_error, "text/plain").string_response(); - return hr; + return std::shared_ptr(new string_response(GENERIC_ERROR, http_utils::http_internal_server_error, "text/plain")); } } @@ -846,63 +843,61 @@ int webserver::finalize_answer( { if(hrm->is_allowed(method)) { - mr->dhrs = NEW_OR_MOVE(http_response, ((hrm)->*(mr->callback))(*mr->dhr)); //copy in memory (move in case) + mr->dhrs = ((hrm)->*(mr->callback))(*mr->dhr); //copy in memory (move in case) if (mr->dhrs->get_response_code() == -1) { - mr->dhrs = NEW_OR_MOVE(http_response, internal_error_page(mr)); + mr->dhrs = internal_error_page(mr); } } else { - mr->dhrs = NEW_OR_MOVE(http_response, method_not_allowed_page(mr)); + mr->dhrs = method_not_allowed_page(mr); } } catch(const std::exception& e) { - mr->dhrs = NEW_OR_MOVE(http_response, internal_error_page(mr)); + mr->dhrs = internal_error_page(mr); } catch(...) { - mr->dhrs = NEW_OR_MOVE(http_response, internal_error_page(mr)); + mr->dhrs = internal_error_page(mr); } } else { - mr->dhrs = NEW_OR_MOVE(http_response, not_found_page(mr)); + mr->dhrs = not_found_page(mr); } - mr->dhrs->underlying_connection = connection; - try { try { - mr->dhrs->get_raw_response(&raw_response, this); + raw_response = mr->dhrs->get_raw_response(); } catch(const std::invalid_argument& iae) { - mr->dhrs = NEW_OR_MOVE(http_response, not_found_page(mr)); - mr->dhrs->get_raw_response(&raw_response, this); + mr->dhrs = not_found_page(mr); + raw_response = mr->dhrs->get_raw_response(); } catch(const std::exception& e) { - mr->dhrs = NEW_OR_MOVE(http_response, internal_error_page(mr)); - mr->dhrs->get_raw_response(&raw_response, this); + mr->dhrs = internal_error_page(mr); + raw_response = mr->dhrs->get_raw_response(); } catch(...) { - mr->dhrs = NEW_OR_MOVE(http_response, internal_error_page(mr)); - mr->dhrs->get_raw_response(&raw_response, this); + mr->dhrs = internal_error_page(mr); + raw_response = mr->dhrs->get_raw_response(); } } catch(...) // catches errors in internal error page { - mr->dhrs = NEW_OR_MOVE(http_response, internal_error_page(mr, true)); - mr->dhrs->get_raw_response(&raw_response, this); + mr->dhrs = internal_error_page(mr, true); + raw_response = mr->dhrs->get_raw_response(); } mr->dhrs->decorate_response(raw_response); to_ret = mr->dhrs->enqueue_response(connection, raw_response); - MHD_destroy_response (raw_response); + MHD_destroy_response(raw_response); return to_ret; } diff --git a/test/integ/authentication.cpp b/test/integ/authentication.cpp index f278c916..79193fd9 100644 --- a/test/integ/authentication.cpp +++ b/test/integ/authentication.cpp @@ -48,33 +48,33 @@ size_t writefunc(void *ptr, size_t size, size_t nmemb, std::string *s) class user_pass_resource : public httpserver::http_resource { public: - const httpserver::http_response render_GET(const httpserver::http_request& req) + const shared_ptr render_GET(const http_request& req) { if (req.get_user() != "myuser" || req.get_pass() != "mypass") { - return httpserver::http_response_builder("FAIL").basic_auth_fail_response("test@example.com"); + return shared_ptr(new basic_auth_fail_response("FAIL", "test@example.com")); } - return httpserver::http_response_builder(req.get_user() + " " + req.get_pass(), 200, "text/plain").string_response(); + return shared_ptr(new string_response(req.get_user() + " " + req.get_pass(), 200, "text/plain")); } }; class digest_resource : public httpserver::http_resource { public: - const httpserver::http_response render_GET(const httpserver::http_request& req) + const shared_ptr render_GET(const http_request& req) { if (req.get_digested_user() == "") { - return httpserver::http_response_builder("FAIL").digest_auth_fail_response("test@example.com", MY_OPAQUE, true); + return shared_ptr(new digest_auth_fail_response("FAIL", "test@example.com", MY_OPAQUE, true)); } else { - bool reload_nonce = false;; + bool reload_nonce = false; if(!req.check_digest_auth("test@example.com", "mypass", 300, reload_nonce)) { - return httpserver::http_response_builder("FAIL").digest_auth_fail_response("test@example.com", MY_OPAQUE, reload_nonce); + return shared_ptr(new digest_auth_fail_response("FAIL", "test@example.com", MY_OPAQUE, reload_nonce)); } } - return httpserver::http_response_builder("SUCCESS", 200, "text/plain").string_response(); + return shared_ptr(new string_response("SUCCESS", 200, "text/plain")); } }; diff --git a/test/integ/ban_system.cpp b/test/integ/ban_system.cpp index 3deda0bb..505c7638 100644 --- a/test/integ/ban_system.cpp +++ b/test/integ/ban_system.cpp @@ -38,9 +38,9 @@ size_t writefunc(void *ptr, size_t size, size_t nmemb, std::string *s) class ok_resource : public http_resource { public: - const http_response render_GET(const http_request& req) + const shared_ptr render_GET(const http_request& req) { - return http_response_builder("OK", 200, "text/plain").string_response(); + return shared_ptr(new string_response("OK", 200, "text/plain")); } }; diff --git a/test/integ/basic.cpp b/test/integ/basic.cpp index df9eec9b..27b99977 100644 --- a/test/integ/basic.cpp +++ b/test/integ/basic.cpp @@ -49,148 +49,148 @@ size_t headerfunc(void *ptr, size_t size, size_t nmemb, map* ss) class simple_resource : public http_resource { public: - const http_response render_GET(const http_request& req) + const shared_ptr render_GET(const http_request& req) { - return http_response_builder("OK", 200, "text/plain").string_response(); + return shared_ptr(new string_response("OK", 200, "text/plain")); } - const http_response render_POST(const http_request& req) + const shared_ptr render_POST(const http_request& req) { - return http_response_builder(req.get_arg("arg1")+req.get_arg("arg2"), 200, "text/plain").string_response(); + return shared_ptr(new string_response(req.get_arg("arg1")+req.get_arg("arg2"), 200, "text/plain")); } }; class args_resource : public http_resource { public: - const http_response render_GET(const http_request& req) + const shared_ptr render_GET(const http_request& req) { - return http_response_builder(req.get_arg("arg") + req.get_arg("arg2"), 200, "text/plain").string_response(); + return shared_ptr(new string_response(req.get_arg("arg") + req.get_arg("arg2"), 200, "text/plain")); } }; class long_content_resource : public http_resource { public: - const http_response render_GET(const http_request& req) + const shared_ptr render_GET(const http_request& req) { - return http_response_builder(lorem_ipsum, 200, "text/plain").string_response(); + return shared_ptr(new string_response(lorem_ipsum, 200, "text/plain")); } }; class header_set_test_resource : public http_resource { public: - const http_response render_GET(const http_request& req) + const shared_ptr render_GET(const http_request& req) { - http_response_builder hrb("OK", 200, "text/plain"); - hrb.with_header("KEY", "VALUE"); - return hrb.string_response(); + shared_ptr hrb(new string_response("OK", 200, "text/plain")); + hrb->with_header("KEY", "VALUE"); + return hrb; } }; class cookie_set_test_resource : public http_resource { public: - const http_response render_GET(const http_request& req) + const shared_ptr render_GET(const http_request& req) { - http_response_builder hrb("OK", 200, "text/plain"); - hrb.with_cookie("MyCookie", "CookieValue"); - return hrb.string_response(); + shared_ptr hrb(new string_response("OK", 200, "text/plain")); + hrb->with_cookie("MyCookie", "CookieValue"); + return hrb; } }; class cookie_reading_resource : public http_resource { public: - const http_response render_GET(const http_request& req) + const shared_ptr render_GET(const http_request& req) { - return http_response_builder(req.get_cookie("name"), 200, "text/plain").string_response(); + return shared_ptr(new string_response(req.get_cookie("name"), 200, "text/plain")); } }; class header_reading_resource : public http_resource { public: - const http_response render_GET(const http_request& req) + const shared_ptr render_GET(const http_request& req) { - return http_response_builder(req.get_header("MyHeader"), 200, "text/plain").string_response(); + return shared_ptr(new string_response(req.get_header("MyHeader"), 200, "text/plain")); } }; class complete_test_resource : public http_resource { public: - const http_response render_GET(const http_request& req) + const shared_ptr render_GET(const http_request& req) { - return http_response_builder("OK", 200, "text/plain").string_response(); + return shared_ptr(new string_response("OK", 200, "text/plain")); } - const http_response render_POST(const http_request& req) + const shared_ptr render_POST(const http_request& req) { - return http_response_builder("OK", 200, "text/plain").string_response(); + return shared_ptr(new string_response("OK", 200, "text/plain")); } - const http_response render_PUT(const http_request& req) + const shared_ptr render_PUT(const http_request& req) { - return http_response_builder("OK", 200, "text/plain").string_response(); + return shared_ptr(new string_response("OK", 200, "text/plain")); } - const http_response render_DELETE(const http_request& req) + const shared_ptr render_DELETE(const http_request& req) { - return http_response_builder("OK", 200, "text/plain").string_response(); + return shared_ptr(new string_response("OK", 200, "text/plain")); } - const http_response render_CONNECT(const http_request& req) + const shared_ptr render_CONNECT(const http_request& req) { - return http_response_builder("OK", 200, "text/plain").string_response(); + return shared_ptr(new string_response("OK", 200, "text/plain")); } }; class only_render_resource : public http_resource { public: - const http_response render(const http_request& req) + const shared_ptr render(const http_request& req) { - return http_response_builder("OK", 200, "text/plain").string_response(); + return shared_ptr(new string_response("OK", 200, "text/plain")); } }; class ok_resource : public http_resource { public: - const http_response render_GET(const http_request& req) + const shared_ptr render_GET(const http_request& req) { - return http_response_builder("OK", 200, "text/plain").string_response(); + return shared_ptr(new string_response("OK", 200, "text/plain")); } }; class nok_resource : public http_resource { public: - const http_response render_GET(const http_request& req) + const shared_ptr render_GET(const http_request& req) { - return http_response_builder("NOK", 200, "text/plain").string_response(); + return shared_ptr(new string_response("NOK", 200, "text/plain")); } }; class no_response_resource : public http_resource { public: - const http_response render_GET(const http_request& req) + const shared_ptr render_GET(const http_request& req) { - return http_response(); + return shared_ptr(new http_response()); } }; class file_response_resource : public http_resource { public: - const http_response render_GET(const http_request& req) + const shared_ptr render_GET(const http_request& req) { - return http_response_builder("test_content", 200, "text/plain").file_response(); + return shared_ptr(new file_response("test_content", 200, "text/plain")); } }; class exception_resource : public http_resource { public: - const http_response render_GET(const http_request& req) + const shared_ptr render_GET(const http_request& req) { throw std::domain_error("invalid"); } @@ -199,7 +199,7 @@ class exception_resource : public http_resource class error_resource : public http_resource { public: - const http_response render_GET(const http_request& req) + const shared_ptr render_GET(const http_request& req) { throw "invalid"; } @@ -213,10 +213,10 @@ class print_request_resource : public http_resource this->ss = ss; } - const http_response render_GET(const http_request& req) + const shared_ptr render_GET(const http_request& req) { (*ss) << req; - return http_response_builder("OK", 200, "text/plain").string_response(); + return shared_ptr(new string_response("OK", 200, "text/plain")); } private: @@ -231,15 +231,15 @@ class print_response_resource : public http_resource this->ss = ss; } - const http_response render_GET(const http_request& req) + const shared_ptr render_GET(const http_request& req) { - http_response hresp(http_response_builder("OK", 200, "text/plain") - .with_header("MyResponseHeader", "MyResponseHeaderValue") - .with_footer("MyResponseFooter", "MyResponseFooterValue") - .with_cookie("MyResponseCookie", "MyResponseCookieValue") - .string_response() - ); - (*ss) << hresp; + shared_ptr hresp(new string_response("OK", 200, "text/plain")); + + hresp->with_header("MyResponseHeader", "MyResponseHeaderValue"); + hresp->with_footer("MyResponseFooter", "MyResponseFooterValue"); + hresp->with_cookie("MyResponseCookie", "MyResponseCookieValue"); + + (*ss) << *hresp; return hresp; } diff --git a/test/integ/deferred.cpp b/test/integ/deferred.cpp index 2db6fc0f..c853ab06 100644 --- a/test/integ/deferred.cpp +++ b/test/integ/deferred.cpp @@ -62,12 +62,12 @@ ssize_t test_callback (char* buf, size_t max) } } -class deferred_resource : public httpserver::http_resource +class deferred_resource : public http_resource { public: - const httpserver::http_response render_GET(const httpserver::http_request& req) + const shared_ptr render_GET(const http_request& req) { - return httpserver::http_response_builder("cycle callback response").deferred_response(test_callback); + return shared_ptr(new deferred_response(test_callback, "cycle callback response")); } }; diff --git a/test/integ/threaded.cpp b/test/integ/threaded.cpp index 62f7d973..14cb4c35 100644 --- a/test/integ/threaded.cpp +++ b/test/integ/threaded.cpp @@ -30,9 +30,9 @@ using namespace std; class ok_resource : public http_resource { public: - const http_response render_GET(const http_request& req) + const shared_ptr render_GET(const http_request& req) { - return http_response_builder("OK", 200, "text/plain").string_response(); + return shared_ptr(new string_response("OK", 200, "text/plain")); } }; diff --git a/test/integ/ws_start_stop.cpp b/test/integ/ws_start_stop.cpp index 545a0516..2dbf7d16 100644 --- a/test/integ/ws_start_stop.cpp +++ b/test/integ/ws_start_stop.cpp @@ -44,23 +44,23 @@ size_t writefunc(void *ptr, size_t size, size_t nmemb, std::string *s) return size*nmemb; } -class ok_resource : public httpserver::http_resource +class ok_resource : public http_resource { public: - const httpserver::http_response render_GET(const httpserver::http_request& req) + const shared_ptr render_GET(const http_request& req) { - return httpserver::http_response_builder("OK", 200, "text/plain").string_response(); + return shared_ptr(new string_response("OK", 200, "text/plain")); } }; -const httpserver::http_response not_found_custom(const httpserver::http_request& req) +const shared_ptr not_found_custom(const http_request& req) { - return httpserver::http_response_builder("Not found custom", 404, "text/plain").string_response(); + return shared_ptr(new string_response("Not found custom", 404, "text/plain")); } -const httpserver::http_response not_allowed_custom(const httpserver::http_request& req) +const shared_ptr not_allowed_custom(const http_request& req) { - return httpserver::http_response_builder("Not allowed custom", 405, "text/plain").string_response(); + return shared_ptr(new string_response("Not allowed custom", 405, "text/plain")); } LT_BEGIN_SUITE(ws_start_stop_suite) From 1e61eae1f1874a26c447d0fa26ef663073ab2312 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sat, 12 Jan 2019 09:02:20 +0000 Subject: [PATCH 375/623] Add -std=c++11 option to configure --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index bc33cae3..b3ae5e70 100644 --- a/configure.ac +++ b/configure.ac @@ -113,7 +113,7 @@ AC_CHECK_HEADER([microhttpd.h], [AC_MSG_ERROR(["microhttpd.h not found"])] ) -CXXFLAGS="-DHTTPSERVER_COMPILATION -D_REENTRANT $LIBMICROHTTPD_CFLAGS $CXXFLAGS" +CXXFLAGS="-std=c++11 -DHTTPSERVER_COMPILATION -D_REENTRANT $LIBMICROHTTPD_CFLAGS $CXXFLAGS" LDFLAGS="$LIBMICROHTTPD_LIBS $REGEX_LIBS $LDFLAGS" AC_MSG_CHECKING([whether to build with TCP_FASTOPEN support]) From 00e6761e89ce580ad6a777176f77ce968faeced9 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sat, 12 Jan 2019 12:08:14 -0800 Subject: [PATCH 376/623] Added g++-8, clang6 and clang7 --- .travis.yml | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/.travis.yml b/.travis.yml index 8d3f2805..80c9ac26 100644 --- a/.travis.yml +++ b/.travis.yml @@ -170,6 +170,15 @@ matrix: - g++-7 env: - MATRIX_EVAL="CC=gcc-7 && CXX=g++-7" + - os: linux + addons: + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - g++-8 + env: + - MATRIX_EVAL="CC=gcc-8 && CXX=g++-8" #- os: osx # osx_image: xcode8 # env: @@ -249,6 +258,24 @@ matrix: - clang-5.0 env: - MATRIX_EVAL="CC=clang-5.0 && CXX=clang++-5.0" + - os: linux + addons: + apt: + sources: + - llvm-toolchain-trusty-6.0 + packages: + - clang-5.0 + env: + - MATRIX_EVAL="CC=clang-6.0 && CXX=clang++-6.0" + - os: linux + addons: + apt: + sources: + - llvm-toolchain-trusty-7 + packages: + - clang-5.0 + env: + - MATRIX_EVAL="CC=clang-7 && CXX=clang++-7" - os: linux compiler: clang addons: From 3009e981e83083b7efc4daaacfbf93aca248c96b Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sat, 12 Jan 2019 12:31:13 -0800 Subject: [PATCH 377/623] Fixing the packages selected for clang6 and 7 --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 80c9ac26..19919f28 100644 --- a/.travis.yml +++ b/.travis.yml @@ -264,7 +264,7 @@ matrix: sources: - llvm-toolchain-trusty-6.0 packages: - - clang-5.0 + - clang-6.0 env: - MATRIX_EVAL="CC=clang-6.0 && CXX=clang++-6.0" - os: linux @@ -273,7 +273,7 @@ matrix: sources: - llvm-toolchain-trusty-7 packages: - - clang-5.0 + - clang-7.0 env: - MATRIX_EVAL="CC=clang-7 && CXX=clang++-7" - os: linux From 466a558191077ae333c80cfe8f6a4e5dfde2384a Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sat, 12 Jan 2019 13:57:31 -0800 Subject: [PATCH 378/623] Asking for test toolchain for clang 6 and 7 --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index 19919f28..487bea93 100644 --- a/.travis.yml +++ b/.travis.yml @@ -263,6 +263,7 @@ matrix: apt: sources: - llvm-toolchain-trusty-6.0 + - ubuntu-toolchain-r-test packages: - clang-6.0 env: @@ -272,6 +273,7 @@ matrix: apt: sources: - llvm-toolchain-trusty-7 + - ubuntu-toolchain-r-test packages: - clang-7.0 env: From 56823a0c98f2b1b3e4cab56b161c5b774a44df91 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sat, 12 Jan 2019 14:20:07 -0800 Subject: [PATCH 379/623] Fix clang-7 package name --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 487bea93..6156e9ce 100644 --- a/.travis.yml +++ b/.travis.yml @@ -275,7 +275,7 @@ matrix: - llvm-toolchain-trusty-7 - ubuntu-toolchain-r-test packages: - - clang-7.0 + - clang-7 env: - MATRIX_EVAL="CC=clang-7 && CXX=clang++-7" - os: linux From 070976fff548d4283d713f24938cc23d0928205f Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 13 Jan 2019 02:40:14 +0000 Subject: [PATCH 380/623] Remove unused code --- src/httpserver/webserver.hpp | 1 - src/webserver.cpp | 7 ------- 2 files changed, 8 deletions(-) diff --git a/src/httpserver/webserver.hpp b/src/httpserver/webserver.hpp index 793d8d79..b7191232 100644 --- a/src/httpserver/webserver.hpp +++ b/src/httpserver/webserver.hpp @@ -189,7 +189,6 @@ class webserver std::set allowances; std::vector daemons; - std::vector threads; static void* select(void* self); static void* cleaner(void* self); diff --git a/src/webserver.cpp b/src/webserver.cpp index bb773def..4f1e7a74 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -381,13 +381,6 @@ bool webserver::stop() this->running = false; pthread_cond_signal(&mutexcond); pthread_mutex_unlock(&mutexwait); - for(unsigned int i = 0; i < threads.size(); ++i) - { - void* t_res; - pthread_join(threads[i], &t_res); - free(t_res); - } - threads.clear(); typedef vector::const_iterator daemon_item_it; for(daemon_item_it it = daemons.begin(); it != daemons.end(); ++it) From 27a430f64a1a56ebbf83cf6950f884b916af1b66 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 13 Jan 2019 02:53:53 +0000 Subject: [PATCH 381/623] Removing daemon_item. Simplifying the logic --- src/httpserver/webserver.hpp | 3 +-- src/webserver.cpp | 35 +++++------------------------------ 2 files changed, 6 insertions(+), 32 deletions(-) diff --git a/src/httpserver/webserver.hpp b/src/httpserver/webserver.hpp index b7191232..049c94f0 100644 --- a/src/httpserver/webserver.hpp +++ b/src/httpserver/webserver.hpp @@ -59,7 +59,6 @@ struct httpserver_ska; }; namespace details { - struct daemon_item; struct modded_request; } @@ -188,7 +187,7 @@ class webserver std::set bans; std::set allowances; - std::vector daemons; + struct MHD_Daemon* daemon; static void* select(void* self); static void* cleaner(void* self); diff --git a/src/webserver.cpp b/src/webserver.cpp index 4f1e7a74..090f10d2 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -66,26 +66,6 @@ using namespace std; namespace httpserver { -namespace details -{ - -struct daemon_item -{ - webserver* ws; - struct MHD_Daemon* daemon; - daemon_item(webserver* ws, struct MHD_Daemon* daemon): - ws(ws), - daemon(daemon) - { - } - ~daemon_item() - { - MHD_stop_daemon (this->daemon); - } -}; - -} - using namespace http; int policy_callback (void *, const struct sockaddr*, socklen_t); @@ -329,16 +309,16 @@ bool webserver::start(bool blocking) start_conf |= MHD_USE_TCP_FASTOPEN; #endif - struct MHD_Daemon* daemon = NULL; + this->daemon = NULL; if(bind_address == 0x0) { - daemon = MHD_start_daemon + this->daemon = MHD_start_daemon ( start_conf, this->port, &policy_callback, this, &answer_to_connection, this, MHD_OPTION_ARRAY, &iov[0], MHD_OPTION_END ); } else { - daemon = MHD_start_daemon + this->daemon = MHD_start_daemon ( start_conf, 1, &policy_callback, this, &answer_to_connection, this, MHD_OPTION_ARRAY, @@ -346,12 +326,10 @@ bool webserver::start(bool blocking) ); } - if(NULL == daemon) + if(this->daemon == NULL) { throw std::invalid_argument("Unable to connect daemon to port: " + this->port); } - details::daemon_item* di = new details::daemon_item(this, daemon); - daemons.push_back(di); bool value_onclose = false; @@ -381,11 +359,8 @@ bool webserver::stop() this->running = false; pthread_cond_signal(&mutexcond); pthread_mutex_unlock(&mutexwait); - typedef vector::const_iterator daemon_item_it; - for(daemon_item_it it = daemons.begin(); it != daemons.end(); ++it) - delete *it; - daemons.clear(); + MHD_stop_daemon(this->daemon); shutdown(bind_socket, 2); From 032dd9ed9bdc5040e3a3358761611f109ea4bd25 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 13 Jan 2019 03:21:40 +0000 Subject: [PATCH 382/623] Add valgrind memcheck test to travis --- .travis.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/.travis.yml b/.travis.yml index 6156e9ce..bf311862 100644 --- a/.travis.yml +++ b/.travis.yml @@ -55,6 +55,8 @@ install: ../configure --disable-fastopen --build `../config.guess` --host aarch64-linux-gnu CC="gcc -B$HOME/linker_bin" CXX="g++ -B$HOME/linker_bin"; elif [ "$CROSS_COMPILE" = "1" ] && [ "$ARM_ARCH_DIR" = "arm-linux-gnueabi" ]; then ../configure --disable-fastopen --build `../config.guess` --host arm-linux-gnueabi; + elif [ "$VALGRIND" = "valgrind" ]; then + ../configure --enable-debug --disable-fastopen --disable-valgrind-helgrind --disable-valgrind-drd --disable-valgrind-sgcheck; else ../configure --disable-fastopen; fi @@ -84,6 +86,7 @@ script: fi else make check ; + if [ "$VALGRIND" = "valgrind" ]; then make check-valgrind; fi cat test/test-suite.log ; ls -l /usr/local/lib/ ; ls -l /usr/lib/ ; @@ -179,6 +182,17 @@ matrix: - g++-8 env: - MATRIX_EVAL="CC=gcc-8 && CXX=g++-8" + - os: linux + addons: + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - g++-7 + - valgrind + - valgrind-dbg + env: + - MATRIX_EVAL="CC=gcc-7 && CXX=g++-7 && VALGRIND=valgrind" #- os: osx # osx_image: xcode8 # env: From e929341c2148d41891974bc378323aa513573c15 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 13 Jan 2019 03:31:06 +0000 Subject: [PATCH 383/623] Adding missing semicolon to travis config --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index bf311862..68b86ec5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -86,7 +86,7 @@ script: fi else make check ; - if [ "$VALGRIND" = "valgrind" ]; then make check-valgrind; fi + if [ "$VALGRIND" = "valgrind" ]; then make check-valgrind; fi; cat test/test-suite.log ; ls -l /usr/local/lib/ ; ls -l /usr/lib/ ; From 3a98efec4d56f20bd2b5ed59fdef088176c4ef5f Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 13 Jan 2019 05:39:05 +0000 Subject: [PATCH 384/623] Move make check into an independent execution on travis --- .travis.yml | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/.travis.yml b/.travis.yml index 6156e9ce..c059cadf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -82,13 +82,11 @@ script: qemu-arm -L /usr/arm-linux-gnueabi/ ./.libs/http_utils ; qemu-arm -L /usr/arm-linux-gnueabi/ ./.libs/threaded ; fi - else - make check ; - cat test/test-suite.log ; - ls -l /usr/local/lib/ ; - ls -l /usr/lib/ ; - if [ "$TRAVIS_OS_NAME" = "linux" ]; then cd ../src/; cppcheck --error-exitcode=1 .; cd ../build; fi - fi + - make check + - cat test/test-suite.log + - ls -l /usr/local/lib/ + - ls -l /usr/lib/ + - if [ "$TRAVIS_OS_NAME" = "linux" ]; then cd ../src/; cppcheck --error-exitcode=1 .; cd ../build; fi after_success: - if [ "$DEBUG" = "debug" ] && [ "$COVERAGE" = "coverage" ] && [ "$TRAVIS_OS_NAME" = "linux" ]; then bash <(curl -s https://codecov.io/bash); fi matrix: From a825c08b92a9417cef349da1a6ad6d4cabf02576 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 13 Jan 2019 05:53:07 +0000 Subject: [PATCH 385/623] Fix script --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index c059cadf..ef28c78f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -82,6 +82,7 @@ script: qemu-arm -L /usr/arm-linux-gnueabi/ ./.libs/http_utils ; qemu-arm -L /usr/arm-linux-gnueabi/ ./.libs/threaded ; fi + fi - make check - cat test/test-suite.log - ls -l /usr/local/lib/ From 3065dea4c191dca9fb248fc217a3edce2f6949d7 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 13 Jan 2019 06:20:32 +0000 Subject: [PATCH 386/623] Added more examples --- examples/Makefile.am | 5 +-- examples/custom_error.cpp | 54 ++++++++++++++++++++++++++++++++ examples/minimal_hello_world.cpp | 40 +++++++++++++++++++++++ 3 files changed, 97 insertions(+), 2 deletions(-) create mode 100644 examples/custom_error.cpp create mode 100644 examples/minimal_hello_world.cpp diff --git a/examples/Makefile.am b/examples/Makefile.am index e0c44ac6..a820556d 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -19,8 +19,9 @@ LDADD = $(top_builddir)/src/libhttpserver.la AM_CPPFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/src/httpserver/ METASOURCES = AUTO -noinst_PROGRAMS = hello_world service +noinst_PROGRAMS = hello_world service minimal_hello_world custom_error hello_world_SOURCES = hello_world.cpp service_SOURCES = service.cpp - +minimal_hello_world_SOURCES = minimal_hello_world.cpp +custom_error_SOURCES = custom_error.cpp diff --git a/examples/custom_error.cpp b/examples/custom_error.cpp new file mode 100644 index 00000000..034f334c --- /dev/null +++ b/examples/custom_error.cpp @@ -0,0 +1,54 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#include + +using namespace httpserver; + +const std::shared_ptr not_found_custom(const http_request& req) +{ + return std::shared_ptr(new string_response("Not found custom", 404, "text/plain")); +} + +const std::shared_ptr not_allowed_custom(const http_request& req) +{ + return std::shared_ptr(new string_response("Not allowed custom", 405, "text/plain")); +} + +class hello_world_resource : public http_resource { +public: + const std::shared_ptr render(const http_request&) { + return std::shared_ptr(new string_response("Hello, World!")); + } +}; + +int main(int argc, char** argv) { + webserver ws = create_webserver(8080) + .not_found_resource(not_found_custom) + .method_not_allowed_resource(not_allowed_custom); + + hello_world_resource hwr; + hwr.disallow_all(); + hwr.set_allowing("GET", true); + ws.register_resource("/hello", &hwr); + ws.start(true); + + return 0; +} diff --git a/examples/minimal_hello_world.cpp b/examples/minimal_hello_world.cpp new file mode 100644 index 00000000..76489a0e --- /dev/null +++ b/examples/minimal_hello_world.cpp @@ -0,0 +1,40 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#include + +using namespace httpserver; + +class hello_world_resource : public http_resource { +public: + const std::shared_ptr render(const http_request&) { + return std::shared_ptr(new string_response("Hello, World!")); + } +}; + +int main(int argc, char** argv) { + webserver ws = create_webserver(8080); + + hello_world_resource hwr; + ws.register_resource("/hello", &hwr); + ws.start(true); + + return 0; +} From 3341abb61ea5683376efed62f81cd453fe9ca3a1 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 13 Jan 2019 06:32:03 +0000 Subject: [PATCH 387/623] Disable custom fd test on darwin --- configure.ac | 2 ++ test/integ/ws_start_stop.cpp | 2 ++ 2 files changed, 4 insertions(+) diff --git a/configure.ac b/configure.ac index b3ae5e70..203e25f6 100644 --- a/configure.ac +++ b/configure.ac @@ -270,6 +270,8 @@ if test x"$coverit" = x"yes"; then case $host_os in darwin* ) echo "Coverage not supported on OSX" + AM_CXXFLAGS="$AM_CXXFLAGS -DDARWIN" + AM_CFLAGS="$AM_CFLAGS -DDARWIN" cond_gcov="no" ;; *) diff --git a/test/integ/ws_start_stop.cpp b/test/integ/ws_start_stop.cpp index 2dbf7d16..cae2d4dc 100644 --- a/test/integ/ws_start_stop.cpp +++ b/test/integ/ws_start_stop.cpp @@ -241,6 +241,7 @@ LT_BEGIN_AUTO_TEST(ws_start_stop_suite, enable_options) LT_END_AUTO_TEST(enable_options) LT_BEGIN_AUTO_TEST(ws_start_stop_suite, custom_socket) +#ifndef DARWIN int fd = socket(AF_INET, SOCK_STREAM, 0); struct sockaddr_in address; @@ -269,6 +270,7 @@ LT_BEGIN_AUTO_TEST(ws_start_stop_suite, custom_socket) curl_easy_cleanup(curl); ws.stop(); +#endif LT_END_AUTO_TEST(custom_socket) LT_BEGIN_AUTO_TEST(ws_start_stop_suite, single_resource) From e671ba38c929bfb155d7057603b915f2b43b8da2 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 13 Jan 2019 06:51:25 +0000 Subject: [PATCH 388/623] Always check darwin --- configure.ac | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index 203e25f6..dc42c5f8 100644 --- a/configure.ac +++ b/configure.ac @@ -258,6 +258,13 @@ else AM_CFLAGS="$AM_CXXFLAGS -O3" fi +case $host_os in + darwin* ) + AM_CXXFLAGS="$AM_CXXFLAGS -DDARWIN" + AM_CFLAGS="$AM_CFLAGS -DDARWIN" + ;; +esac + AC_MSG_CHECKING([whether to build with coverage information]) AC_ARG_ENABLE([coverage], [AS_HELP_STRING([--enable-coverage], @@ -270,8 +277,6 @@ if test x"$coverit" = x"yes"; then case $host_os in darwin* ) echo "Coverage not supported on OSX" - AM_CXXFLAGS="$AM_CXXFLAGS -DDARWIN" - AM_CFLAGS="$AM_CFLAGS -DDARWIN" cond_gcov="no" ;; *) From d33a8bb7794356f288fce08c5bdd4b0144a3ad32 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 13 Jan 2019 08:13:54 +0000 Subject: [PATCH 389/623] Created additional examples --- examples/Makefile.am | 8 +++- examples/allowing_disallowing_methods.cpp | 42 +++++++++++++++++++++ examples/basic_authentication.cpp | 46 +++++++++++++++++++++++ examples/custom_access_log.cpp | 46 +++++++++++++++++++++++ examples/handlers.cpp | 45 ++++++++++++++++++++++ examples/hello_with_get_arg.cpp | 40 ++++++++++++++++++++ examples/setting_headers.cpp | 42 +++++++++++++++++++++ 7 files changed, 268 insertions(+), 1 deletion(-) create mode 100644 examples/allowing_disallowing_methods.cpp create mode 100644 examples/basic_authentication.cpp create mode 100644 examples/custom_access_log.cpp create mode 100644 examples/handlers.cpp create mode 100644 examples/hello_with_get_arg.cpp create mode 100644 examples/setting_headers.cpp diff --git a/examples/Makefile.am b/examples/Makefile.am index a820556d..bf2da41e 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -19,9 +19,15 @@ LDADD = $(top_builddir)/src/libhttpserver.la AM_CPPFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/src/httpserver/ METASOURCES = AUTO -noinst_PROGRAMS = hello_world service minimal_hello_world custom_error +noinst_PROGRAMS = hello_world service minimal_hello_world custom_error allowing_disallowing_methods handlers hello_with_get_arg setting_headers custom_access_log basic_authentication hello_world_SOURCES = hello_world.cpp service_SOURCES = service.cpp minimal_hello_world_SOURCES = minimal_hello_world.cpp custom_error_SOURCES = custom_error.cpp +allowing_disallowing_methods_SOURCES = allowing_disallowing_methods.cpp +handlers_SOURCES = handlers.cpp +hello_with_get_arg_SOURCES = hello_with_get_arg.cpp +setting_headers_SOURCES = setting_headers.cpp +custom_access_log_SOURCES = custom_access_log.cpp +basic_authentication_SOURCES = basic_authentication.cpp diff --git a/examples/allowing_disallowing_methods.cpp b/examples/allowing_disallowing_methods.cpp new file mode 100644 index 00000000..69295af4 --- /dev/null +++ b/examples/allowing_disallowing_methods.cpp @@ -0,0 +1,42 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#include + +using namespace httpserver; + +class hello_world_resource : public http_resource { +public: + const std::shared_ptr render(const http_request&) { + return std::shared_ptr(new string_response("Hello, World!")); + } +}; + +int main(int argc, char** argv) { + webserver ws = create_webserver(8080); + + hello_world_resource hwr; + hwr.disallow_all(); + hwr.set_allowing("GET", true); + ws.register_resource("/hello", &hwr); + ws.start(true); + + return 0; +} diff --git a/examples/basic_authentication.cpp b/examples/basic_authentication.cpp new file mode 100644 index 00000000..84de823e --- /dev/null +++ b/examples/basic_authentication.cpp @@ -0,0 +1,46 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#include + +using namespace httpserver; + +class user_pass_resource : public httpserver::http_resource +{ + public: + const std::shared_ptr render_GET(const http_request& req) + { + if (req.get_user() != "myuser" || req.get_pass() != "mypass") + { + return std::shared_ptr(new basic_auth_fail_response("FAIL", "test@example.com")); + } + return std::shared_ptr(new string_response(req.get_user() + " " + req.get_pass(), 200, "text/plain")); + } +}; + +int main(int argc, char** argv) { + webserver ws = create_webserver(8080); + + user_pass_resource hwr; + ws.register_resource("/hello", &hwr); + ws.start(true); + + return 0; +} diff --git a/examples/custom_access_log.cpp b/examples/custom_access_log.cpp new file mode 100644 index 00000000..7e5f1ef5 --- /dev/null +++ b/examples/custom_access_log.cpp @@ -0,0 +1,46 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#include +#include + +using namespace httpserver; + +void custom_access_log(const std::string& url) { + std::cout << "ACCESSING: " << url << std::endl; +} + +class hello_world_resource : public http_resource { +public: + const std::shared_ptr render(const http_request&) { + return std::shared_ptr(new string_response("Hello, World!")); + } +}; + +int main(int argc, char** argv) { + webserver ws = create_webserver(8080) + .log_access(custom_access_log); + + hello_world_resource hwr; + ws.register_resource("/hello", &hwr); + ws.start(true); + + return 0; +} diff --git a/examples/handlers.cpp b/examples/handlers.cpp new file mode 100644 index 00000000..4ddad426 --- /dev/null +++ b/examples/handlers.cpp @@ -0,0 +1,45 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#include + +using namespace httpserver; + +class hello_world_resource : public http_resource { +public: + const std::shared_ptr render_GET(const http_request&) { + return std::shared_ptr(new string_response("GET: Hello, World!")); + } + + const std::shared_ptr render(const http_request&) { + return std::shared_ptr(new string_response("OTHER: Hello, World!")); + } +}; + +int main(int argc, char** argv) { + webserver ws = create_webserver(8080); + + hello_world_resource hwr; + ws.register_resource("/hello", &hwr); + ws.start(true); + + return 0; +} + diff --git a/examples/hello_with_get_arg.cpp b/examples/hello_with_get_arg.cpp new file mode 100644 index 00000000..07ae90c1 --- /dev/null +++ b/examples/hello_with_get_arg.cpp @@ -0,0 +1,40 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#include + +using namespace httpserver; + +class hello_world_resource : public http_resource { +public: + const std::shared_ptr render(const http_request& req) { + return std::shared_ptr(new string_response("Hello: " + req.get_arg("name"))); + } +}; + +int main(int argc, char** argv) { + webserver ws = create_webserver(8080); + + hello_world_resource hwr; + ws.register_resource("/hello", &hwr); + ws.start(true); + + return 0; +} diff --git a/examples/setting_headers.cpp b/examples/setting_headers.cpp new file mode 100644 index 00000000..0fb73c79 --- /dev/null +++ b/examples/setting_headers.cpp @@ -0,0 +1,42 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#include + +using namespace httpserver; + +class hello_world_resource : public http_resource { +public: + const std::shared_ptr render(const http_request&) { + std::shared_ptr response = std::shared_ptr(new string_response("Hello, World!")); + response->with_header("MyHeader", "MyValue"); + return response; + } +}; + +int main(int argc, char** argv) { + webserver ws = create_webserver(8080); + + hello_world_resource hwr; + ws.register_resource("/hello", &hwr); + ws.start(true); + + return 0; +} From cd75f74dacacc87989ebdbb8c0f46e904d2b5df1 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 13 Jan 2019 21:53:56 +0000 Subject: [PATCH 390/623] Additional examples Digest authentication. Deferred response. File response. HTTPS. --- configure.ac | 3 ++ examples/Makefile.am | 6 +++- examples/digest_authentication.cpp | 51 +++++++++++++++++++++++++++ examples/minimal_deferred.cpp | 55 ++++++++++++++++++++++++++++++ examples/minimal_file_response.cpp | 42 +++++++++++++++++++++++ examples/minimal_https.cpp | 43 +++++++++++++++++++++++ 6 files changed, 199 insertions(+), 1 deletion(-) create mode 100644 examples/digest_authentication.cpp create mode 100644 examples/minimal_deferred.cpp create mode 100644 examples/minimal_file_response.cpp create mode 100644 examples/minimal_https.cpp diff --git a/configure.ac b/configure.ac index b3ae5e70..624520ce 100644 --- a/configure.ac +++ b/configure.ac @@ -325,6 +325,9 @@ AC_CONFIG_LINKS([test/test_content:test/test_content]) AC_CONFIG_LINKS([test/cert.pem:test/cert.pem]) AC_CONFIG_LINKS([test/key.pem:test/key.pem]) AC_CONFIG_LINKS([test/test_root_ca.pem:test/test_root_ca.pem]) +AC_CONFIG_LINKS([examples/cert.pem:examples/cert.pem]) +AC_CONFIG_LINKS([examples/key.pem:examples/key.pem]) +AC_CONFIG_LINKS([examples/test_content:examples/test_content]) AC_OUTPUT( libhttpserver.pc diff --git a/examples/Makefile.am b/examples/Makefile.am index bf2da41e..548b3168 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -19,7 +19,7 @@ LDADD = $(top_builddir)/src/libhttpserver.la AM_CPPFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/src/httpserver/ METASOURCES = AUTO -noinst_PROGRAMS = hello_world service minimal_hello_world custom_error allowing_disallowing_methods handlers hello_with_get_arg setting_headers custom_access_log basic_authentication +noinst_PROGRAMS = hello_world service minimal_hello_world custom_error allowing_disallowing_methods handlers hello_with_get_arg setting_headers custom_access_log basic_authentication digest_authentication minimal_https minimal_file_response minimal_deferred hello_world_SOURCES = hello_world.cpp service_SOURCES = service.cpp @@ -31,3 +31,7 @@ hello_with_get_arg_SOURCES = hello_with_get_arg.cpp setting_headers_SOURCES = setting_headers.cpp custom_access_log_SOURCES = custom_access_log.cpp basic_authentication_SOURCES = basic_authentication.cpp +digest_authentication_SOURCES = digest_authentication.cpp +minimal_https_SOURCES = minimal_https.cpp +minimal_file_response_SOURCES = minimal_file_response.cpp +minimal_deferred_SOURCES = minimal_deferred.cpp diff --git a/examples/digest_authentication.cpp b/examples/digest_authentication.cpp new file mode 100644 index 00000000..55f119a0 --- /dev/null +++ b/examples/digest_authentication.cpp @@ -0,0 +1,51 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#include + +#define MY_OPAQUE "11733b200778ce33060f31c9af70a870ba96ddd4" + +using namespace httpserver; + +class digest_resource : public httpserver::http_resource { +public: + const std::shared_ptr render_GET(const http_request& req) { + if (req.get_digested_user() == "") { + return std::shared_ptr(new digest_auth_fail_response("FAIL", "test@example.com", MY_OPAQUE, true)); + } + else { + bool reload_nonce = false; + if(!req.check_digest_auth("test@example.com", "mypass", 300, reload_nonce)) { + return std::shared_ptr(new digest_auth_fail_response("FAIL", "test@example.com", MY_OPAQUE, reload_nonce)); + } + } + return std::shared_ptr(new string_response("SUCCESS", 200, "text/plain")); + } +}; + +int main(int argc, char** argv) { + webserver ws = create_webserver(8080); + + digest_resource hwr; + ws.register_resource("/hello", &hwr); + ws.start(true); + + return 0; +} diff --git a/examples/minimal_deferred.cpp b/examples/minimal_deferred.cpp new file mode 100644 index 00000000..a08599c2 --- /dev/null +++ b/examples/minimal_deferred.cpp @@ -0,0 +1,55 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#include + +using namespace httpserver; + +static int counter = 0; + +ssize_t test_callback (char* buf, size_t max) { + if (counter == 2) { + return -1; + } + else { + memset(buf, 0, max); + strcat(buf, " test "); + counter++; + return std::string(buf).size(); + } +} + +class deferred_resource : public http_resource { + public: + const std::shared_ptr render_GET(const http_request& req) { + return std::shared_ptr(new deferred_response(test_callback, "cycle callback response")); + } +}; + +int main(int argc, char** argv) { + webserver ws = create_webserver(8080); + + deferred_resource hwr; + ws.register_resource("/hello", &hwr); + ws.start(true); + + return 0; +} + diff --git a/examples/minimal_file_response.cpp b/examples/minimal_file_response.cpp new file mode 100644 index 00000000..b82d2929 --- /dev/null +++ b/examples/minimal_file_response.cpp @@ -0,0 +1,42 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#include + +using namespace httpserver; + +class file_response_resource : public http_resource +{ + public: + const std::shared_ptr render_GET(const http_request& req) + { + return std::shared_ptr(new file_response("test_content", 200, "text/plain")); + } +}; + +int main(int argc, char** argv) { + webserver ws = create_webserver(8080); + + file_response_resource hwr; + ws.register_resource("/hello", &hwr); + ws.start(true); + + return 0; +} diff --git a/examples/minimal_https.cpp b/examples/minimal_https.cpp new file mode 100644 index 00000000..def0452a --- /dev/null +++ b/examples/minimal_https.cpp @@ -0,0 +1,43 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#include + +using namespace httpserver; + +class hello_world_resource : public http_resource { +public: + const std::shared_ptr render(const http_request&) { + return std::shared_ptr(new string_response("Hello, World!")); + } +}; + +int main(int argc, char** argv) { + webserver ws = create_webserver(8080) + .use_ssl() + .https_mem_key("key.pem") + .https_mem_cert("cert.pem"); + + hello_world_resource hwr; + ws.register_resource("/hello", &hwr); + ws.start(true); + + return 0; +} From 4ad67d0b923a7964951ed99559d368553ec52c2b Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 13 Jan 2019 05:39:05 +0000 Subject: [PATCH 391/623] Move make check into an independent execution on travis --- .travis.yml | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/.travis.yml b/.travis.yml index 68b86ec5..4c2da0bf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -84,14 +84,13 @@ script: qemu-arm -L /usr/arm-linux-gnueabi/ ./.libs/http_utils ; qemu-arm -L /usr/arm-linux-gnueabi/ ./.libs/threaded ; fi - else - make check ; - if [ "$VALGRIND" = "valgrind" ]; then make check-valgrind; fi; - cat test/test-suite.log ; - ls -l /usr/local/lib/ ; - ls -l /usr/lib/ ; - if [ "$TRAVIS_OS_NAME" = "linux" ]; then cd ../src/; cppcheck --error-exitcode=1 .; cd ../build; fi - fi + - make check + - cat test/test-suite.log + - if [ "$VALGRIND" = "valgrind" ]; then make check-valgrind; fi; + - if [ "$VALGRIND" = "valgrind" ]; then cat test/test-suite-memcheck.log; fi; + - ls -l /usr/local/lib/ + - ls -l /usr/lib/ + - if [ "$TRAVIS_OS_NAME" = "linux" ]; then cd ../src/; cppcheck --error-exitcode=1 .; cd ../build; fi after_success: - if [ "$DEBUG" = "debug" ] && [ "$COVERAGE" = "coverage" ] && [ "$TRAVIS_OS_NAME" = "linux" ]; then bash <(curl -s https://codecov.io/bash); fi matrix: From c754663f8516e77698f93b9b5d462b692aa4bdac Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 13 Jan 2019 05:53:07 +0000 Subject: [PATCH 392/623] Fix script --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 4c2da0bf..53aa4f06 100644 --- a/.travis.yml +++ b/.travis.yml @@ -84,6 +84,7 @@ script: qemu-arm -L /usr/arm-linux-gnueabi/ ./.libs/http_utils ; qemu-arm -L /usr/arm-linux-gnueabi/ ./.libs/threaded ; fi + fi - make check - cat test/test-suite.log - if [ "$VALGRIND" = "valgrind" ]; then make check-valgrind; fi; From e79b73b66812f39677143290807be07005f3c77c Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 13 Jan 2019 06:20:32 +0000 Subject: [PATCH 393/623] Added more examples --- examples/Makefile.am | 5 +-- examples/custom_error.cpp | 54 ++++++++++++++++++++++++++++++++ examples/minimal_hello_world.cpp | 40 +++++++++++++++++++++++ 3 files changed, 97 insertions(+), 2 deletions(-) create mode 100644 examples/custom_error.cpp create mode 100644 examples/minimal_hello_world.cpp diff --git a/examples/Makefile.am b/examples/Makefile.am index e0c44ac6..a820556d 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -19,8 +19,9 @@ LDADD = $(top_builddir)/src/libhttpserver.la AM_CPPFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/src/httpserver/ METASOURCES = AUTO -noinst_PROGRAMS = hello_world service +noinst_PROGRAMS = hello_world service minimal_hello_world custom_error hello_world_SOURCES = hello_world.cpp service_SOURCES = service.cpp - +minimal_hello_world_SOURCES = minimal_hello_world.cpp +custom_error_SOURCES = custom_error.cpp diff --git a/examples/custom_error.cpp b/examples/custom_error.cpp new file mode 100644 index 00000000..034f334c --- /dev/null +++ b/examples/custom_error.cpp @@ -0,0 +1,54 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#include + +using namespace httpserver; + +const std::shared_ptr not_found_custom(const http_request& req) +{ + return std::shared_ptr(new string_response("Not found custom", 404, "text/plain")); +} + +const std::shared_ptr not_allowed_custom(const http_request& req) +{ + return std::shared_ptr(new string_response("Not allowed custom", 405, "text/plain")); +} + +class hello_world_resource : public http_resource { +public: + const std::shared_ptr render(const http_request&) { + return std::shared_ptr(new string_response("Hello, World!")); + } +}; + +int main(int argc, char** argv) { + webserver ws = create_webserver(8080) + .not_found_resource(not_found_custom) + .method_not_allowed_resource(not_allowed_custom); + + hello_world_resource hwr; + hwr.disallow_all(); + hwr.set_allowing("GET", true); + ws.register_resource("/hello", &hwr); + ws.start(true); + + return 0; +} diff --git a/examples/minimal_hello_world.cpp b/examples/minimal_hello_world.cpp new file mode 100644 index 00000000..76489a0e --- /dev/null +++ b/examples/minimal_hello_world.cpp @@ -0,0 +1,40 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#include + +using namespace httpserver; + +class hello_world_resource : public http_resource { +public: + const std::shared_ptr render(const http_request&) { + return std::shared_ptr(new string_response("Hello, World!")); + } +}; + +int main(int argc, char** argv) { + webserver ws = create_webserver(8080); + + hello_world_resource hwr; + ws.register_resource("/hello", &hwr); + ws.start(true); + + return 0; +} From 6fb3c12fe7d83f7c6885f149a7469bae164b0e54 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 13 Jan 2019 06:32:03 +0000 Subject: [PATCH 394/623] Disable custom fd test on darwin --- configure.ac | 2 ++ test/integ/ws_start_stop.cpp | 2 ++ 2 files changed, 4 insertions(+) diff --git a/configure.ac b/configure.ac index b3ae5e70..203e25f6 100644 --- a/configure.ac +++ b/configure.ac @@ -270,6 +270,8 @@ if test x"$coverit" = x"yes"; then case $host_os in darwin* ) echo "Coverage not supported on OSX" + AM_CXXFLAGS="$AM_CXXFLAGS -DDARWIN" + AM_CFLAGS="$AM_CFLAGS -DDARWIN" cond_gcov="no" ;; *) diff --git a/test/integ/ws_start_stop.cpp b/test/integ/ws_start_stop.cpp index 2dbf7d16..cae2d4dc 100644 --- a/test/integ/ws_start_stop.cpp +++ b/test/integ/ws_start_stop.cpp @@ -241,6 +241,7 @@ LT_BEGIN_AUTO_TEST(ws_start_stop_suite, enable_options) LT_END_AUTO_TEST(enable_options) LT_BEGIN_AUTO_TEST(ws_start_stop_suite, custom_socket) +#ifndef DARWIN int fd = socket(AF_INET, SOCK_STREAM, 0); struct sockaddr_in address; @@ -269,6 +270,7 @@ LT_BEGIN_AUTO_TEST(ws_start_stop_suite, custom_socket) curl_easy_cleanup(curl); ws.stop(); +#endif LT_END_AUTO_TEST(custom_socket) LT_BEGIN_AUTO_TEST(ws_start_stop_suite, single_resource) From 0d533e527b31f04a3b12f7df8b16dcbe9fe05b85 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 13 Jan 2019 06:51:25 +0000 Subject: [PATCH 395/623] Always check darwin --- configure.ac | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index 203e25f6..dc42c5f8 100644 --- a/configure.ac +++ b/configure.ac @@ -258,6 +258,13 @@ else AM_CFLAGS="$AM_CXXFLAGS -O3" fi +case $host_os in + darwin* ) + AM_CXXFLAGS="$AM_CXXFLAGS -DDARWIN" + AM_CFLAGS="$AM_CFLAGS -DDARWIN" + ;; +esac + AC_MSG_CHECKING([whether to build with coverage information]) AC_ARG_ENABLE([coverage], [AS_HELP_STRING([--enable-coverage], @@ -270,8 +277,6 @@ if test x"$coverit" = x"yes"; then case $host_os in darwin* ) echo "Coverage not supported on OSX" - AM_CXXFLAGS="$AM_CXXFLAGS -DDARWIN" - AM_CFLAGS="$AM_CFLAGS -DDARWIN" cond_gcov="no" ;; *) From 10eb4e102b5663454eaf42907eca43b9b53fcb2a Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 13 Jan 2019 05:39:05 +0000 Subject: [PATCH 396/623] Move make check into an independent execution on travis --- .travis.yml | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/.travis.yml b/.travis.yml index 6156e9ce..c059cadf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -82,13 +82,11 @@ script: qemu-arm -L /usr/arm-linux-gnueabi/ ./.libs/http_utils ; qemu-arm -L /usr/arm-linux-gnueabi/ ./.libs/threaded ; fi - else - make check ; - cat test/test-suite.log ; - ls -l /usr/local/lib/ ; - ls -l /usr/lib/ ; - if [ "$TRAVIS_OS_NAME" = "linux" ]; then cd ../src/; cppcheck --error-exitcode=1 .; cd ../build; fi - fi + - make check + - cat test/test-suite.log + - ls -l /usr/local/lib/ + - ls -l /usr/lib/ + - if [ "$TRAVIS_OS_NAME" = "linux" ]; then cd ../src/; cppcheck --error-exitcode=1 .; cd ../build; fi after_success: - if [ "$DEBUG" = "debug" ] && [ "$COVERAGE" = "coverage" ] && [ "$TRAVIS_OS_NAME" = "linux" ]; then bash <(curl -s https://codecov.io/bash); fi matrix: From 22922c5a8ddfe5122aa63ed1f0aeae38e621df12 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 13 Jan 2019 05:53:07 +0000 Subject: [PATCH 397/623] Fix script --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index c059cadf..ef28c78f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -82,6 +82,7 @@ script: qemu-arm -L /usr/arm-linux-gnueabi/ ./.libs/http_utils ; qemu-arm -L /usr/arm-linux-gnueabi/ ./.libs/threaded ; fi + fi - make check - cat test/test-suite.log - ls -l /usr/local/lib/ From d959a6c7f33900ea2b37a38484508db336db488d Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 13 Jan 2019 06:32:03 +0000 Subject: [PATCH 398/623] Disable custom fd test on darwin --- configure.ac | 2 ++ test/integ/ws_start_stop.cpp | 2 ++ 2 files changed, 4 insertions(+) diff --git a/configure.ac b/configure.ac index 624520ce..464aeb11 100644 --- a/configure.ac +++ b/configure.ac @@ -270,6 +270,8 @@ if test x"$coverit" = x"yes"; then case $host_os in darwin* ) echo "Coverage not supported on OSX" + AM_CXXFLAGS="$AM_CXXFLAGS -DDARWIN" + AM_CFLAGS="$AM_CFLAGS -DDARWIN" cond_gcov="no" ;; *) diff --git a/test/integ/ws_start_stop.cpp b/test/integ/ws_start_stop.cpp index 2dbf7d16..cae2d4dc 100644 --- a/test/integ/ws_start_stop.cpp +++ b/test/integ/ws_start_stop.cpp @@ -241,6 +241,7 @@ LT_BEGIN_AUTO_TEST(ws_start_stop_suite, enable_options) LT_END_AUTO_TEST(enable_options) LT_BEGIN_AUTO_TEST(ws_start_stop_suite, custom_socket) +#ifndef DARWIN int fd = socket(AF_INET, SOCK_STREAM, 0); struct sockaddr_in address; @@ -269,6 +270,7 @@ LT_BEGIN_AUTO_TEST(ws_start_stop_suite, custom_socket) curl_easy_cleanup(curl); ws.stop(); +#endif LT_END_AUTO_TEST(custom_socket) LT_BEGIN_AUTO_TEST(ws_start_stop_suite, single_resource) From 1597f8494be6ddc7c3ded9a4b53fac412422e4da Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 13 Jan 2019 06:51:25 +0000 Subject: [PATCH 399/623] Always check darwin --- configure.ac | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index 464aeb11..2d859e7f 100644 --- a/configure.ac +++ b/configure.ac @@ -258,6 +258,13 @@ else AM_CFLAGS="$AM_CXXFLAGS -O3" fi +case $host_os in + darwin* ) + AM_CXXFLAGS="$AM_CXXFLAGS -DDARWIN" + AM_CFLAGS="$AM_CFLAGS -DDARWIN" + ;; +esac + AC_MSG_CHECKING([whether to build with coverage information]) AC_ARG_ENABLE([coverage], [AS_HELP_STRING([--enable-coverage], @@ -270,8 +277,6 @@ if test x"$coverit" = x"yes"; then case $host_os in darwin* ) echo "Coverage not supported on OSX" - AM_CXXFLAGS="$AM_CXXFLAGS -DDARWIN" - AM_CFLAGS="$AM_CFLAGS -DDARWIN" cond_gcov="no" ;; *) From 54072bf7a3c7bf7529a8191057bb2f4c2613f2d8 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 13 Jan 2019 22:34:43 +0000 Subject: [PATCH 400/623] Forcing curl to cleanup after itself --- test/integ/ws_start_stop.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/test/integ/ws_start_stop.cpp b/test/integ/ws_start_stop.cpp index cae2d4dc..ad8652dd 100644 --- a/test/integ/ws_start_stop.cpp +++ b/test/integ/ws_start_stop.cpp @@ -377,6 +377,7 @@ LT_BEGIN_AUTO_TEST(ws_start_stop_suite, ssl_base) LT_ASSERT_EQ(res, 0); LT_CHECK_EQ(s, "OK"); curl_easy_cleanup(curl); + curl_global_cleanup(); ws.stop(); LT_END_AUTO_TEST(ssl_base) From 4a183cdf4621089bc6639f5271923aca6aa7f99d Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 13 Jan 2019 23:33:57 +0000 Subject: [PATCH 401/623] Adding missing test file --- examples/test_content | 1 + 1 file changed, 1 insertion(+) create mode 100755 examples/test_content diff --git a/examples/test_content b/examples/test_content new file mode 100755 index 00000000..5f643138 --- /dev/null +++ b/examples/test_content @@ -0,0 +1 @@ +test content of file From 7a6aebf63f2f79c1a8d6470ac925488259ec22fc Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Mon, 14 Jan 2019 01:18:34 +0000 Subject: [PATCH 402/623] Additional examples. url_registration and ban_system --- examples/Makefile.am | 4 ++- examples/minimal_ip_ban.cpp | 43 ++++++++++++++++++++++++ examples/url_registration.cpp | 63 +++++++++++++++++++++++++++++++++++ 3 files changed, 109 insertions(+), 1 deletion(-) create mode 100644 examples/minimal_ip_ban.cpp create mode 100644 examples/url_registration.cpp diff --git a/examples/Makefile.am b/examples/Makefile.am index 548b3168..d957e3c8 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -19,7 +19,7 @@ LDADD = $(top_builddir)/src/libhttpserver.la AM_CPPFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/src/httpserver/ METASOURCES = AUTO -noinst_PROGRAMS = hello_world service minimal_hello_world custom_error allowing_disallowing_methods handlers hello_with_get_arg setting_headers custom_access_log basic_authentication digest_authentication minimal_https minimal_file_response minimal_deferred +noinst_PROGRAMS = hello_world service minimal_hello_world custom_error allowing_disallowing_methods handlers hello_with_get_arg setting_headers custom_access_log basic_authentication digest_authentication minimal_https minimal_file_response minimal_deferred url_registration minimal_ip_ban hello_world_SOURCES = hello_world.cpp service_SOURCES = service.cpp @@ -35,3 +35,5 @@ digest_authentication_SOURCES = digest_authentication.cpp minimal_https_SOURCES = minimal_https.cpp minimal_file_response_SOURCES = minimal_file_response.cpp minimal_deferred_SOURCES = minimal_deferred.cpp +url_registration_SOURCES = url_registration.cpp +minimal_ip_ban_SOURCES = minimal_ip_ban.cpp diff --git a/examples/minimal_ip_ban.cpp b/examples/minimal_ip_ban.cpp new file mode 100644 index 00000000..ea0923e7 --- /dev/null +++ b/examples/minimal_ip_ban.cpp @@ -0,0 +1,43 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#include + +using namespace httpserver; + +class hello_world_resource : public http_resource { +public: + const std::shared_ptr render(const http_request&) { + return std::shared_ptr(new string_response("Hello, World!")); + } +}; + +int main(int argc, char** argv) { + webserver ws = create_webserver(8080) + .default_policy(http::http_utils::REJECT); + + ws.allow_ip("127.0.0.1"); + + hello_world_resource hwr; + ws.register_resource("/hello", &hwr); + ws.start(true); + + return 0; +} diff --git a/examples/url_registration.cpp b/examples/url_registration.cpp new file mode 100644 index 00000000..0f8da6ad --- /dev/null +++ b/examples/url_registration.cpp @@ -0,0 +1,63 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#include + +using namespace httpserver; + +class hello_world_resource : public http_resource { +public: + const std::shared_ptr render(const http_request&) { + return std::shared_ptr(new string_response("Hello, World!")); + } +}; + +class handling_multiple_resource : public http_resource { +public: + const std::shared_ptr render(const http_request& req) { + return std::shared_ptr(new string_response("Your URL: " + req.get_path())); + } +}; + +class url_args_resource : public http_resource { +public: + const std::shared_ptr render(const http_request& req) { + return std::shared_ptr(new string_response("ARGS: " + req.get_arg("arg1") + " and " + req.get_arg("arg2"))); + } +}; + +int main(int argc, char** argv) { + webserver ws = create_webserver(8080); + + hello_world_resource hwr; + ws.register_resource("/hello", &hwr); + + handling_multiple_resource hmr; + ws.register_resource("/family", &hmr, true); + ws.register_resource("/with_regex_[0-9]+", &hmr); + + url_args_resource uar; + ws.register_resource("/url/with/{arg1}/and/{arg2}", &uar); + ws.register_resource("/url/with/parametric/args/{arg1|[0-9]+}/and/{arg2|[A-Z]+}", &uar); + + ws.start(true); + + return 0; +} From 9034289daecf21d742bbcce10c5faf5ec14de860 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 13 Jan 2019 19:12:32 -0800 Subject: [PATCH 403/623] Update README.me to the latest version --- README.md | 1072 +++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 909 insertions(+), 163 deletions(-) diff --git a/README.md b/README.md index 84c0effe..f974588d 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ -The libhttpserver (0.8.0) reference manual -========================================== +# The libhttpserver reference manual [![Build Status](https://travis-ci.org/etr/libhttpserver.png?branch=master)](https://travis-ci.org/etr/libhttpserver) [![codecov](https://codecov.io/gh/etr/libhttpserver/branch/master/graph/badge.svg)](https://codecov.io/gh/etr/libhttpserver) @@ -18,178 +17,868 @@ The libhttpserver (0.8.0) reference manual [![ko-fi](https://www.ko-fi.com/img/donate_sm.png)](https://ko-fi.com/F1F5HY8B) -This library has been originally developed under the zencoders flags and this community has always supported me all along this work so I am happy to put the logo on this readme. - - When you see this tree, know that you've came across ZenCoders.org - - with open('ZenCoders. - `num` in numbers synchronized - datetime d glob. sys.argv[2] . - def myclass `..` @@oscla org. . class { - displ hooks( public static void ma functor: - $myclass->method( impport sys, os.pipe ` @param name` - fcl if(system(cmd) myc. /de ` $card( array("a" srand - format lists: ++: conc ++ "my an WHERE for( == myi - `sys: myvalue(myvalue) sys.t Console.W try{ rais using - connec SELECT * FROM table mycnf acco desc and or selector::clas at - openldap string sys. print "zenc der " { 'a': `ls -l` > appe &firs - import Tkinter paste( $obh &a or it myval bro roll: :: [] require a - case `` super. +y expr say " %rooms 1 --account fb- yy - proc meth Animate => send(D, open) putd EndIf 10 whi myc` cont - and main (--) import loop $$ or end onload UNION WITH tab timer 150 *2 - end. begin True GtkLabel *label doto partition te let auto i<- (i + d ); - .mushup ``/. ^/zenc/ myclass->her flv op <> element >> 71 or - QFileDi : and .. with myc toA channel::bo myc isEmpty a not bodt; - class T public pol str mycalc d pt &&a *i fc add ^ac - ::ZenCoders::core::namespac boost::function st f = std: ;; int assert - cout << endl public genera #include "b ost ::ac myna const cast mys - ac size_t return ran int (*getNextValue)(void) ff double sa_family_t famil - pu a do puts(" ac int main(int argc, char* "%5d struct nam - cs float for typedef enum puts getchar() - if( else #define fp FILE* f char* s - i++ strcat( %s int - 31] total+= do - }do while(1) sle - getc strcpy( a for - prin scanf(%d, & get - int void myfunc(int pa retu - BEQ BNEQZ R1 10 ANDI R1 R2 SYS - XOR SYSCALL 5 SLTIU MFLO 15 SW JAL - BNE BLTZAL R1 1 LUI 001 NOOP MULTU SLLV - MOV R1 ADD R1 R2 JUMP 10 1001 BEQ R1 R2 1 ANDI - 1101 1010001100 111 001 01 1010 101100 1001 100 - 110110 100 0 01 101 01100 100 100 1000100011 - 11101001001 00 11 100 11 10100010 - 000101001001 10 1001 101000101 - 010010010010110101001010 - -For further information: -visit our website www.zencoders.org - -Author: Sebastiano Merlino - -Copying -======= -This manual is for libhttpserver, C++ library for creating an -embedded Rest HTTP server (and more). - -> Permission is granted to copy, distribute and/or modify this document -> under the terms of the GNU Free Documentation License, Version 1.3 -> or any later version published by the Free Software Foundation; -> with no Invariant Sections, no Front-Cover Texts, and no Back-Cover -> Texts. A copy of the license is included in the section entitled GNU -> Free Documentation License. - -Contents -======== -* Introduction. -* Requirements. -* Compilation. -* Constants. -* Structures and classes type definition. -* Callback functions definition. -* Create and work with server. -* Registering resources. -* Building responses to requests. -* Whitelists and Blacklists. -* Simple comet semantics. -* Utilizing Authentication. -* Obtaining and modifying status information. - -Appendices ----------- -* GNU-LGPL: The GNU Lesser General Public License says how you can copy and share almost all of libhttpserver. -* GNU-FDL: The GNU Free Documentation License says how you can copy and share the documentation of libhttpserver. - -Introduction -============ -libhttpserver is meant to constitute an easy system to build HTTP -servers with REST fashion. -libhttpserver is based on libmicrohttpd and, like this, it is a -daemon library. -The mission of this library is to support all possible HTTP features -directly and with a simple semantic allowing then the user to concentrate -only on his application and not on HTTP request handling details. - -The library is supposed to work transparently for the client Implementing -the business logic and using the library itself to realize an interface. -If the user wants it must be able to change every behavior of the library -itself through the registration of callbacks. - -Like the api is based on (libmicrohttpd), libhttpserver is able to decode -certain body format a and automatically format them in object oriented -fashion. This is true for query arguments and for *POST* and *PUT* -requests bodies if *application/x-www-form-urlencoded* or -*multipart/form-data* header are passed. - -The header reproduce all the constants defined by libhttpserver. -These maps various constant used by the HTTP protocol that are exported -as a convenience for users of the library. Is is possible for the user -to define their own extensions of the HTTP standard and use those with -libhttpserver. - -All functions are guaranteed to be completely reentrant and -thread-safe (unless differently specified). -Additionally, clients can specify resource limits on the overall -number of connections, number of connections per IP address and memory -used per connection to avoid resource exhaustion. - -Requirements -============ -* g++ >= 4.1.2 -* libmicrohttpd >= 0.9.37 -* doxygen (if you want to build code reference) +## Tl;dr +libhttpserver is a C++ library for building high performance RESTful web servers. +libhttpserver is buildt upon [libmicrohttpd](https://www.gnu.org/software/libmicrohttpd/) to provide a simple API for developers to create HTTP services in C++. + +**Features:** +- HTTP 1.1 compatible request parser +- RESTful oriented interface +- Flexible handler API +- Cross-platform compatible +- Implementation is HTTP 1.1 compliant +- Multiple threading models +- Support for IPv6 +- Support for SHOUTcast +- Support for incremental processing of POST data (optional) +- Support for basic and digest authentication (optional) +- Support for TLS (requires libgnutls, optional) + +## Table of Contents +* [Introduction](#introduction) +* [Requirements](#requirements) +* [Building](#building) +* [Getting Started](#getting-started) +* [Structures and classes type definition](#structures-and-classes-type-definition) +* [Create and work with a webserver](#create-and-work-with-a-webserver) +* [The resource object](#the-resource-object) +* [Registering resources](#registering-resources) +* [Parsing requests](#parsing-requests) +* [Building responses to requests](#building-responses-to-requests) +* [IP Blacklisting and Whitelisting](#ip-blacklisting-and-whitelisting) +* [Authentication](#authentication) +* [HTTP Utils](#http-utils) +* [Other Examples](#other-examples) + +#### Community +* [Code of Conduct (on a separate page)](https://github.com/etr/libhttpserver/blob/master/CODE_OF_CONDUCT.md) +* [Contributing (on a separate page)](https://github.com/etr/libhttpserver/blob/master/CODE_OF_CONDUCT.md) + +#### Appendices +* [Copying statement](#copying) +* [GNU-LGPL](#GNU-lesser-general-public-license): The GNU Lesser General Public License says how you can copy and share almost all of libhttpserver. +* [GNU-FDL](#GNU-free-documentation-license): The GNU Free Documentation License says how you can copy and share the documentation of libhttpserver. + +## Introduction +libhttpserver is meant to constitute an easy system to build HTTP servers with REST fashion. +libhttpserver is based on [libmicrohttpd](https://www.gnu.org/software/libmicrohttpd/) and, like this, it is a daemon library (parts of this documentation are, in fact, matching those of the wrapped library). +The mission of this library is to support all possible HTTP features directly and with a simple semantic allowing then the user to concentrate only on his application and not on HTTP request handling details. + +The library is supposed to work transparently for the client Implementing the business logic and using the library itself to realize an interface. +If the user wants it must be able to change every behavior of the library itself through the registration of callbacks. + +libhttpserver is able to decode certain body format a and automatically format them in object oriented fashion. This is true for query arguments and for *POST* and *PUT* requests bodies if *application/x-www-form-urlencoded* or *multipart/form-data* header are passed. + +All functions are guaranteed to be completely reentrant and thread-safe (unless differently specified). +Additionally, clients can specify resource limits on the overall number of connections, number of connections per IP address and memory used per connection to avoid resource exhaustion. + +[Back to TOC](#table-of-contents) + +## Requirements +libhttpserver can be used without any dependencies aside for libmicrohttpd. + +The minimum versions required are: +* g++ >= 4.8.4 or clang-3.6 +* libmicrohttpd >= 0.9.52 +* [Optionally]: for TLS (HTTPS) support, you'll need [libgnutls](http://www.gnutls.org/). +* [Optionally]: to compile the code-reference, you'll need [doxygen](http://www.doxygen.nl/). Additionally, for MinGW on windows you will need: * libwinpthread (For MinGW-w64, if you use thread model posix then you have this) * libgnurx >= 2.5.1 -Compilation -=========== -libhttpserver uses the standard system where the usual build process -involves running +Furthermore, the testcases use [libcurl](http://curl.haxx.se/libcurl/) but you don't need it to compile the library. + +[Back to TOC](#table-of-contents) + +## Building +libhttpserver uses the standard system where the usual build process involves running > ./bootstrap > mkdir build > cd build -> ../configure +> \.\./configure > make -> make install -> make doxygen-doc # if you want to build the code reference +> make install # (optionally to install on the system) + +[Back to TOC](#table-of-contents) -Optional parameters to configure script ---------------------------------------- +### Optional parameters to configure script A complete list of parameters can be obtained running 'configure --help'. Here are listed the libhttpserver specific options (the canonical configure options are also supported). -* --enable-same-directory-build: enable to compile in the same directory. This is heavily discouraged. (def=no) -* --enable-debug: enable debug data generation (def=no) -* --enable-cpp11: enable c++11 std classes (def=no) -* --disable-doxygen-doc: don't generate any doxygen documentation -* --disable-doxygen-dot: don't generate graphics for doxygen documentation -* --disable-doxygen-man: don't generate doxygen manual pages -* --enable-doxygen-rtf: generate doxygen RTF documentation -* --enable-doxygen-xml: generate doxygen XML documentation -* --enable-doxygen-chm: generate doxygen compressed HTML help documentation -* --enable-doxygen-chi: generate doxygen seperate compressed HTML help index file -* --disable-doxygen-html: don't generate doxygen plain HTML documentation -* --enable-doxygen-ps: generate doxygen PostScript documentation -* --enable-doxygen-pdf: generate doxygen PDF documentation - -Constants -========= -W.I.P. - -Structures and classes type definition -====================================== -* http_resource (CPP class): Represents the resource associated with a specific http endpoint. -* http_request (CPP class): Represents the request received by the resource that process it. -* http_response (CPP class): Represents the response sent by the server once the resource finished its work. -* event_supplier (CPP class): Represents a class that supplies events to the webserver. It can be used to trigger the internal select of it. -* webserver (CPP class): Represents the daemon listening on a socket for HTTP traffic. - -GNU Lesser General Public License -================================= +* _\-\-enable-same-directory-build:_ enable to compile in the same directory. This is heavily discouraged. (def=no) +* _\-\-enable-debug:_ enable debug data generation. (def=no) +* _\-\-disable-doxygen-doc:_ don't generate any doxygen documentation. Doxygen is automatically invoked if present on the system. Automatically disabled otherwise. +* _\-\-enable-fastopen:_ enable use of TCP_FASTOPEN (def=yes) +* _\-\-enable-poll[=ARG]:_ enable poll support. Internal behavior of the `INTERNAL_SELECT` (yes, no, auto) [auto] +* _\-\-enable-epoll[=ARG]:_ enable epoll support. Internal behavior of the `INTERNAL_SELECT` (yes, no, auto) [auto] +* _\-\-enable-static:_ enable use static linking (def=yes) + +[Back to TOC](#table-of-contents) + +## Getting Started +The most basic example of creating a server and handling a requests for the path `/hello`: + + #include + + using namespace httpserver; + + class hello_world_resource : public http_resource { + public: + const std::shared_ptr render(const http_request&) { + return std::shared_ptr(new string_response("Hello, World!")); + } + }; + + int main(int argc, char** argv) { + webserver ws = create_webserver(8080); + + hello_world_resource hwr; + ws.register_resource("/hello", &hwr); + ws.start(true); + + return 0; + } + +To test the above example, you could run the following command from a terminal: + + curl -XGET -v http://localhost:8080/hello + +You can also check this example on [github](https://github.com/etr/libhttpserver/blob/master/examples/minimal_hello_world.cpp). + +[Back to TOC](#table-of-contents) + +## Structures and classes type definition +* _webserver:_ Represents the daemon listening on a socket for HTTP traffic. + * _create_webserver:_ Builder class to support the creation of a webserver. +* _http_resource:_ Represents the resource associated with a specific http endpoint. +* _http_request:_ Represents the request received by the resource that process it. +* _http_response:_ Represents the response sent by the server once the resource finished its work. + * _string_response:_ A simple string response. + * _file_response:_ A response getting content from a fail. + * _basic_auth_fail_response:_ A failure in basic authentication. + * _digest_auth_fail_response:_ A failure in digest authentication. + * _deferred_response:_ A response getting content from a callback. + +[Back to TOC](#table-of-contents) + +## Create and work with a webserver +As you can see from the example above, creating a webserver with standard configuration is quite simple: + + webserver ws = create_webserver(8080); + +The `create_webserver` class is a supporting _builder_ class that eases the building of a webserver through chained syntax. + +### Basic Startup Options + +In this section we will explore other basic options that you can use when configuring your server. More advanced options (custom callbacks, https support, etc...) will be discussed separately. + +* _.port(**int** port):_ The port at which the server will listen. This can also be passed to the consturctor of `create_webserver`. E.g. `create_webserver(8080)`. +* _.max_connections(**int** max_conns):_ Maximum number of concurrent connections to accept. The default is `FD_SETSIZE - 4` (the maximum number of file descriptors supported by `select` minus four for `stdin`, `stdout`, `stderr` and the server socket). In other words, the default is as large as possible. Note that if you set a low connection limit, you can easily get into trouble with browsers doing request pipelining. +For example, if your connection limit is “1”, a browser may open a first connection to access your “index.html” file, keep it open but use a second connection to retrieve CSS files, images and the like. In fact, modern browsers are typically by default configured for up to 15 parallel connections to a single server. If this happens, the library will refuse to even accept the second connection until the first connection is closed — which does not happen until timeout. As a result, the browser will fail to render the page and seem to hang. If you expect your server to operate close to the connection limit, you should first consider using a lower timeout value and also possibly add a “Connection: close” header to your response to ensure that request pipelining is not used and connections are closed immediately after the request has completed. +* _.content_size_limit(**size_t** size_limit):_ Sets the maximum size of the content that a client can send over in a single block. The default is `-1 = unlimited`. +* _.connection_timeout(**int** timeout):_ Determines after how many seconds of inactivity a connection should be timed out automatically. The default timeout is `180 seconds`. +* _.memory_limit(**int** memory_limit):_ Maximum memory size per connection (followed by a `size_t`). The default is 32 kB (32*1024 bytes). Values above 128k are unlikely to result in much benefit, as half of the memory will be typically used for IO, and TCP buffers are unlikely to support window sizes above 64k on most systems. +* _.per_IP_connection_limit(**int** connection_limit):_ Limit on the number of (concurrent) connections made to the server from the same IP address. Can be used to prevent one IP from taking over all of the allowed connections. If the same IP tries to establish more than the specified number of connections, they will be immediately rejected. The default is `0`, which means no limit on the number of connections from the same IP address. +* _.bind_socket(**int** socket_fd):_ Listen socket to use. Pass a listen socket for the daemon to use (systemd-style). If this option is used, the daemon will not open its own listen socket(s). The argument passed must be of type "int" and refer to an existing socket that has been bound to a port and is listening. +* _.max_thread_stack_size(**int** stack_size):_ Maximum stack size for threads created by the library. Not specifying this option or using a value of zero means using the system default (which is likely to differ based on your platform). Default is `0 (system default)`. +* _.use_ipv6() and .no_ipv6():_ Enable or disable the IPv6 protocol support (by default, libhttpserver will just support IPv4). If you specify this and the local platform does not support it, starting up the server will throw an exception. `off` by default. +* _.pedantic() and .no_pedantic():_ Enables pedantic checks about the protocol (as opposed to as tolerant as possible). Specifically, at the moment, this flag causes the library to reject HTTP 1.1 connections without a `Host` header. This is required by the standard, but of course in violation of the “be as liberal as possible in what you accept” norm. It is recommended to turn this **off** if you are testing clients against the library, and **on** in production. `off` by default. +* _.debug() and .no_debug():_ Enables debug messages from the library. `off` by default. +* _.regex_checking() and .no_regex_checking():_ Enables pattern matching for endpoints. Read more [here](#registering-resources). `on` by default. +* _.post_process() and .no_post_process():_ Enables/Disables the library to automatically parse the body of the http request as arguments if in querystring format. Read more [here](#parsing-requests). `on` by default. +* _.deferred()_ and _.no_deferred():_ Enables/Disables the ability for the server to suspend and resume connections. Simply put, it enables/disables the ability to use `deferred_response`. Read more [here](#building-responses-to-requests). `on` by default. +* _.single_resource() and .no_single_resource:_ Sets or unsets the server in single resource mode. This limits all endpoints to be served from a single resource. The resultant is that the webserver will process the request matching to the endpoint skipping any complex semantic. Because of this, the option is incompatible with `regex_checking` and requires the resource to be registered against an empty endpoint or the root endpoint (`"/"`). The resource will also have to be registered as family. (For more information on resource registration, read more [here](#registering-resources)). `off` by default. + +### Threading Models +* _.start_method(**const http::http_utils::start_method_T&** start_method):_ libhttpserver can operate with two different threading models that can be selected through this method. Default value is `INTERNAL_SELECT`. + * `http::http_utils::INTERNAL_SELECT`: In this mode, libhttpserver uses only a single thread to handle listening on the port and processing of requests. This mode is preferable if spawning a thread for each connection would be costly. If the HTTP server is able to quickly produce responses without much computational overhead for each connection, this mode can be a great choice. Note that libhttpserver will still start a single thread for itself -- this way, the main program can continue with its operations after calling the start method. Naturally, if the HTTP server needs to interact with shared state in the main application, synchronization will be required. If such synchronization in code providing a response results in blocking, all HTTP server operations on all connections will stall. This mode is a bad choice if response data cannot always be provided instantly. The reason is that the code generating responses should not block (since that would block all other connections) and on the other hand, if response data is not available immediately, libhttpserver will start to busy wait on it. If you need to scale along the number of concurrent connection and scale on multiple thread you can specify a value for `max_threads` (see below) thus enabling a thread pool - this is different from `THREAD_PER_CONNECTION` below where a new thread is spawned for each connection. + * `http::http_utils::THREAD_PER_CONNECTION`: In this mode, libhttpserver starts one thread to listen on the port for new connections and then spawns a new thread to handle each connection. This mode is great if the HTTP server has hardly any state that is shared between connections (no synchronization issues!) and may need to perform blocking operations (such as extensive IO or running of code) to handle an individual connection. +* _.max_threads(**int** max_threads):_ A thread pool can be combined with the `INTERNAL_SELECT` mode to benefit implementations that require scalability. As said before, by default this mode only uses a single thread. When combined with the thread pool option, it is possible to handle multiple connections with multiple threads. Any value greater than one for this option will activate the use of the thread pool. In contrast to the `THREAD_PER_CONNECTION` mode (where each thread handles one and only one connection), threads in the pool can handle a large number of concurrent connections. Using `INTERNAL_SELECT` in combination with a thread pool is typically the most scalable (but also hardest to debug) mode of operation for libhttpserver. Default value is `1`. This option is incompatible with `THREAD_PER_CONNECTION`. + +### Custom defaulted error messages +libhttpserver allows to override internal error retrieving functions to provide custom messages to the HTTP client. There are only 3 cases in which implementing logic (an http_resource) cannot be invoked: (1) a not found resource, where the library is not being able to match the URL requested by the client to any implementing http_resource object; (2) a not allowed method, when the HTTP client is requesting a method explicitly marked as not allowed (more info [here](#allowing-and-disallowing-methods-on-a-resource)) by the implementation; (3) an exception being thrown. +In all these 3 cases libhttpserver would provide a standard HTTP response to the client with the correct error code; respectively a `404`, a `405` and a `500`. The library allows its user to specify custom callbacks that will be called to replace the default behavior. +* _.not_found_resource(**const shared_ptr(*render_ptr)(const http_request&)** resource):_ Specifies a function to handle a request when no matching registered endpoint exist for the URL requested by the client. +* _.method_not_allowed_resource(**const shared_ptr(*render_ptr)(const http_request&)** resource):_ Specifies a function to handle a request that is asking for a method marked as not allowed on the matching http_resource. +* _.internal_error_resource(**const shared_ptr(*render_ptr)(const http_request&)** resource):_ Specifies a function to handle a request that is causing an uncaught exception during its execution. **REMEMBER:** is this callback is causing an exception itself, the standard default response from libhttpserver will be reported to the HTTP client. + +#### Example of custom errors: + #include + + using namespace httpserver; + + const std::shared_ptr not_found_custom(const http_request& req) { + return std::shared_ptr(new string_response("Not found custom", 404, "text/plain")); + } + + const std::shared_ptr not_allowed_custom(const http_request& req) { + return std::shared_ptr(new string_response("Not allowed custom", 405, "text/plain")); + } + + class hello_world_resource : public http_resource { + public: + const std::shared_ptr render(const http_request&) { + return std::shared_ptr(new string_response("Hello, World!")); + } + }; + + int main(int argc, char** argv) { + webserver ws = create_webserver(8080) + .not_found_resource(not_found_custom) + .method_not_allowed_resource(not_allowed_custom); + + hello_world_resource hwr; + hwr.disallow_all(); + hwr.set_allowing("GET", true); + ws.register_resource("/hello", &hwr); + ws.start(true); + + return 0; + } + +To test the above example, you can run the following command from a terminal: + + curl -XGET -v http://localhost:8080/hello + +If you try to run either of the two following commands, you'll see your custom errors: +* `curl -XGET -v http://localhost:8080/morning`: will return your custom `not found` error. +* `curl -XPOST -v http://localhost:8080/hello`: will return your custom `not allowed` error. + +You can also check this example on [github](https://github.com/etr/libhttpserver/blob/master/examples/custom_error.cpp). + +### Custom logging callbacks +* _.log_access(**void(*log_access_ptr)(const std::string&)** functor):_ Specifies a function used to log accesses (requests) to the server. +* _.log_error(**void(*log_error_ptr)(const std::string&)** functor):_ Specifies a function used to log errors generating from the server. + +#### Example of custom logging callback + #include + #include + + using namespace httpserver; + + void custom_access_log(const std::string& url) { + std::cout << "ACCESSING: " << url << std::endl; + } + + class hello_world_resource : public http_resource { + public: + const std::shared_ptr render(const http_request&) { + return std::shared_ptr(new string_response("Hello, World!")); + } + }; + + int main(int argc, char** argv) { + webserver ws = create_webserver(8080) + .log_access(custom_access_log); + + hello_world_resource hwr; + ws.register_resource("/hello", &hwr); + ws.start(true); + + return 0; + } + +To test the above example, you can run the following command from a terminal: + + curl -XGET -v http://localhost:8080/hello + +You'll notice how, on the terminal runing your server, the logs will now be printed in output for each request received. + +You can also check this example on [github](https://github.com/etr/libhttpserver/blob/master/examples/custom_access_log.cpp). + +### TLS/HTTPS +* _.use_ssl() and .no_ssl():_ Determines whether to run in HTTPS-mode or not. If you set this as on and libhttpserver was compiled without SSL support, the library will throw an exception at start of the server. `off` by default. +* _.cred_type(**const http::http_utils::cred_type_T&** cred_type):_ Daemon credentials type. Either certificate or anonymous. Acceptable values are: + * `NONE`: No credentials. + * `CERTIFICATE`: Certificate credential. + * `ANON`: Anonymous credential. + * `SRP`: SRP credential. + * `PSK`: PSK credential. + * `IA`: IA credential. +* _.https_mem_key(**const std::string&** filename):_ String representing the path to a file containing the private key to be used by the HTTPS daemon. This must be used in conjunction with `https_mem_cert`. +* _.https_mem_cert(**const std::string&** filename):_ String representing the path to a file containing the certificate to be used by the HTTPS daemon. This must be used in conjunction with `https_mem_key`. +* _.https_mem_trust(**const std::string&** filename):_ String representing the path to a file containing the CA certificate to be used by the HTTPS daemon to authenticate and trust clients certificates. The presence of this option activates the request of certificate to the client. The request to the client is marked optional, and it is the responsibility of the server to check the presence of the certificate if needed. Note that most browsers will only present a client certificate only if they have one matching the specified CA, not sending any certificate otherwise. +* _.https_priorities(**const std::string&** priority_string):_ SSL/TLS protocol version and ciphers. Must be followed by a string specifying the SSL/TLS protocol versions and ciphers that are acceptable for the application. The string is passed unchanged to gnutls_priority_init. If this option is not specified, `"NORMAL"` is used. + +#### Minimal example using HTTPS + #include + + using namespace httpserver; + + class hello_world_resource : public http_resource { + public: + const std::shared_ptr render(const http_request&) { + return std::shared_ptr(new string_response("Hello, World!")); + } + }; + + int main(int argc, char** argv) { + webserver ws = create_webserver(8080) + .use_ssl() + .https_mem_key("key.pem") + .https_mem_cert("cert.pem"); + + hello_world_resource hwr; + ws.register_resource("/hello", &hwr); + ws.start(true); + + return 0; + } + +To test the above example, you can run the following command from a terminal: + + curl -XGET -v -k 'https://localhost:8080/hello' + +You can also check this example on [github](https://github.com/etr/libhttpserver/blob/master/examples/minimal_https.cpp). + +### IP Blacklisting/Whitelisting +libhttpserver supports IP blacklisting and whitelisting as an internal feature. This section explains the startup options related with IP blacklisting/whitelisting. See the [specific section](#ip-blacklisting-and-whitelisting) to read more about the topic. +* _.ban_system() and .no_ban_system:_ Can be used to enable/disable the ban system. `on` by default. +* _.default_policy(**const http::http_utils::policy_T&** default_policy):_ Specifies what should be the default behavior when receiving a request. Possible values are `ACCEPT` and `REJECT`. Default is `ACCEPT`. + +### Authentication +* _.basic_auth() and .no_basic_auth:_ Can be used to enable/disable parsing of the basic authorization header sent by the client. `on` by default. +* _.digest_auth() and .no_digest_auth:_ Can be used to enable/disable parsing of the digested authentication data sent by the client. `on` by default. +* _.nonce_nc_size(**int** nonce_size):_ Size of an array of nonce and nonce counter map. This option represents the size (number of elements) of a map of a nonce and a nonce-counter. If this option is not specified, a default value of 4 will be used (which might be too small for servers handling many requests). +You should calculate the value of NC_SIZE based on the number of connections per second multiplied by your expected session duration plus a factor of about two for hash table collisions. For example, if you expect 100 digest-authenticated connections per second and the average user to stay on your site for 5 minutes, then you likely need a value of about 60000. On the other hand, if you can only expect only 10 digest-authenticated connections per second, tolerate browsers getting a fresh nonce for each request and expect a HTTP request latency of 250 ms, then a value of about 5 should be fine. +* _.digest_auth_random(**const std::string&** nonce_seed):_ Digest Authentication nonce’s seed. For security, you SHOULD provide a fresh random nonce when actually using Digest Authentication with libhttpserver in production. + +### Examples of chaining syntax to create a webserver + + webserver ws = create_webserver(8080) + .no_ssl() + .no_ipv6() + .no_debug() + .no_pedantic() + .no_basic_auth() + .no_digest_auth() + .no_comet() + .no_regex_checking() + .no_ban_system() + .no_post_process(); +## + webserver ws = create_webserver(8080) + .use_ssl() + .https_mem_key("key.pem") + .https_mem_cert("cert.pem"); + +### Starting and stopping a webserver +Once a webserver is created, you can manage its execution through the following methods on the `webserver` class: +* _**void** webserver::start(**bool** blocking):_ Allows to start a server. If the `blocking` flag is passed as `true`, it will block the execution of the current thread until a call to stop on the same webserver object is performed. +* _**void** webserver::stop():_ Allows to stop a server. It immediately stops it. +* _**bool** webserver::is_running():_ Checks if a server is running +* _**void** webserver::sweet_kill():_ Allows to stop a server. It doesn't guarantee an immediate halt to allow for thread termination and connection closure. + +[Back to TOC](#table-of-contents) + +## The Resource Object +The `http_resource` class represents a logical collection of HTTP methods that will be associated to a URL when registered on the webserver. The class is **designed for extension** and it is where most of your code should ideally live. When the webserver matches a request against a resource (see: [resource registration](#registering-resources)), the method correspondent to the one in the request (GET, POST, etc..) (see below) is called on the resource. + +Given this, the `http_resource` class contains the following extensible methods (also called `handlers` or `render methods`): +* _**const std::shared_ptr** http_resource::render_GET(**const http_request&** req):_ Invoked on an HTTP GET request. +* _**const std::shared_ptr** http_resource::render_POST(**const http_request&** req):_ Invoked on an HTTP POST request. +* _**const std::shared_ptr** http_resource::render_PUT(**const http_request&** req):_ Invoked on an HTTP PUT request. +* _**const std::shared_ptr** http_resource::render_HEAD(**const http_request&** req):_ Invoked on an HTTP HEAD request. +* _**const std::shared_ptr** http_resource::render_DELETE(**const http_request&** req):_ Invoked on an HTTP DELETE request. +* _**const std::shared_ptr** http_resource::render_TRACE(**const http_request&** req):_ Invoked on an HTTP TRACE request. +* _**const std::shared_ptr** http_resource::render_OPTIONS(**const http_request&** req):_ Invoked on an HTTP OPTIONS request. +* _**const std::shared_ptr** http_resource::render_CONNECT(**const http_request&** req):_ Invoked on an HTTP CONNECT request. +* _**const std::shared_ptr** http_resource::render(**const http_request&** req):_ Invoked as a backup method if the matching method is not implemented. It can be used whenever you want all the invocations on a URL to activate the same behavior regardless of the HTTP method requested. The default implementation of the `render` method returns an empty response with a `404`. + +#### Example of implementation of render methods + #include + + using namespace httpserver; + + class hello_world_resource : public http_resource { + public: + const std::shared_ptr render_GET(const http_request&) { + return std::shared_ptr(new string_response("GET: Hello, World!")); + } + + const std::shared_ptr render(const http_request&) { + return std::shared_ptr(new string_response("OTHER: Hello, World!")); + } + }; + + int main(int argc, char** argv) { + webserver ws = create_webserver(8080); + + hello_world_resource hwr; + ws.register_resource("/hello", &hwr); + ws.start(true); + + return 0; + } + +To test the above example, you can run the following commands from a terminal: + * `curl -XGET -v http://localhost:8080/hello`: will return `GET: Hello, World!`. + * `curl -XPOST -v http://localhost:8080/hello`: will return `OTHER: Hello, World!`. You can try requesting other methods beside `POST` to verify how the same message will be returned. + +You can also check this example on [github](https://github.com/etr/libhttpserver/blob/master/examples/handlers.cpp). + +### Allowing and disallowing methods on a resource +By default, all methods an a resource are allowed, meaning that an HTTP request with that method will be invoked. It is possible to mark methods as `not allowed` on a resource. When a method not allowed is requested on a resource, the default `method_not_allowed` method is invoked - the default can be overriden as explain in the section [Custom defaulted error messages](custom-defaulted-error-messages). +The base `http_resource` class has a set of methods that can be used to allow and disallow HTTP methods. +* _**void** http_resource::set_allowing(**const std::string&** method, **bool** allowed):_ Used to allow or disallow a method. The `method` parameter is a string representing an HTTP method (GET, POST, PUT, etc...). +* _**void** http_resource::allow_all():_ Marks all HTTP methods as allowed. +* _**void** http_resource::disallow_all():_ Marks all HTTP methods as not allowed. + +#### Example of methods allowed/disallowed + #include + + using namespace httpserver; + + class hello_world_resource : public http_resource { + public: + const std::shared_ptr render(const http_request&) { + return std::shared_ptr(new string_response("Hello, World!")); + } + }; + + int main(int argc, char** argv) { + webserver ws = create_webserver(8080); + + hello_world_resource hwr; + hwr.disallow_all(); + hwr.set_allowing("GET", true); + ws.register_resource("/hello", &hwr); + ws.start(true); + + return 0; + } + +To test the above example, you can run the following command from a terminal: + + curl -XGET -v http://localhost:8080/hello + +If you try to run the following command, you'll see a `method_not_allowed` error: +* `curl -XPOST -v http://localhost:8080/hello`. + +You can also check this example on [github](https://github.com/etr/libhttpserver/blob/master/examples/allowing_disallowing_methods.cpp). + +[Back to TOC](#table-of-contents) + +## Registering resources +Once you have created your resource and extended its methods, you'll have to register the resource on the webserver. Registering a resource will associate it with an endpoint and allows the webserver to route it. +The `webserver` class offers a method to register a resource: +* _**bool** register_resource(**const std::string&** endpoint, **http_resource*** resource, **bool** family = `false`):_ Registers the `resource` to an `endpoint`. The endpoint is a string representing the path on your webserver from where you want your resource to be served from (e.g. `"/path/to/resource"`). The optional `family` parameter allows to register a resource as a "family" resource that will match any path nested into the one specified. For example, if family is set to `true` and endpoint is set to `"/path"`, the webserver will route to the resource not only the requests against `"/path"` but also everything in its nested path `"/path/on/the/previous/one"`. + +### Specifying endpoints +There are essentially four ways to specify an endpoint string: +* **A simple path (e.g. `"/path/to/resource"`).** In this case, the webserver will try to match exactly the value of the endpoint. +* **A regular exception.** In this case, the webserver will try to match the URL of the request with the regex passed. For example, if passing `"/path/as/decimal/[0-9]+`, requests on URLs like `"/path/as/decimal/5"` or `"/path/as/decimal/42"` will be matched; instead, URLs like `"/path/as/decimal/three"` will not. +* **A parametrized path. (e.g. `"/path/to/resource/with/{arg1}/{arg2}/in/url"`)**. In this case, the webserver will match the argument with any value passed. In addition to this, the arguments will be passed to the resource as part of the arguments (readable from the `http_request::get_arg` method - see [here](#parsing-requests)). For example, if passing `"/path/to/resource/with/{arg1}/{arg2}/in/url"` will match any request on URL with any value in place of `{arg1}` and `{arg2}`. +* **A parametrized path with custom parameters.** This is the same of a normal parametrized path, but allows to specify a regular expression for the argument (e.g. `"/path/to/resource/with/{arg1|[0-9]+}/{arg2|[a-z]+}/in/url"`. In this case, the webserver will match the arguments with any value passed that satisfies the regex. In addition to this, as above, the arguments will be passed to the resource as part of the arguments (readable from the `http_request::get_arg` method - see [here](#parsing-requests)). For example, if passing `"/path/to/resource/with/{arg1|[0-9]+}/{arg2|[a-z]+}/in/url"` will match requests on URLs like `"/path/to/resource/with/10/AA/in/url"` but not like `""/path/to/resource/with/BB/10/in/url""` +* Any of the above marked as `family`. Will match any request on URLs having path that is prefixed by the path passed. For example, if family is set to `true` and endpoint is set to `"/path"`, the webserver will route to the resource not only the requests against `"/path"` but also everything in its nested path `"/path/on/the/previous/one"`. + + #include + + using namespace httpserver; + + class hello_world_resource : public http_resource { + public: + const std::shared_ptr render(const http_request&) { + return std::shared_ptr(new string_response("Hello, World!")); + } + }; + + class handling_multiple_resource : public http_resource { + public: + const std::shared_ptr render(const http_request& req) { + return std::shared_ptr(new string_response("Your URL: " + req.get_path())); + } + }; + + class url_args_resource : public http_resource { + public: + const std::shared_ptr render(const http_request& req) { + return std::shared_ptr(new string_response("ARGS: " + req.get_arg("arg1") + " and " + req.get_arg("arg2"))); + } + }; + + int main(int argc, char** argv) { + webserver ws = create_webserver(8080); + + hello_world_resource hwr; + ws.register_resource("/hello", &hwr); + + handling_multiple_resource hmr; + ws.register_resource("/family", &hmr, true); + ws.register_resource("/with_regex_[0-9]+", &hmr); + + url_args_resource uar; + ws.register_resource("/url/with/{arg1}/and/{arg2}", &uar); + ws.register_resource("/url/with/parametric/args/{arg1|[0-9]+}/and/{arg2|[A-Z]+}", &uar); + + ws.start(true); + + return 0; + } + +To test the above example, you can run the following commands from a terminal: + +* `curl -XGET -v http://localhost:8080/hello`: will return the `Hello, World!` message. +* `curl -XGET -v http://localhost:8080/family`: will return the `Your URL: /family` message. +* `curl -XGET -v http://localhost:8080/family/with/suffix`: will return the `Your URL: /family/with/suffix` message. +* `curl -XGET -v http://localhost:8080/with_regex_10`: will return the `Your URL: /with_regex_10` message. +* `curl -XGET -v http://localhost:8080/url/with/AA/and/BB`: will return the `ARGS: AA and BB` message. You can change `AA` and `BB` with any value and observe how the URL is still matched and parameters are read. +* `curl -XGET -v http://localhost:8080/url/with/parametric/args/10/and/AA`: will return the `ARGS: 10 and AA` message. You can change `10` and `AA` with any value matching the regexes and observe how the URL is still matched and parameters are read. + +Conversely, you can observe how these URL will not be matched (al the following will give you a `not found` message): +* `curl -XGET -v http://localhost:8080/with_regex_A` +* `curl -XGET -v http://localhost:8080/url/with/parametric/args/AA/and/BB` + +You can also check this example on [github](https://github.com/etr/libhttpserver/blob/master/examples/url_registration.cpp). + +[Back to TOC](#table-of-contents) + +## Parsing requests +As seen in the documentation of [http_resource](#the-resource-object), every extensible method takes in input a `http_request` object. The webserver takes the responsibility to extract the data from the HTTP request on the network and does all the heavy lifting to build the instance of `http_request`. + +The `http_request` class has a set of methods you will have access to when implementing your handlers: +* _**const std::string&** get_path() **const**:_ Returns the path as requested from the HTTP client. +* _**const std::vector\&** get_path_pieces() **const**:_ Returns the components of the path requested by the HTTP client (each piece of the path split by `'/'`. +* _**const std::string&** get_path_piece(int index) **const**:_ Returns one piece of the path requested by the HTTP client. The piece is selected through the `index` parameter (0-indexed). +* _**const std::string&** get_method() **const**:_ Returns the method requested by the HTTP client. +* _**const std::string&** get_header(**const std::string&** key) **const**:_ Returns the header with name equal to `key` if present in the HTTP request. Returns an `empty string` otherwise. +* _**const std::string&** get_cookie(**const std::string&** key) **const**:_ Returns the cookie with name equal to `key` if present in the HTTP request. Returns an `empty string` otherwise. +* _**const std::string&** get_footer(**const std::string&** key) **const**:_ Returns the footer with name equal to `key` if present in the HTTP request (only for http 1.1 chunked encodings). Returns an `empty string` otherwise. +* _**const std::string&** get_arg(**const std::string&** key) **const**:_ Returns the argument with name equal to `key` if present in the HTTP request. Arguments can be (1) querystring parameters, (2) path argument (in case of parametric endpoint, (3) parameters parsed from the HTTP request body if the body is in `application/x-www-form-urlencoded` or `multipart/form-data` formats and the postprocessor is enabled in the webserver (enabled by default). +* _**const std::map&** get_headers() **const**:_ Returns a map containing all the headers present in the HTTP request. +* _**const std::map&** get_cookies() **const**:_ Returns a map containing all the cookies present in the HTTP request. +* _**const std::map&** get_footers() **const**:_ Returns a map containing all the footers present in the HTTP request (only for http 1.1 chunked encodings). +* _**const std::map&** get_args() **const**:_ Returns all the arguments present in the HTTP request. Arguments can be (1) querystring parameters, (2) path argument (in case of parametric endpoint, (3) parameters parsed from the HTTP request body if the body is in `application/x-www-form-urlencoded` or `multipart/form-data` formats and the postprocessor is enabled in the webserver (enabled by default). +* _**const std::string&** get_content() **const**:_ Returns the body of the HTTP request. +* _**bool** content_too_large() **const**:_ Returns `true` if the body length of the HTTP request sent by the client is longer than the max allowed on the server. +* _**const std::string&** get_querystring() **const**:_ Returns the `querystring` of the HTTP request. +* _**const std::string&** get_version() **const**:_ Returns the HTTP version of the client request. +* _**const std::string&** get_requestor() **const**:_ Returns the IP from which the client is sending the request. +* _**unsigned short** get_requestor_port() **const**:_ Returns the port from which the client is sending the request. +* _**const std::string&** get_user() **const**:_ Returns the `user` as self-identified through basic authentication. The content of the user header will be parsed only if basic authentication is enabled on the server (enabled by default). +* _**const std::string&** get_pass() **const**:_ Returns the `password` as self-identified through basic authentication. The content of the password header will be parsed only if basic authentication is enabled on the server (enabled by default). +* _**const std::string&** get_digested_user() **const**:_ Returns the `digested user` as self-identified through digest authentication. The content of the user header will be parsed only if digest authentication is enabled on the server (enabled by default). +* _**bool** check_digest_auth(**const std::string&** realm, **const std::string&** password, **int** nonce_timeout, **bool&** reload_nonce) **const**:_ Allows to check the validity of the authentication token sent through digest authentication (if the provided values in the WWW-Authenticate header are valid and sound according to RFC2716). Takes in input the `realm` of validity of the authentication, the `password` as known to the server to compare against, the `nonce_timeout` to indicate how long the nonce is valid and `reload_nonce` a boolean that will be set by the method to indicate a nonce being reloaded. The method returns `true` if the authentication is valid, `false` otherwise. + +#### Example of handler reading arguments from a request + #include + + using namespace httpserver; + + class hello_world_resource : public http_resource { + public: + const std::shared_ptr render(const http_request& req) { + return std::shared_ptr(new string_response("Hello: " + req.get_arg("name"))); + } + }; + + int main(int argc, char** argv) { + webserver ws = create_webserver(8080); + + hello_world_resource hwr; + ws.register_resource("/hello", &hwr); + ws.start(true); + + return 0; + } + +To test the above example, you can run the following command from a terminal: + + curl -XGET -v "http://localhost:8080/hello?name=John" + +You will receive the message `Hello: John` in reply. Given that the body post processing is enabled, you can also run `curl -d "name=John" -X POST http://localhost:8080/hello` to obtain the same result. + +You can also check this example on [github](https://github.com/etr/libhttpserver/blob/master/examples/hello_with_get_arg.cpp). + +[Back to TOC](#table-of-contents) + +## Building responses to requests +As seen in the documentation of [http_resource](#the-resource-object), every extensible method returns in output a `http_response` object. The webserver takes the responsibility to convert the `http_response` object you create into a response on the network. + +There are 5 types of response that you can create - we will describe them here through their constructors: +* _string_response(**const std::string&** content, **int** response_code = `200`, **const std::string&** content_type = `"text/plain"`):_ The most basic type of response. It uses the `content` string passed in construction as body of the HTTP response. The other two optional parameters are the `response_code` and the `content_type`. You can find constant definition for the various response codes within the [http_utils](https://github.com/etr/libhttpserver/blob/master/src/httpserver/http_utils.hpp) library file. +* _file_response(**const std::string&** filename, **int** response_code = `200`, **const std::string&** content_type = `"text/plain"`):_ Uses the `filename` passed in construction as pointer to a file on disk. The body of the HTTP response will be set using the content of the file. The other two optional parameters are the `response_code` and the `content_type`. You can find constant definition for the various response codes within the [http_utils](https://github.com/etr/libhttpserver/blob/master/src/httpserver/http_utils.hpp) library file. +* _basic_auth_fail_response(**const std::string&** content, **const std::string&** realm = `""`, **int** response_code = `200`, **const std::string&** content_type = `"text/plain"`):_ A response in return to a failure during basic authentication. It allows to specify a `content` string as a message to send back to the client. The `realm` parameter should contain your realm of authentication (if any). The other two optional parameters are the `response_code` and the `content_type`. You can find constant definition for the various response codes within the [http_utils](https://github.com/etr/libhttpserver/blob/master/src/httpserver/http_utils.hpp) library file. +* _digest_auth_fail_response(**const std::string&** content, **const std::string&** realm = `""`, **const std::string&** opaque = `""`, **bool** reload_nonce = `false`, **int** response_code = `200`, **const std::string&** content_type = `"text/plain"`):_ A response in return to a failure during digest authentication. It allows to specify a `content` string as a message to send back to the client. The `realm` parameter should contain your realm of authentication (if any). The `opaque` represents a value that gets passed to the client and expected to be passed again to the server as-is. This value can be a hexadecimal or base64 string. The `reload_nonce` parameter tells the server to reload the nonce (you should use the value returned by the `check_digest_auth` method on the `http_request`. The other two optional parameters are the `response_code` and the `content_type`. You can find constant definition for the various response codes within the [http_utils](https://github.com/etr/libhttpserver/blob/master/src/httpserver/http_utils.hpp) library file. +* _deferred_response(**ssize_t(*cycle_callback_ptr)(char*, size_t)** cycle_callback, **const std::string&** content = `""`, **int** response_code = `200`, **const std::string&** content_type = `"text/plain"`):_ A response that obtains additional content from a callback executed in a deferred way. It leaves the client in pending state (returning a `100 CONTINUE` message) and suspends the connection. Besides the callback, optionally, you can provide a `content` parameter that sets the initial message sent immediately to the client. The other two optional parameters are the `response_code` and the `content_type`. You can find constant definition for the various response codes within the [http_utils](https://github.com/etr/libhttpserver/blob/master/src/httpserver/http_utils.hpp) library file. To use `deferred_response` you need to have the `deferred` option active on your webserver (enabled by default). + * The `cycle_callback_ptr` has this shape: + _**ssize_t** cycle_callback(**char*** buf, **size_t** max_size)_. + You are supposed to implement a function in this shape and provide it to the `deferred_repsonse` method. The webserver will provide a `char*` to the function. It is responsibility of the function to allocate it and fill its content. The method is supposed to respect the `max_size` parameter passed in input. The function must return a `ssize_t` value representing the actual size you filled the `buf` with. Any value different from `-1` will keep the resume the connection, deliver the content and suspend it again (with a `100 CONTINUE`). If the method returns `-1`, the webserver will complete the communication with the client and close the connection. + +### Setting additional properties of the response +The `http_response` class offers an additional set of methods to "decorate" your responses. This set of methods is: +* _**void** with_header(**const std::string&** key, **const std::string&** value):_ Sets an HTTP header with name set to `key` and value set to `value`. +* _**void** with_footer(**const std::string&** key, **const std::string&** value):_ Sets an HTTP footer with name set to `key` and value set to `value`. +* _**void** with_cookie(**const std::string&** key, **const std::string&** value):_ Sets an HTTP cookie with name set to `key` and value set to `value` (only for http 1.1 chunked encodings). +* _**void** shoutCAST():_ Mark the response as a `shoutCAST` one. + +### Example of response setting headers + #include + + using namespace httpserver; + + class hello_world_resource : public http_resource { + public: + const std::shared_ptr render(const http_request&) { + std::shared_ptr response = std::shared_ptr(new string_response("Hello, World!")); + response->with_header("MyHeader", "MyValue"); + return response; + } + }; + + int main(int argc, char** argv) { + webserver ws = create_webserver(8080); + + hello_world_resource hwr; + ws.register_resource("/hello", &hwr); + ws.start(true); + + return 0; + } + +To test the above example, you could run the following command from a terminal: + + curl -XGET -v "http://localhost:8080/hello" + +You will receive the message custom header in reply. + +You can also check this example on [github](https://github.com/etr/libhttpserver/blob/master/examples/setting_headers.cpp). + +[Back to TOC](#table-of-contents) + +## IP Blacklisting and Whitelisting +libhttpserver provides natively a system to blacklist and whitelist IP addresses. To enable/disable the system, it is possible to use the `ban_system` and `no_ban_system` methods on the `create_webserver` class. In the same way, you can specify what you want to be your "default behavior" (allow by default or disallow by default) by using the `default_policy` method (see [here](#create-and-work-with-a-webserver)). +The system supports both IPV4 and IPV6 and manages them transparently. The only requirement is for ipv6 to be enabled on your server - you'll have to enable this by using the `use_ipv6` method on `create_webserver`. + +You can explicitly ban or allow an IP address using the following methods on the `webserver` class: +* _**void** ban_ip(**const std::string&** ip):_ Adds one IP (or a range of IPs) to the list of the banned ones. Takes in input a `string` that contains the IP (or range of IPs) to ban. To use when the `default_policy` is `ACCEPT`. +* _**void** allow_ip(**const std::string&** ip):_ Adds one IP (or a range of IPs) to the list of the allowed ones. Takes in input a `string` that contains the IP (or range of IPs) to allow. To use when the `default_policy` is `REJECT`. +* _**void** unban_ip(**const std::string&** ip):_ Removes one IP (or a range of IPs) from the list of the banned ones. Takes in input a `string` that contains the IP (or range of IPs) to remove from the list. To use when the `default_policy` is `REJECT`. +* _**void** disallow_ip(**const std::string&** ip):_ Removes one IP (or a range of IPs) from the list of the allowed ones. Takes in input a `string` that contains the IP (or range of IPs) to remove from the list. To use when the `default_policy` is `REJECT`. + +### IP String Format +The IP string format can represent both IPV4 and IPV6. Addresses will be normalized by the webserver to operate in the same sapce. Any valid IPV4 or IPV6 textual representation works. +It is also possible to specify ranges of IPs. To do so, omit the octect you want to express as a range and specify a `'*'` in its place. +Examples of valid IPs include: +* `"192.168.5.5"`: standard IPV4 +* `"192.168.*.*"`: range of IPV4 addresses. In the example, everything between `192.168.0.0` and `192.168.255.255`. +* `"2001:db8:8714:3a90::12"`: standard IPV6 - clustered empty ranges are fully supported. +* `"2001:db8:8714:3a90:*:*"`: range of IPV6 addresses. +* `"::ffff:192.0.2.128"`: IPV4 IPs nested into IPV6. +* `"::192.0.2.128"`: IPV4 IPs nested into IPV6 (without `'ffff'` prefix) +* `"::ffff:192.0.*.*"`: ranges of IPV4 IPs nested into IPV6. + +#### Example of IP Whitelisting/Blacklisting + #include + + using namespace httpserver; + + class hello_world_resource : public http_resource { + public: + const std::shared_ptr render(const http_request&) { + return std::shared_ptr(new string_response("Hello, World!")); + } + }; + + int main(int argc, char** argv) { + webserver ws = create_webserver(8080) + .default_policy(http::http_utils::REJECT); + + ws.allow_ip("127.0.0.1"); + + hello_world_resource hwr; + ws.register_resource("/hello", &hwr); + ws.start(true); + + return 0; + } + +To test the above example, you could run the following command from a terminal: + + curl -XGET -v "http://localhost:8080/hello" + +You can also check this example on [github](https://github.com/etr/libhttpserver/blob/master/examples/minimal_ip_ban.cpp). + +[Back to TOC](#table-of-contents) + +## Authentication +libhttpserver support three types of client authentication. + +Basic authentication uses a simple authentication method based on BASE64 algorithm. Username and password are exchanged in clear between the client and the server, so this method must only be used for non-sensitive content or when the session is protected with https. When using basic authentication libhttpserver will have access to the clear password, possibly allowing to create a chained authentication toward an external authentication server. You can enable/disable support for Basic authentication through the `basic_auth` and `no_basic_auth` methods of the `create_webserver` class. + +Digest authentication uses a one-way authentication method based on MD5 hash algorithm. Only the hash will transit over the network, hence protecting the user password. The nonce will prevent replay attacks. This method is appropriate for general use, especially when https is not used to encrypt the session. You can enable/disable support for Digest authentication through the `digest_auth` and `no_digest_auth` methods of the `create_webserver` class. + +Client certificate authentication uses a X.509 certificate from the client. This is the strongest authentication mechanism but it requires the use of HTTPS. Client certificate authentication can be used simultaneously with Basic or Digest Authentication in order to provide a two levels authentication (like for instance separate machine and user authentication). You can enable/disable support for Certificate authentication through the `use_ssl` and `no_ssl` methods of the `create_webserver` class. + +### Using Basic Authentication + #include + + using namespace httpserver; + + class user_pass_resource : public httpserver::http_resource { + public: + const std::shared_ptr render_GET(const http_request& req) { + if (req.get_user() != "myuser" || req.get_pass() != "mypass") { + return std::shared_ptr(new basic_auth_fail_response("FAIL", "test@example.com")); + } + return std::shared_ptr(new string_response(req.get_user() + " " + req.get_pass(), 200, "text/plain")); + } + }; + + int main(int argc, char** argv) { + webserver ws = create_webserver(8080); + + user_pass_resource hwr; + ws.register_resource("/hello", &hwr); + ws.start(true); + + return 0; + } + +To test the above example, you can run the following command from a terminal: + + curl -XGET -v -u myuser:mypass "http://localhost:8080/hello" + +You will receive back the user and password you passed in input. Try to pass the wrong credentials to see the failure. + +You can also check this example on [github](https://github.com/etr/libhttpserver/blob/master/examples/basic_authentication.cpp). + +### Using Digest Authentication + #include + + #define MY_OPAQUE "11733b200778ce33060f31c9af70a870ba96ddd4" + + using namespace httpserver; + + class digest_resource : public httpserver::http_resource { + public: + const std::shared_ptr render_GET(const http_request& req) { + if (req.get_digested_user() == "") { + return std::shared_ptr(new digest_auth_fail_response("FAIL", "test@example.com", MY_OPAQUE, true)); + } + else { + bool reload_nonce = false; + if(!req.check_digest_auth("test@example.com", "mypass", 300, reload_nonce)) { + return std::shared_ptr(new digest_auth_fail_response("FAIL", "test@example.com", MY_OPAQUE, reload_nonce)); + } + } + return std::shared_ptr(new string_response("SUCCESS", 200, "text/plain")); + } + }; + + int main(int argc, char** argv) { + webserver ws = create_webserver(8080); + + digest_resource hwr; + ws.register_resource("/hello", &hwr); + ws.start(true); + + return 0; + } + +To test the above example, you can run the following command from a terminal: + + curl -XGET -v --digest --user myuser:mypass localhost:8080/hello + +You will receive a `SUCCESS` in response (observe the response message from the server in detail and you'll see the full interaction). Try to pass the wrong credentials or send a request without `digest` active to see the failure. + +You can also check this example on [github](https://github.com/etr/libhttpserver/blob/master/examples/digest_authentication.cpp). + +[Back to TOC](#table-of-contents) + +## HTTP Utils +libhttpserver provides a set of constants to help you develop your HTTP server. It would be redudant to list them here; so, please, consult the list directly [here](https://github.com/etr/libhttpserver/blob/master/src/httpserver/http_utils.hpp). + +[Back to TOC](#table-of-contents) + +## Other Examples + +#### Example of returning a response from a file + #include + + using namespace httpserver; + + class file_response_resource : public http_resource { + public: + const std::shared_ptr render_GET(const http_request& req) { + return std::shared_ptr(new file_response("test_content", 200, "text/plain")); + } + }; + + int main(int argc, char** argv) { + webserver ws = create_webserver(8080); + + file_response_resource hwr; + ws.register_resource("/hello", &hwr); + ws.start(true); + + return 0; + } + +To test the above example, you can run the following command from a terminal: + + curl -XGET -v localhost:8080/hello + +You can also check this example on [github](https://github.com/etr/libhttpserver/blob/master/examples/minimal_file_response.cpp). + +#### Example of a deferred response through callback + #include + + using namespace httpserver; + + static int counter = 0; + + ssize_t test_callback (char* buf, size_t max) { + if (counter == 2) { + return -1; + } else { + memset(buf, 0, max); + strcat(buf, " test "); + counter++; + return std::string(buf).size(); + } + } + + class deferred_resource : public http_resource { + public: + const std::shared_ptr render_GET(const http_request& req) { + return std::shared_ptr(new deferred_response(test_callback, "cycle callback response")); + } + }; + + int main(int argc, char** argv) { + webserver ws = create_webserver(8080); + + deferred_resource hwr; + ws.register_resource("/hello", &hwr); + ws.start(true); + + return 0; + } + +To test the above example, you can run the following command from a terminal: + + curl -XGET -v localhost:8080/hello + +You can also check this example on [github](https://github.com/etr/libhttpserver/blob/master/examples/minimal_deferred.cpp). + +[Back to TOC](#table-of-contents) + +## Copying +This manual is for libhttpserver, C++ library for creating an embedded Rest HTTP server (and more). + +> Permission is granted to copy, distribute and/or modify this document +> under the terms of the GNU Free Documentation License, Version 1.3 +> or any later version published by the Free Software Foundation; +> with no Invariant Sections, no Front-Cover Texts, and no Back-Cover +> Texts. A copy of the license is included in the section entitled GNU +> Free Documentation License. + +[Back to TOC](#table-of-contents) + +## GNU Lesser General Public License Version 2.1, February 1999 Copyright © 1991, 1999 Free Software Foundation, Inc. @@ -682,8 +1371,9 @@ necessary. Here is a sample; alter the names: That's all there is to it! -GNU Free Documentation License -============================== +[Back to TOC](#table-of-contents) + +## GNU Free Documentation License Version 1.3, 3 November 2008 @@ -1133,3 +1823,59 @@ If your document contains nontrivial examples of program code, we recommend releasing these examples in parallel under your choice of free software license, such as the GNU General Public License, to permit their use in free software. + +[Back to TOC](#table-of-contents) + +## Thanks + +This library has been originally developed under the zencoders flags and this community has always supported me all along this work so I am happy to put the logo on this readme. + + When you see this tree, know that you've came across ZenCoders.org + + with open('ZenCoders. + `num` in numbers synchronized + datetime d glob. sys.argv[2] . + def myclass `..` @@oscla org. . class { + displ hooks( public static void ma functor: + $myclass->method( impport sys, os.pipe ` @param name` + fcl if(system(cmd) myc. /de ` $card( array("a" srand + format lists: ++: conc ++ "my an WHERE for( == myi + `sys: myvalue(myvalue) sys.t Console.W try{ rais using + connec SELECT * FROM table mycnf acco desc and or selector::clas at + openldap string sys. print "zenc der " { 'a': `ls -l` > appe &firs + import Tkinter paste( $obh &a or it myval bro roll: :: [] require a + case `` super. +y expr say " %rooms 1 --account fb- yy + proc meth Animate => send(D, open) putd EndIf 10 whi myc` cont + and main (--) import loop $$ or end onload UNION WITH tab timer 150 *2 + end. begin True GtkLabel *label doto partition te let auto i<- (i + d ); + .mushup ``/. ^/zenc/ myclass->her flv op <> element >> 71 or + QFileDi : and .. with myc toA channel::bo myc isEmpty a not bodt; + class T public pol str mycalc d pt &&a *i fc add ^ac + ::ZenCoders::core::namespac boost::function st f = std: ;; int assert + cout << endl public genera #include "b ost ::ac myna const cast mys + ac size_t return ran int (*getNextValue)(void) ff double sa_family_t famil + pu a do puts(" ac int main(int argc, char* "%5d struct nam + cs float for typedef enum puts getchar() + if( else #define fp FILE* f char* s + i++ strcat( %s int + 31] total+= do + }do while(1) sle + getc strcpy( a for + prin scanf(%d, & get + int void myfunc(int pa retu + BEQ BNEQZ R1 10 ANDI R1 R2 SYS + XOR SYSCALL 5 SLTIU MFLO 15 SW JAL + BNE BLTZAL R1 1 LUI 001 NOOP MULTU SLLV + MOV R1 ADD R1 R2 JUMP 10 1001 BEQ R1 R2 1 ANDI + 1101 1010001100 111 001 01 1010 101100 1001 100 + 110110 100 0 01 101 01100 100 100 1000100011 + 11101001001 00 11 100 11 10100010 + 000101001001 10 1001 101000101 + 010010010010110101001010 + +For further information: +visit our website www.zencoders.org + +**Author:** Sebastiano Merlino + +[Back to TOC](#table-of-contents) From ffa98fab4929b117b43b2f7a1e8087bfc5b7b255 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 13 Jan 2019 19:13:42 -0800 Subject: [PATCH 404/623] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f974588d..bacb5ba1 100644 --- a/README.md +++ b/README.md @@ -337,7 +337,7 @@ libhttpserver supports IP blacklisting and whitelisting as an internal feature. * _.ban_system() and .no_ban_system:_ Can be used to enable/disable the ban system. `on` by default. * _.default_policy(**const http::http_utils::policy_T&** default_policy):_ Specifies what should be the default behavior when receiving a request. Possible values are `ACCEPT` and `REJECT`. Default is `ACCEPT`. -### Authentication +### Authentication Parameters * _.basic_auth() and .no_basic_auth:_ Can be used to enable/disable parsing of the basic authorization header sent by the client. `on` by default. * _.digest_auth() and .no_digest_auth:_ Can be used to enable/disable parsing of the digested authentication data sent by the client. `on` by default. * _.nonce_nc_size(**int** nonce_size):_ Size of an array of nonce and nonce counter map. This option represents the size (number of elements) of a map of a nonce and a nonce-counter. If this option is not specified, a default value of 4 will be used (which might be too small for servers handling many requests). From 1e1c622ebbb59f709a62060997c96ef9551982fd Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Tue, 15 Jan 2019 07:19:53 +0000 Subject: [PATCH 405/623] Making examples building optional --- Makefile.am | 10 ++++++++-- configure.ac | 7 +++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/Makefile.am b/Makefile.am index 4deb3bf8..14604f4f 100644 --- a/Makefile.am +++ b/Makefile.am @@ -24,8 +24,14 @@ LIBTOOL_DEPS = @LIBTOOL_DEPS@ AUTOMAKE_OPTIONS = foreign 1.4 ACLOCAL_AMFLAGS = -I m4 -SUBDIRS = src test examples -DIST_SUBDIRS = src test examples +SUBDIRS = src test +DIST_SUBDIRS = src test + +if BUILD_EXAMPLES +SUBDIRS += examples +DIST_SUBDIRS += examples +endif + EXTRA_DIST = libhttpserver.pc.in $(DX_CONFIG) MOSTLYCLEANFILES = $(DX_CLEANFILES) *.gcda *.gcno *.gcov diff --git a/configure.ac b/configure.ac index 2d859e7f..03e5634f 100644 --- a/configure.ac +++ b/configure.ac @@ -286,6 +286,12 @@ if test x"$coverit" = x"yes"; then esac fi +AC_ARG_ENABLE([[examples]], + [AS_HELP_STRING([[--disable-examples]], [do not build any examples])], , + [enable_examples=yes]) +test "x$enable_examples" = "xno" || enable_examples=yes +AM_CONDITIONAL([BUILD_EXAMPLES], [test "x$enable_examples" = "xyes"]) + if test "$CROSS_COMPILE" == "1"; then if test "$ARM_ARCH_DIR" == "aarch64-linux-gnu"; then AM_CXXFLAGS="$AM_CXXFLAGS --prefix=${ARM_LD_PATH}/" @@ -355,4 +361,5 @@ AC_MSG_NOTICE([Configuration Summary: poll support : ${enable_poll=no} epoll support : ${enable_epoll=no} Static : ${static} + Build examples : ${enable_examples} ]) From 41f1b72cfe27989085bc0e6291cd80bda0f0e08c Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 13 Jan 2019 08:13:54 +0000 Subject: [PATCH 406/623] Created additional examples --- examples/Makefile.am | 8 +++- examples/allowing_disallowing_methods.cpp | 42 +++++++++++++++++++++ examples/basic_authentication.cpp | 46 +++++++++++++++++++++++ examples/custom_access_log.cpp | 46 +++++++++++++++++++++++ examples/handlers.cpp | 45 ++++++++++++++++++++++ examples/hello_with_get_arg.cpp | 40 ++++++++++++++++++++ examples/setting_headers.cpp | 42 +++++++++++++++++++++ 7 files changed, 268 insertions(+), 1 deletion(-) create mode 100644 examples/allowing_disallowing_methods.cpp create mode 100644 examples/basic_authentication.cpp create mode 100644 examples/custom_access_log.cpp create mode 100644 examples/handlers.cpp create mode 100644 examples/hello_with_get_arg.cpp create mode 100644 examples/setting_headers.cpp diff --git a/examples/Makefile.am b/examples/Makefile.am index a820556d..bf2da41e 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -19,9 +19,15 @@ LDADD = $(top_builddir)/src/libhttpserver.la AM_CPPFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/src/httpserver/ METASOURCES = AUTO -noinst_PROGRAMS = hello_world service minimal_hello_world custom_error +noinst_PROGRAMS = hello_world service minimal_hello_world custom_error allowing_disallowing_methods handlers hello_with_get_arg setting_headers custom_access_log basic_authentication hello_world_SOURCES = hello_world.cpp service_SOURCES = service.cpp minimal_hello_world_SOURCES = minimal_hello_world.cpp custom_error_SOURCES = custom_error.cpp +allowing_disallowing_methods_SOURCES = allowing_disallowing_methods.cpp +handlers_SOURCES = handlers.cpp +hello_with_get_arg_SOURCES = hello_with_get_arg.cpp +setting_headers_SOURCES = setting_headers.cpp +custom_access_log_SOURCES = custom_access_log.cpp +basic_authentication_SOURCES = basic_authentication.cpp diff --git a/examples/allowing_disallowing_methods.cpp b/examples/allowing_disallowing_methods.cpp new file mode 100644 index 00000000..69295af4 --- /dev/null +++ b/examples/allowing_disallowing_methods.cpp @@ -0,0 +1,42 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#include + +using namespace httpserver; + +class hello_world_resource : public http_resource { +public: + const std::shared_ptr render(const http_request&) { + return std::shared_ptr(new string_response("Hello, World!")); + } +}; + +int main(int argc, char** argv) { + webserver ws = create_webserver(8080); + + hello_world_resource hwr; + hwr.disallow_all(); + hwr.set_allowing("GET", true); + ws.register_resource("/hello", &hwr); + ws.start(true); + + return 0; +} diff --git a/examples/basic_authentication.cpp b/examples/basic_authentication.cpp new file mode 100644 index 00000000..84de823e --- /dev/null +++ b/examples/basic_authentication.cpp @@ -0,0 +1,46 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#include + +using namespace httpserver; + +class user_pass_resource : public httpserver::http_resource +{ + public: + const std::shared_ptr render_GET(const http_request& req) + { + if (req.get_user() != "myuser" || req.get_pass() != "mypass") + { + return std::shared_ptr(new basic_auth_fail_response("FAIL", "test@example.com")); + } + return std::shared_ptr(new string_response(req.get_user() + " " + req.get_pass(), 200, "text/plain")); + } +}; + +int main(int argc, char** argv) { + webserver ws = create_webserver(8080); + + user_pass_resource hwr; + ws.register_resource("/hello", &hwr); + ws.start(true); + + return 0; +} diff --git a/examples/custom_access_log.cpp b/examples/custom_access_log.cpp new file mode 100644 index 00000000..7e5f1ef5 --- /dev/null +++ b/examples/custom_access_log.cpp @@ -0,0 +1,46 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#include +#include + +using namespace httpserver; + +void custom_access_log(const std::string& url) { + std::cout << "ACCESSING: " << url << std::endl; +} + +class hello_world_resource : public http_resource { +public: + const std::shared_ptr render(const http_request&) { + return std::shared_ptr(new string_response("Hello, World!")); + } +}; + +int main(int argc, char** argv) { + webserver ws = create_webserver(8080) + .log_access(custom_access_log); + + hello_world_resource hwr; + ws.register_resource("/hello", &hwr); + ws.start(true); + + return 0; +} diff --git a/examples/handlers.cpp b/examples/handlers.cpp new file mode 100644 index 00000000..4ddad426 --- /dev/null +++ b/examples/handlers.cpp @@ -0,0 +1,45 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#include + +using namespace httpserver; + +class hello_world_resource : public http_resource { +public: + const std::shared_ptr render_GET(const http_request&) { + return std::shared_ptr(new string_response("GET: Hello, World!")); + } + + const std::shared_ptr render(const http_request&) { + return std::shared_ptr(new string_response("OTHER: Hello, World!")); + } +}; + +int main(int argc, char** argv) { + webserver ws = create_webserver(8080); + + hello_world_resource hwr; + ws.register_resource("/hello", &hwr); + ws.start(true); + + return 0; +} + diff --git a/examples/hello_with_get_arg.cpp b/examples/hello_with_get_arg.cpp new file mode 100644 index 00000000..07ae90c1 --- /dev/null +++ b/examples/hello_with_get_arg.cpp @@ -0,0 +1,40 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#include + +using namespace httpserver; + +class hello_world_resource : public http_resource { +public: + const std::shared_ptr render(const http_request& req) { + return std::shared_ptr(new string_response("Hello: " + req.get_arg("name"))); + } +}; + +int main(int argc, char** argv) { + webserver ws = create_webserver(8080); + + hello_world_resource hwr; + ws.register_resource("/hello", &hwr); + ws.start(true); + + return 0; +} diff --git a/examples/setting_headers.cpp b/examples/setting_headers.cpp new file mode 100644 index 00000000..0fb73c79 --- /dev/null +++ b/examples/setting_headers.cpp @@ -0,0 +1,42 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#include + +using namespace httpserver; + +class hello_world_resource : public http_resource { +public: + const std::shared_ptr render(const http_request&) { + std::shared_ptr response = std::shared_ptr(new string_response("Hello, World!")); + response->with_header("MyHeader", "MyValue"); + return response; + } +}; + +int main(int argc, char** argv) { + webserver ws = create_webserver(8080); + + hello_world_resource hwr; + ws.register_resource("/hello", &hwr); + ws.start(true); + + return 0; +} From 8b052df05591ea94164368306ff3333fe13ff33a Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 13 Jan 2019 21:53:56 +0000 Subject: [PATCH 407/623] Additional examples Digest authentication. Deferred response. File response. HTTPS. --- configure.ac | 3 ++ examples/Makefile.am | 6 +++- examples/digest_authentication.cpp | 51 +++++++++++++++++++++++++++ examples/minimal_deferred.cpp | 55 ++++++++++++++++++++++++++++++ examples/minimal_file_response.cpp | 42 +++++++++++++++++++++++ examples/minimal_https.cpp | 43 +++++++++++++++++++++++ 6 files changed, 199 insertions(+), 1 deletion(-) create mode 100644 examples/digest_authentication.cpp create mode 100644 examples/minimal_deferred.cpp create mode 100644 examples/minimal_file_response.cpp create mode 100644 examples/minimal_https.cpp diff --git a/configure.ac b/configure.ac index dc42c5f8..2d859e7f 100644 --- a/configure.ac +++ b/configure.ac @@ -332,6 +332,9 @@ AC_CONFIG_LINKS([test/test_content:test/test_content]) AC_CONFIG_LINKS([test/cert.pem:test/cert.pem]) AC_CONFIG_LINKS([test/key.pem:test/key.pem]) AC_CONFIG_LINKS([test/test_root_ca.pem:test/test_root_ca.pem]) +AC_CONFIG_LINKS([examples/cert.pem:examples/cert.pem]) +AC_CONFIG_LINKS([examples/key.pem:examples/key.pem]) +AC_CONFIG_LINKS([examples/test_content:examples/test_content]) AC_OUTPUT( libhttpserver.pc diff --git a/examples/Makefile.am b/examples/Makefile.am index bf2da41e..548b3168 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -19,7 +19,7 @@ LDADD = $(top_builddir)/src/libhttpserver.la AM_CPPFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/src/httpserver/ METASOURCES = AUTO -noinst_PROGRAMS = hello_world service minimal_hello_world custom_error allowing_disallowing_methods handlers hello_with_get_arg setting_headers custom_access_log basic_authentication +noinst_PROGRAMS = hello_world service minimal_hello_world custom_error allowing_disallowing_methods handlers hello_with_get_arg setting_headers custom_access_log basic_authentication digest_authentication minimal_https minimal_file_response minimal_deferred hello_world_SOURCES = hello_world.cpp service_SOURCES = service.cpp @@ -31,3 +31,7 @@ hello_with_get_arg_SOURCES = hello_with_get_arg.cpp setting_headers_SOURCES = setting_headers.cpp custom_access_log_SOURCES = custom_access_log.cpp basic_authentication_SOURCES = basic_authentication.cpp +digest_authentication_SOURCES = digest_authentication.cpp +minimal_https_SOURCES = minimal_https.cpp +minimal_file_response_SOURCES = minimal_file_response.cpp +minimal_deferred_SOURCES = minimal_deferred.cpp diff --git a/examples/digest_authentication.cpp b/examples/digest_authentication.cpp new file mode 100644 index 00000000..55f119a0 --- /dev/null +++ b/examples/digest_authentication.cpp @@ -0,0 +1,51 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#include + +#define MY_OPAQUE "11733b200778ce33060f31c9af70a870ba96ddd4" + +using namespace httpserver; + +class digest_resource : public httpserver::http_resource { +public: + const std::shared_ptr render_GET(const http_request& req) { + if (req.get_digested_user() == "") { + return std::shared_ptr(new digest_auth_fail_response("FAIL", "test@example.com", MY_OPAQUE, true)); + } + else { + bool reload_nonce = false; + if(!req.check_digest_auth("test@example.com", "mypass", 300, reload_nonce)) { + return std::shared_ptr(new digest_auth_fail_response("FAIL", "test@example.com", MY_OPAQUE, reload_nonce)); + } + } + return std::shared_ptr(new string_response("SUCCESS", 200, "text/plain")); + } +}; + +int main(int argc, char** argv) { + webserver ws = create_webserver(8080); + + digest_resource hwr; + ws.register_resource("/hello", &hwr); + ws.start(true); + + return 0; +} diff --git a/examples/minimal_deferred.cpp b/examples/minimal_deferred.cpp new file mode 100644 index 00000000..a08599c2 --- /dev/null +++ b/examples/minimal_deferred.cpp @@ -0,0 +1,55 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#include + +using namespace httpserver; + +static int counter = 0; + +ssize_t test_callback (char* buf, size_t max) { + if (counter == 2) { + return -1; + } + else { + memset(buf, 0, max); + strcat(buf, " test "); + counter++; + return std::string(buf).size(); + } +} + +class deferred_resource : public http_resource { + public: + const std::shared_ptr render_GET(const http_request& req) { + return std::shared_ptr(new deferred_response(test_callback, "cycle callback response")); + } +}; + +int main(int argc, char** argv) { + webserver ws = create_webserver(8080); + + deferred_resource hwr; + ws.register_resource("/hello", &hwr); + ws.start(true); + + return 0; +} + diff --git a/examples/minimal_file_response.cpp b/examples/minimal_file_response.cpp new file mode 100644 index 00000000..b82d2929 --- /dev/null +++ b/examples/minimal_file_response.cpp @@ -0,0 +1,42 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#include + +using namespace httpserver; + +class file_response_resource : public http_resource +{ + public: + const std::shared_ptr render_GET(const http_request& req) + { + return std::shared_ptr(new file_response("test_content", 200, "text/plain")); + } +}; + +int main(int argc, char** argv) { + webserver ws = create_webserver(8080); + + file_response_resource hwr; + ws.register_resource("/hello", &hwr); + ws.start(true); + + return 0; +} diff --git a/examples/minimal_https.cpp b/examples/minimal_https.cpp new file mode 100644 index 00000000..def0452a --- /dev/null +++ b/examples/minimal_https.cpp @@ -0,0 +1,43 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#include + +using namespace httpserver; + +class hello_world_resource : public http_resource { +public: + const std::shared_ptr render(const http_request&) { + return std::shared_ptr(new string_response("Hello, World!")); + } +}; + +int main(int argc, char** argv) { + webserver ws = create_webserver(8080) + .use_ssl() + .https_mem_key("key.pem") + .https_mem_cert("cert.pem"); + + hello_world_resource hwr; + ws.register_resource("/hello", &hwr); + ws.start(true); + + return 0; +} From 9dddada2e0712583241f691bff5e002c07492de2 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 13 Jan 2019 23:33:57 +0000 Subject: [PATCH 408/623] Adding missing test file --- examples/test_content | 1 + 1 file changed, 1 insertion(+) create mode 100755 examples/test_content diff --git a/examples/test_content b/examples/test_content new file mode 100755 index 00000000..5f643138 --- /dev/null +++ b/examples/test_content @@ -0,0 +1 @@ +test content of file From 9863fe5e4b714851b2e7a5434db0e5ded8b1c67c Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Mon, 14 Jan 2019 01:18:34 +0000 Subject: [PATCH 409/623] Additional examples. url_registration and ban_system --- examples/Makefile.am | 4 ++- examples/minimal_ip_ban.cpp | 43 ++++++++++++++++++++++++ examples/url_registration.cpp | 63 +++++++++++++++++++++++++++++++++++ 3 files changed, 109 insertions(+), 1 deletion(-) create mode 100644 examples/minimal_ip_ban.cpp create mode 100644 examples/url_registration.cpp diff --git a/examples/Makefile.am b/examples/Makefile.am index 548b3168..d957e3c8 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -19,7 +19,7 @@ LDADD = $(top_builddir)/src/libhttpserver.la AM_CPPFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/src/httpserver/ METASOURCES = AUTO -noinst_PROGRAMS = hello_world service minimal_hello_world custom_error allowing_disallowing_methods handlers hello_with_get_arg setting_headers custom_access_log basic_authentication digest_authentication minimal_https minimal_file_response minimal_deferred +noinst_PROGRAMS = hello_world service minimal_hello_world custom_error allowing_disallowing_methods handlers hello_with_get_arg setting_headers custom_access_log basic_authentication digest_authentication minimal_https minimal_file_response minimal_deferred url_registration minimal_ip_ban hello_world_SOURCES = hello_world.cpp service_SOURCES = service.cpp @@ -35,3 +35,5 @@ digest_authentication_SOURCES = digest_authentication.cpp minimal_https_SOURCES = minimal_https.cpp minimal_file_response_SOURCES = minimal_file_response.cpp minimal_deferred_SOURCES = minimal_deferred.cpp +url_registration_SOURCES = url_registration.cpp +minimal_ip_ban_SOURCES = minimal_ip_ban.cpp diff --git a/examples/minimal_ip_ban.cpp b/examples/minimal_ip_ban.cpp new file mode 100644 index 00000000..ea0923e7 --- /dev/null +++ b/examples/minimal_ip_ban.cpp @@ -0,0 +1,43 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#include + +using namespace httpserver; + +class hello_world_resource : public http_resource { +public: + const std::shared_ptr render(const http_request&) { + return std::shared_ptr(new string_response("Hello, World!")); + } +}; + +int main(int argc, char** argv) { + webserver ws = create_webserver(8080) + .default_policy(http::http_utils::REJECT); + + ws.allow_ip("127.0.0.1"); + + hello_world_resource hwr; + ws.register_resource("/hello", &hwr); + ws.start(true); + + return 0; +} diff --git a/examples/url_registration.cpp b/examples/url_registration.cpp new file mode 100644 index 00000000..0f8da6ad --- /dev/null +++ b/examples/url_registration.cpp @@ -0,0 +1,63 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#include + +using namespace httpserver; + +class hello_world_resource : public http_resource { +public: + const std::shared_ptr render(const http_request&) { + return std::shared_ptr(new string_response("Hello, World!")); + } +}; + +class handling_multiple_resource : public http_resource { +public: + const std::shared_ptr render(const http_request& req) { + return std::shared_ptr(new string_response("Your URL: " + req.get_path())); + } +}; + +class url_args_resource : public http_resource { +public: + const std::shared_ptr render(const http_request& req) { + return std::shared_ptr(new string_response("ARGS: " + req.get_arg("arg1") + " and " + req.get_arg("arg2"))); + } +}; + +int main(int argc, char** argv) { + webserver ws = create_webserver(8080); + + hello_world_resource hwr; + ws.register_resource("/hello", &hwr); + + handling_multiple_resource hmr; + ws.register_resource("/family", &hmr, true); + ws.register_resource("/with_regex_[0-9]+", &hmr); + + url_args_resource uar; + ws.register_resource("/url/with/{arg1}/and/{arg2}", &uar); + ws.register_resource("/url/with/parametric/args/{arg1|[0-9]+}/and/{arg2|[A-Z]+}", &uar); + + ws.start(true); + + return 0; +} From de0ceb8003c3c7e1ef994673cf7ab602d6ee86cb Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 13 Jan 2019 19:12:32 -0800 Subject: [PATCH 410/623] Update README.me to the latest version --- README.md | 1072 +++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 909 insertions(+), 163 deletions(-) diff --git a/README.md b/README.md index 84c0effe..f974588d 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ -The libhttpserver (0.8.0) reference manual -========================================== +# The libhttpserver reference manual [![Build Status](https://travis-ci.org/etr/libhttpserver.png?branch=master)](https://travis-ci.org/etr/libhttpserver) [![codecov](https://codecov.io/gh/etr/libhttpserver/branch/master/graph/badge.svg)](https://codecov.io/gh/etr/libhttpserver) @@ -18,178 +17,868 @@ The libhttpserver (0.8.0) reference manual [![ko-fi](https://www.ko-fi.com/img/donate_sm.png)](https://ko-fi.com/F1F5HY8B) -This library has been originally developed under the zencoders flags and this community has always supported me all along this work so I am happy to put the logo on this readme. - - When you see this tree, know that you've came across ZenCoders.org - - with open('ZenCoders. - `num` in numbers synchronized - datetime d glob. sys.argv[2] . - def myclass `..` @@oscla org. . class { - displ hooks( public static void ma functor: - $myclass->method( impport sys, os.pipe ` @param name` - fcl if(system(cmd) myc. /de ` $card( array("a" srand - format lists: ++: conc ++ "my an WHERE for( == myi - `sys: myvalue(myvalue) sys.t Console.W try{ rais using - connec SELECT * FROM table mycnf acco desc and or selector::clas at - openldap string sys. print "zenc der " { 'a': `ls -l` > appe &firs - import Tkinter paste( $obh &a or it myval bro roll: :: [] require a - case `` super. +y expr say " %rooms 1 --account fb- yy - proc meth Animate => send(D, open) putd EndIf 10 whi myc` cont - and main (--) import loop $$ or end onload UNION WITH tab timer 150 *2 - end. begin True GtkLabel *label doto partition te let auto i<- (i + d ); - .mushup ``/. ^/zenc/ myclass->her flv op <> element >> 71 or - QFileDi : and .. with myc toA channel::bo myc isEmpty a not bodt; - class T public pol str mycalc d pt &&a *i fc add ^ac - ::ZenCoders::core::namespac boost::function st f = std: ;; int assert - cout << endl public genera #include "b ost ::ac myna const cast mys - ac size_t return ran int (*getNextValue)(void) ff double sa_family_t famil - pu a do puts(" ac int main(int argc, char* "%5d struct nam - cs float for typedef enum puts getchar() - if( else #define fp FILE* f char* s - i++ strcat( %s int - 31] total+= do - }do while(1) sle - getc strcpy( a for - prin scanf(%d, & get - int void myfunc(int pa retu - BEQ BNEQZ R1 10 ANDI R1 R2 SYS - XOR SYSCALL 5 SLTIU MFLO 15 SW JAL - BNE BLTZAL R1 1 LUI 001 NOOP MULTU SLLV - MOV R1 ADD R1 R2 JUMP 10 1001 BEQ R1 R2 1 ANDI - 1101 1010001100 111 001 01 1010 101100 1001 100 - 110110 100 0 01 101 01100 100 100 1000100011 - 11101001001 00 11 100 11 10100010 - 000101001001 10 1001 101000101 - 010010010010110101001010 - -For further information: -visit our website www.zencoders.org - -Author: Sebastiano Merlino - -Copying -======= -This manual is for libhttpserver, C++ library for creating an -embedded Rest HTTP server (and more). - -> Permission is granted to copy, distribute and/or modify this document -> under the terms of the GNU Free Documentation License, Version 1.3 -> or any later version published by the Free Software Foundation; -> with no Invariant Sections, no Front-Cover Texts, and no Back-Cover -> Texts. A copy of the license is included in the section entitled GNU -> Free Documentation License. - -Contents -======== -* Introduction. -* Requirements. -* Compilation. -* Constants. -* Structures and classes type definition. -* Callback functions definition. -* Create and work with server. -* Registering resources. -* Building responses to requests. -* Whitelists and Blacklists. -* Simple comet semantics. -* Utilizing Authentication. -* Obtaining and modifying status information. - -Appendices ----------- -* GNU-LGPL: The GNU Lesser General Public License says how you can copy and share almost all of libhttpserver. -* GNU-FDL: The GNU Free Documentation License says how you can copy and share the documentation of libhttpserver. - -Introduction -============ -libhttpserver is meant to constitute an easy system to build HTTP -servers with REST fashion. -libhttpserver is based on libmicrohttpd and, like this, it is a -daemon library. -The mission of this library is to support all possible HTTP features -directly and with a simple semantic allowing then the user to concentrate -only on his application and not on HTTP request handling details. - -The library is supposed to work transparently for the client Implementing -the business logic and using the library itself to realize an interface. -If the user wants it must be able to change every behavior of the library -itself through the registration of callbacks. - -Like the api is based on (libmicrohttpd), libhttpserver is able to decode -certain body format a and automatically format them in object oriented -fashion. This is true for query arguments and for *POST* and *PUT* -requests bodies if *application/x-www-form-urlencoded* or -*multipart/form-data* header are passed. - -The header reproduce all the constants defined by libhttpserver. -These maps various constant used by the HTTP protocol that are exported -as a convenience for users of the library. Is is possible for the user -to define their own extensions of the HTTP standard and use those with -libhttpserver. - -All functions are guaranteed to be completely reentrant and -thread-safe (unless differently specified). -Additionally, clients can specify resource limits on the overall -number of connections, number of connections per IP address and memory -used per connection to avoid resource exhaustion. - -Requirements -============ -* g++ >= 4.1.2 -* libmicrohttpd >= 0.9.37 -* doxygen (if you want to build code reference) +## Tl;dr +libhttpserver is a C++ library for building high performance RESTful web servers. +libhttpserver is buildt upon [libmicrohttpd](https://www.gnu.org/software/libmicrohttpd/) to provide a simple API for developers to create HTTP services in C++. + +**Features:** +- HTTP 1.1 compatible request parser +- RESTful oriented interface +- Flexible handler API +- Cross-platform compatible +- Implementation is HTTP 1.1 compliant +- Multiple threading models +- Support for IPv6 +- Support for SHOUTcast +- Support for incremental processing of POST data (optional) +- Support for basic and digest authentication (optional) +- Support for TLS (requires libgnutls, optional) + +## Table of Contents +* [Introduction](#introduction) +* [Requirements](#requirements) +* [Building](#building) +* [Getting Started](#getting-started) +* [Structures and classes type definition](#structures-and-classes-type-definition) +* [Create and work with a webserver](#create-and-work-with-a-webserver) +* [The resource object](#the-resource-object) +* [Registering resources](#registering-resources) +* [Parsing requests](#parsing-requests) +* [Building responses to requests](#building-responses-to-requests) +* [IP Blacklisting and Whitelisting](#ip-blacklisting-and-whitelisting) +* [Authentication](#authentication) +* [HTTP Utils](#http-utils) +* [Other Examples](#other-examples) + +#### Community +* [Code of Conduct (on a separate page)](https://github.com/etr/libhttpserver/blob/master/CODE_OF_CONDUCT.md) +* [Contributing (on a separate page)](https://github.com/etr/libhttpserver/blob/master/CODE_OF_CONDUCT.md) + +#### Appendices +* [Copying statement](#copying) +* [GNU-LGPL](#GNU-lesser-general-public-license): The GNU Lesser General Public License says how you can copy and share almost all of libhttpserver. +* [GNU-FDL](#GNU-free-documentation-license): The GNU Free Documentation License says how you can copy and share the documentation of libhttpserver. + +## Introduction +libhttpserver is meant to constitute an easy system to build HTTP servers with REST fashion. +libhttpserver is based on [libmicrohttpd](https://www.gnu.org/software/libmicrohttpd/) and, like this, it is a daemon library (parts of this documentation are, in fact, matching those of the wrapped library). +The mission of this library is to support all possible HTTP features directly and with a simple semantic allowing then the user to concentrate only on his application and not on HTTP request handling details. + +The library is supposed to work transparently for the client Implementing the business logic and using the library itself to realize an interface. +If the user wants it must be able to change every behavior of the library itself through the registration of callbacks. + +libhttpserver is able to decode certain body format a and automatically format them in object oriented fashion. This is true for query arguments and for *POST* and *PUT* requests bodies if *application/x-www-form-urlencoded* or *multipart/form-data* header are passed. + +All functions are guaranteed to be completely reentrant and thread-safe (unless differently specified). +Additionally, clients can specify resource limits on the overall number of connections, number of connections per IP address and memory used per connection to avoid resource exhaustion. + +[Back to TOC](#table-of-contents) + +## Requirements +libhttpserver can be used without any dependencies aside for libmicrohttpd. + +The minimum versions required are: +* g++ >= 4.8.4 or clang-3.6 +* libmicrohttpd >= 0.9.52 +* [Optionally]: for TLS (HTTPS) support, you'll need [libgnutls](http://www.gnutls.org/). +* [Optionally]: to compile the code-reference, you'll need [doxygen](http://www.doxygen.nl/). Additionally, for MinGW on windows you will need: * libwinpthread (For MinGW-w64, if you use thread model posix then you have this) * libgnurx >= 2.5.1 -Compilation -=========== -libhttpserver uses the standard system where the usual build process -involves running +Furthermore, the testcases use [libcurl](http://curl.haxx.se/libcurl/) but you don't need it to compile the library. + +[Back to TOC](#table-of-contents) + +## Building +libhttpserver uses the standard system where the usual build process involves running > ./bootstrap > mkdir build > cd build -> ../configure +> \.\./configure > make -> make install -> make doxygen-doc # if you want to build the code reference +> make install # (optionally to install on the system) + +[Back to TOC](#table-of-contents) -Optional parameters to configure script ---------------------------------------- +### Optional parameters to configure script A complete list of parameters can be obtained running 'configure --help'. Here are listed the libhttpserver specific options (the canonical configure options are also supported). -* --enable-same-directory-build: enable to compile in the same directory. This is heavily discouraged. (def=no) -* --enable-debug: enable debug data generation (def=no) -* --enable-cpp11: enable c++11 std classes (def=no) -* --disable-doxygen-doc: don't generate any doxygen documentation -* --disable-doxygen-dot: don't generate graphics for doxygen documentation -* --disable-doxygen-man: don't generate doxygen manual pages -* --enable-doxygen-rtf: generate doxygen RTF documentation -* --enable-doxygen-xml: generate doxygen XML documentation -* --enable-doxygen-chm: generate doxygen compressed HTML help documentation -* --enable-doxygen-chi: generate doxygen seperate compressed HTML help index file -* --disable-doxygen-html: don't generate doxygen plain HTML documentation -* --enable-doxygen-ps: generate doxygen PostScript documentation -* --enable-doxygen-pdf: generate doxygen PDF documentation - -Constants -========= -W.I.P. - -Structures and classes type definition -====================================== -* http_resource (CPP class): Represents the resource associated with a specific http endpoint. -* http_request (CPP class): Represents the request received by the resource that process it. -* http_response (CPP class): Represents the response sent by the server once the resource finished its work. -* event_supplier (CPP class): Represents a class that supplies events to the webserver. It can be used to trigger the internal select of it. -* webserver (CPP class): Represents the daemon listening on a socket for HTTP traffic. - -GNU Lesser General Public License -================================= +* _\-\-enable-same-directory-build:_ enable to compile in the same directory. This is heavily discouraged. (def=no) +* _\-\-enable-debug:_ enable debug data generation. (def=no) +* _\-\-disable-doxygen-doc:_ don't generate any doxygen documentation. Doxygen is automatically invoked if present on the system. Automatically disabled otherwise. +* _\-\-enable-fastopen:_ enable use of TCP_FASTOPEN (def=yes) +* _\-\-enable-poll[=ARG]:_ enable poll support. Internal behavior of the `INTERNAL_SELECT` (yes, no, auto) [auto] +* _\-\-enable-epoll[=ARG]:_ enable epoll support. Internal behavior of the `INTERNAL_SELECT` (yes, no, auto) [auto] +* _\-\-enable-static:_ enable use static linking (def=yes) + +[Back to TOC](#table-of-contents) + +## Getting Started +The most basic example of creating a server and handling a requests for the path `/hello`: + + #include + + using namespace httpserver; + + class hello_world_resource : public http_resource { + public: + const std::shared_ptr render(const http_request&) { + return std::shared_ptr(new string_response("Hello, World!")); + } + }; + + int main(int argc, char** argv) { + webserver ws = create_webserver(8080); + + hello_world_resource hwr; + ws.register_resource("/hello", &hwr); + ws.start(true); + + return 0; + } + +To test the above example, you could run the following command from a terminal: + + curl -XGET -v http://localhost:8080/hello + +You can also check this example on [github](https://github.com/etr/libhttpserver/blob/master/examples/minimal_hello_world.cpp). + +[Back to TOC](#table-of-contents) + +## Structures and classes type definition +* _webserver:_ Represents the daemon listening on a socket for HTTP traffic. + * _create_webserver:_ Builder class to support the creation of a webserver. +* _http_resource:_ Represents the resource associated with a specific http endpoint. +* _http_request:_ Represents the request received by the resource that process it. +* _http_response:_ Represents the response sent by the server once the resource finished its work. + * _string_response:_ A simple string response. + * _file_response:_ A response getting content from a fail. + * _basic_auth_fail_response:_ A failure in basic authentication. + * _digest_auth_fail_response:_ A failure in digest authentication. + * _deferred_response:_ A response getting content from a callback. + +[Back to TOC](#table-of-contents) + +## Create and work with a webserver +As you can see from the example above, creating a webserver with standard configuration is quite simple: + + webserver ws = create_webserver(8080); + +The `create_webserver` class is a supporting _builder_ class that eases the building of a webserver through chained syntax. + +### Basic Startup Options + +In this section we will explore other basic options that you can use when configuring your server. More advanced options (custom callbacks, https support, etc...) will be discussed separately. + +* _.port(**int** port):_ The port at which the server will listen. This can also be passed to the consturctor of `create_webserver`. E.g. `create_webserver(8080)`. +* _.max_connections(**int** max_conns):_ Maximum number of concurrent connections to accept. The default is `FD_SETSIZE - 4` (the maximum number of file descriptors supported by `select` minus four for `stdin`, `stdout`, `stderr` and the server socket). In other words, the default is as large as possible. Note that if you set a low connection limit, you can easily get into trouble with browsers doing request pipelining. +For example, if your connection limit is “1”, a browser may open a first connection to access your “index.html” file, keep it open but use a second connection to retrieve CSS files, images and the like. In fact, modern browsers are typically by default configured for up to 15 parallel connections to a single server. If this happens, the library will refuse to even accept the second connection until the first connection is closed — which does not happen until timeout. As a result, the browser will fail to render the page and seem to hang. If you expect your server to operate close to the connection limit, you should first consider using a lower timeout value and also possibly add a “Connection: close” header to your response to ensure that request pipelining is not used and connections are closed immediately after the request has completed. +* _.content_size_limit(**size_t** size_limit):_ Sets the maximum size of the content that a client can send over in a single block. The default is `-1 = unlimited`. +* _.connection_timeout(**int** timeout):_ Determines after how many seconds of inactivity a connection should be timed out automatically. The default timeout is `180 seconds`. +* _.memory_limit(**int** memory_limit):_ Maximum memory size per connection (followed by a `size_t`). The default is 32 kB (32*1024 bytes). Values above 128k are unlikely to result in much benefit, as half of the memory will be typically used for IO, and TCP buffers are unlikely to support window sizes above 64k on most systems. +* _.per_IP_connection_limit(**int** connection_limit):_ Limit on the number of (concurrent) connections made to the server from the same IP address. Can be used to prevent one IP from taking over all of the allowed connections. If the same IP tries to establish more than the specified number of connections, they will be immediately rejected. The default is `0`, which means no limit on the number of connections from the same IP address. +* _.bind_socket(**int** socket_fd):_ Listen socket to use. Pass a listen socket for the daemon to use (systemd-style). If this option is used, the daemon will not open its own listen socket(s). The argument passed must be of type "int" and refer to an existing socket that has been bound to a port and is listening. +* _.max_thread_stack_size(**int** stack_size):_ Maximum stack size for threads created by the library. Not specifying this option or using a value of zero means using the system default (which is likely to differ based on your platform). Default is `0 (system default)`. +* _.use_ipv6() and .no_ipv6():_ Enable or disable the IPv6 protocol support (by default, libhttpserver will just support IPv4). If you specify this and the local platform does not support it, starting up the server will throw an exception. `off` by default. +* _.pedantic() and .no_pedantic():_ Enables pedantic checks about the protocol (as opposed to as tolerant as possible). Specifically, at the moment, this flag causes the library to reject HTTP 1.1 connections without a `Host` header. This is required by the standard, but of course in violation of the “be as liberal as possible in what you accept” norm. It is recommended to turn this **off** if you are testing clients against the library, and **on** in production. `off` by default. +* _.debug() and .no_debug():_ Enables debug messages from the library. `off` by default. +* _.regex_checking() and .no_regex_checking():_ Enables pattern matching for endpoints. Read more [here](#registering-resources). `on` by default. +* _.post_process() and .no_post_process():_ Enables/Disables the library to automatically parse the body of the http request as arguments if in querystring format. Read more [here](#parsing-requests). `on` by default. +* _.deferred()_ and _.no_deferred():_ Enables/Disables the ability for the server to suspend and resume connections. Simply put, it enables/disables the ability to use `deferred_response`. Read more [here](#building-responses-to-requests). `on` by default. +* _.single_resource() and .no_single_resource:_ Sets or unsets the server in single resource mode. This limits all endpoints to be served from a single resource. The resultant is that the webserver will process the request matching to the endpoint skipping any complex semantic. Because of this, the option is incompatible with `regex_checking` and requires the resource to be registered against an empty endpoint or the root endpoint (`"/"`). The resource will also have to be registered as family. (For more information on resource registration, read more [here](#registering-resources)). `off` by default. + +### Threading Models +* _.start_method(**const http::http_utils::start_method_T&** start_method):_ libhttpserver can operate with two different threading models that can be selected through this method. Default value is `INTERNAL_SELECT`. + * `http::http_utils::INTERNAL_SELECT`: In this mode, libhttpserver uses only a single thread to handle listening on the port and processing of requests. This mode is preferable if spawning a thread for each connection would be costly. If the HTTP server is able to quickly produce responses without much computational overhead for each connection, this mode can be a great choice. Note that libhttpserver will still start a single thread for itself -- this way, the main program can continue with its operations after calling the start method. Naturally, if the HTTP server needs to interact with shared state in the main application, synchronization will be required. If such synchronization in code providing a response results in blocking, all HTTP server operations on all connections will stall. This mode is a bad choice if response data cannot always be provided instantly. The reason is that the code generating responses should not block (since that would block all other connections) and on the other hand, if response data is not available immediately, libhttpserver will start to busy wait on it. If you need to scale along the number of concurrent connection and scale on multiple thread you can specify a value for `max_threads` (see below) thus enabling a thread pool - this is different from `THREAD_PER_CONNECTION` below where a new thread is spawned for each connection. + * `http::http_utils::THREAD_PER_CONNECTION`: In this mode, libhttpserver starts one thread to listen on the port for new connections and then spawns a new thread to handle each connection. This mode is great if the HTTP server has hardly any state that is shared between connections (no synchronization issues!) and may need to perform blocking operations (such as extensive IO or running of code) to handle an individual connection. +* _.max_threads(**int** max_threads):_ A thread pool can be combined with the `INTERNAL_SELECT` mode to benefit implementations that require scalability. As said before, by default this mode only uses a single thread. When combined with the thread pool option, it is possible to handle multiple connections with multiple threads. Any value greater than one for this option will activate the use of the thread pool. In contrast to the `THREAD_PER_CONNECTION` mode (where each thread handles one and only one connection), threads in the pool can handle a large number of concurrent connections. Using `INTERNAL_SELECT` in combination with a thread pool is typically the most scalable (but also hardest to debug) mode of operation for libhttpserver. Default value is `1`. This option is incompatible with `THREAD_PER_CONNECTION`. + +### Custom defaulted error messages +libhttpserver allows to override internal error retrieving functions to provide custom messages to the HTTP client. There are only 3 cases in which implementing logic (an http_resource) cannot be invoked: (1) a not found resource, where the library is not being able to match the URL requested by the client to any implementing http_resource object; (2) a not allowed method, when the HTTP client is requesting a method explicitly marked as not allowed (more info [here](#allowing-and-disallowing-methods-on-a-resource)) by the implementation; (3) an exception being thrown. +In all these 3 cases libhttpserver would provide a standard HTTP response to the client with the correct error code; respectively a `404`, a `405` and a `500`. The library allows its user to specify custom callbacks that will be called to replace the default behavior. +* _.not_found_resource(**const shared_ptr(*render_ptr)(const http_request&)** resource):_ Specifies a function to handle a request when no matching registered endpoint exist for the URL requested by the client. +* _.method_not_allowed_resource(**const shared_ptr(*render_ptr)(const http_request&)** resource):_ Specifies a function to handle a request that is asking for a method marked as not allowed on the matching http_resource. +* _.internal_error_resource(**const shared_ptr(*render_ptr)(const http_request&)** resource):_ Specifies a function to handle a request that is causing an uncaught exception during its execution. **REMEMBER:** is this callback is causing an exception itself, the standard default response from libhttpserver will be reported to the HTTP client. + +#### Example of custom errors: + #include + + using namespace httpserver; + + const std::shared_ptr not_found_custom(const http_request& req) { + return std::shared_ptr(new string_response("Not found custom", 404, "text/plain")); + } + + const std::shared_ptr not_allowed_custom(const http_request& req) { + return std::shared_ptr(new string_response("Not allowed custom", 405, "text/plain")); + } + + class hello_world_resource : public http_resource { + public: + const std::shared_ptr render(const http_request&) { + return std::shared_ptr(new string_response("Hello, World!")); + } + }; + + int main(int argc, char** argv) { + webserver ws = create_webserver(8080) + .not_found_resource(not_found_custom) + .method_not_allowed_resource(not_allowed_custom); + + hello_world_resource hwr; + hwr.disallow_all(); + hwr.set_allowing("GET", true); + ws.register_resource("/hello", &hwr); + ws.start(true); + + return 0; + } + +To test the above example, you can run the following command from a terminal: + + curl -XGET -v http://localhost:8080/hello + +If you try to run either of the two following commands, you'll see your custom errors: +* `curl -XGET -v http://localhost:8080/morning`: will return your custom `not found` error. +* `curl -XPOST -v http://localhost:8080/hello`: will return your custom `not allowed` error. + +You can also check this example on [github](https://github.com/etr/libhttpserver/blob/master/examples/custom_error.cpp). + +### Custom logging callbacks +* _.log_access(**void(*log_access_ptr)(const std::string&)** functor):_ Specifies a function used to log accesses (requests) to the server. +* _.log_error(**void(*log_error_ptr)(const std::string&)** functor):_ Specifies a function used to log errors generating from the server. + +#### Example of custom logging callback + #include + #include + + using namespace httpserver; + + void custom_access_log(const std::string& url) { + std::cout << "ACCESSING: " << url << std::endl; + } + + class hello_world_resource : public http_resource { + public: + const std::shared_ptr render(const http_request&) { + return std::shared_ptr(new string_response("Hello, World!")); + } + }; + + int main(int argc, char** argv) { + webserver ws = create_webserver(8080) + .log_access(custom_access_log); + + hello_world_resource hwr; + ws.register_resource("/hello", &hwr); + ws.start(true); + + return 0; + } + +To test the above example, you can run the following command from a terminal: + + curl -XGET -v http://localhost:8080/hello + +You'll notice how, on the terminal runing your server, the logs will now be printed in output for each request received. + +You can also check this example on [github](https://github.com/etr/libhttpserver/blob/master/examples/custom_access_log.cpp). + +### TLS/HTTPS +* _.use_ssl() and .no_ssl():_ Determines whether to run in HTTPS-mode or not. If you set this as on and libhttpserver was compiled without SSL support, the library will throw an exception at start of the server. `off` by default. +* _.cred_type(**const http::http_utils::cred_type_T&** cred_type):_ Daemon credentials type. Either certificate or anonymous. Acceptable values are: + * `NONE`: No credentials. + * `CERTIFICATE`: Certificate credential. + * `ANON`: Anonymous credential. + * `SRP`: SRP credential. + * `PSK`: PSK credential. + * `IA`: IA credential. +* _.https_mem_key(**const std::string&** filename):_ String representing the path to a file containing the private key to be used by the HTTPS daemon. This must be used in conjunction with `https_mem_cert`. +* _.https_mem_cert(**const std::string&** filename):_ String representing the path to a file containing the certificate to be used by the HTTPS daemon. This must be used in conjunction with `https_mem_key`. +* _.https_mem_trust(**const std::string&** filename):_ String representing the path to a file containing the CA certificate to be used by the HTTPS daemon to authenticate and trust clients certificates. The presence of this option activates the request of certificate to the client. The request to the client is marked optional, and it is the responsibility of the server to check the presence of the certificate if needed. Note that most browsers will only present a client certificate only if they have one matching the specified CA, not sending any certificate otherwise. +* _.https_priorities(**const std::string&** priority_string):_ SSL/TLS protocol version and ciphers. Must be followed by a string specifying the SSL/TLS protocol versions and ciphers that are acceptable for the application. The string is passed unchanged to gnutls_priority_init. If this option is not specified, `"NORMAL"` is used. + +#### Minimal example using HTTPS + #include + + using namespace httpserver; + + class hello_world_resource : public http_resource { + public: + const std::shared_ptr render(const http_request&) { + return std::shared_ptr(new string_response("Hello, World!")); + } + }; + + int main(int argc, char** argv) { + webserver ws = create_webserver(8080) + .use_ssl() + .https_mem_key("key.pem") + .https_mem_cert("cert.pem"); + + hello_world_resource hwr; + ws.register_resource("/hello", &hwr); + ws.start(true); + + return 0; + } + +To test the above example, you can run the following command from a terminal: + + curl -XGET -v -k 'https://localhost:8080/hello' + +You can also check this example on [github](https://github.com/etr/libhttpserver/blob/master/examples/minimal_https.cpp). + +### IP Blacklisting/Whitelisting +libhttpserver supports IP blacklisting and whitelisting as an internal feature. This section explains the startup options related with IP blacklisting/whitelisting. See the [specific section](#ip-blacklisting-and-whitelisting) to read more about the topic. +* _.ban_system() and .no_ban_system:_ Can be used to enable/disable the ban system. `on` by default. +* _.default_policy(**const http::http_utils::policy_T&** default_policy):_ Specifies what should be the default behavior when receiving a request. Possible values are `ACCEPT` and `REJECT`. Default is `ACCEPT`. + +### Authentication +* _.basic_auth() and .no_basic_auth:_ Can be used to enable/disable parsing of the basic authorization header sent by the client. `on` by default. +* _.digest_auth() and .no_digest_auth:_ Can be used to enable/disable parsing of the digested authentication data sent by the client. `on` by default. +* _.nonce_nc_size(**int** nonce_size):_ Size of an array of nonce and nonce counter map. This option represents the size (number of elements) of a map of a nonce and a nonce-counter. If this option is not specified, a default value of 4 will be used (which might be too small for servers handling many requests). +You should calculate the value of NC_SIZE based on the number of connections per second multiplied by your expected session duration plus a factor of about two for hash table collisions. For example, if you expect 100 digest-authenticated connections per second and the average user to stay on your site for 5 minutes, then you likely need a value of about 60000. On the other hand, if you can only expect only 10 digest-authenticated connections per second, tolerate browsers getting a fresh nonce for each request and expect a HTTP request latency of 250 ms, then a value of about 5 should be fine. +* _.digest_auth_random(**const std::string&** nonce_seed):_ Digest Authentication nonce’s seed. For security, you SHOULD provide a fresh random nonce when actually using Digest Authentication with libhttpserver in production. + +### Examples of chaining syntax to create a webserver + + webserver ws = create_webserver(8080) + .no_ssl() + .no_ipv6() + .no_debug() + .no_pedantic() + .no_basic_auth() + .no_digest_auth() + .no_comet() + .no_regex_checking() + .no_ban_system() + .no_post_process(); +## + webserver ws = create_webserver(8080) + .use_ssl() + .https_mem_key("key.pem") + .https_mem_cert("cert.pem"); + +### Starting and stopping a webserver +Once a webserver is created, you can manage its execution through the following methods on the `webserver` class: +* _**void** webserver::start(**bool** blocking):_ Allows to start a server. If the `blocking` flag is passed as `true`, it will block the execution of the current thread until a call to stop on the same webserver object is performed. +* _**void** webserver::stop():_ Allows to stop a server. It immediately stops it. +* _**bool** webserver::is_running():_ Checks if a server is running +* _**void** webserver::sweet_kill():_ Allows to stop a server. It doesn't guarantee an immediate halt to allow for thread termination and connection closure. + +[Back to TOC](#table-of-contents) + +## The Resource Object +The `http_resource` class represents a logical collection of HTTP methods that will be associated to a URL when registered on the webserver. The class is **designed for extension** and it is where most of your code should ideally live. When the webserver matches a request against a resource (see: [resource registration](#registering-resources)), the method correspondent to the one in the request (GET, POST, etc..) (see below) is called on the resource. + +Given this, the `http_resource` class contains the following extensible methods (also called `handlers` or `render methods`): +* _**const std::shared_ptr** http_resource::render_GET(**const http_request&** req):_ Invoked on an HTTP GET request. +* _**const std::shared_ptr** http_resource::render_POST(**const http_request&** req):_ Invoked on an HTTP POST request. +* _**const std::shared_ptr** http_resource::render_PUT(**const http_request&** req):_ Invoked on an HTTP PUT request. +* _**const std::shared_ptr** http_resource::render_HEAD(**const http_request&** req):_ Invoked on an HTTP HEAD request. +* _**const std::shared_ptr** http_resource::render_DELETE(**const http_request&** req):_ Invoked on an HTTP DELETE request. +* _**const std::shared_ptr** http_resource::render_TRACE(**const http_request&** req):_ Invoked on an HTTP TRACE request. +* _**const std::shared_ptr** http_resource::render_OPTIONS(**const http_request&** req):_ Invoked on an HTTP OPTIONS request. +* _**const std::shared_ptr** http_resource::render_CONNECT(**const http_request&** req):_ Invoked on an HTTP CONNECT request. +* _**const std::shared_ptr** http_resource::render(**const http_request&** req):_ Invoked as a backup method if the matching method is not implemented. It can be used whenever you want all the invocations on a URL to activate the same behavior regardless of the HTTP method requested. The default implementation of the `render` method returns an empty response with a `404`. + +#### Example of implementation of render methods + #include + + using namespace httpserver; + + class hello_world_resource : public http_resource { + public: + const std::shared_ptr render_GET(const http_request&) { + return std::shared_ptr(new string_response("GET: Hello, World!")); + } + + const std::shared_ptr render(const http_request&) { + return std::shared_ptr(new string_response("OTHER: Hello, World!")); + } + }; + + int main(int argc, char** argv) { + webserver ws = create_webserver(8080); + + hello_world_resource hwr; + ws.register_resource("/hello", &hwr); + ws.start(true); + + return 0; + } + +To test the above example, you can run the following commands from a terminal: + * `curl -XGET -v http://localhost:8080/hello`: will return `GET: Hello, World!`. + * `curl -XPOST -v http://localhost:8080/hello`: will return `OTHER: Hello, World!`. You can try requesting other methods beside `POST` to verify how the same message will be returned. + +You can also check this example on [github](https://github.com/etr/libhttpserver/blob/master/examples/handlers.cpp). + +### Allowing and disallowing methods on a resource +By default, all methods an a resource are allowed, meaning that an HTTP request with that method will be invoked. It is possible to mark methods as `not allowed` on a resource. When a method not allowed is requested on a resource, the default `method_not_allowed` method is invoked - the default can be overriden as explain in the section [Custom defaulted error messages](custom-defaulted-error-messages). +The base `http_resource` class has a set of methods that can be used to allow and disallow HTTP methods. +* _**void** http_resource::set_allowing(**const std::string&** method, **bool** allowed):_ Used to allow or disallow a method. The `method` parameter is a string representing an HTTP method (GET, POST, PUT, etc...). +* _**void** http_resource::allow_all():_ Marks all HTTP methods as allowed. +* _**void** http_resource::disallow_all():_ Marks all HTTP methods as not allowed. + +#### Example of methods allowed/disallowed + #include + + using namespace httpserver; + + class hello_world_resource : public http_resource { + public: + const std::shared_ptr render(const http_request&) { + return std::shared_ptr(new string_response("Hello, World!")); + } + }; + + int main(int argc, char** argv) { + webserver ws = create_webserver(8080); + + hello_world_resource hwr; + hwr.disallow_all(); + hwr.set_allowing("GET", true); + ws.register_resource("/hello", &hwr); + ws.start(true); + + return 0; + } + +To test the above example, you can run the following command from a terminal: + + curl -XGET -v http://localhost:8080/hello + +If you try to run the following command, you'll see a `method_not_allowed` error: +* `curl -XPOST -v http://localhost:8080/hello`. + +You can also check this example on [github](https://github.com/etr/libhttpserver/blob/master/examples/allowing_disallowing_methods.cpp). + +[Back to TOC](#table-of-contents) + +## Registering resources +Once you have created your resource and extended its methods, you'll have to register the resource on the webserver. Registering a resource will associate it with an endpoint and allows the webserver to route it. +The `webserver` class offers a method to register a resource: +* _**bool** register_resource(**const std::string&** endpoint, **http_resource*** resource, **bool** family = `false`):_ Registers the `resource` to an `endpoint`. The endpoint is a string representing the path on your webserver from where you want your resource to be served from (e.g. `"/path/to/resource"`). The optional `family` parameter allows to register a resource as a "family" resource that will match any path nested into the one specified. For example, if family is set to `true` and endpoint is set to `"/path"`, the webserver will route to the resource not only the requests against `"/path"` but also everything in its nested path `"/path/on/the/previous/one"`. + +### Specifying endpoints +There are essentially four ways to specify an endpoint string: +* **A simple path (e.g. `"/path/to/resource"`).** In this case, the webserver will try to match exactly the value of the endpoint. +* **A regular exception.** In this case, the webserver will try to match the URL of the request with the regex passed. For example, if passing `"/path/as/decimal/[0-9]+`, requests on URLs like `"/path/as/decimal/5"` or `"/path/as/decimal/42"` will be matched; instead, URLs like `"/path/as/decimal/three"` will not. +* **A parametrized path. (e.g. `"/path/to/resource/with/{arg1}/{arg2}/in/url"`)**. In this case, the webserver will match the argument with any value passed. In addition to this, the arguments will be passed to the resource as part of the arguments (readable from the `http_request::get_arg` method - see [here](#parsing-requests)). For example, if passing `"/path/to/resource/with/{arg1}/{arg2}/in/url"` will match any request on URL with any value in place of `{arg1}` and `{arg2}`. +* **A parametrized path with custom parameters.** This is the same of a normal parametrized path, but allows to specify a regular expression for the argument (e.g. `"/path/to/resource/with/{arg1|[0-9]+}/{arg2|[a-z]+}/in/url"`. In this case, the webserver will match the arguments with any value passed that satisfies the regex. In addition to this, as above, the arguments will be passed to the resource as part of the arguments (readable from the `http_request::get_arg` method - see [here](#parsing-requests)). For example, if passing `"/path/to/resource/with/{arg1|[0-9]+}/{arg2|[a-z]+}/in/url"` will match requests on URLs like `"/path/to/resource/with/10/AA/in/url"` but not like `""/path/to/resource/with/BB/10/in/url""` +* Any of the above marked as `family`. Will match any request on URLs having path that is prefixed by the path passed. For example, if family is set to `true` and endpoint is set to `"/path"`, the webserver will route to the resource not only the requests against `"/path"` but also everything in its nested path `"/path/on/the/previous/one"`. + + #include + + using namespace httpserver; + + class hello_world_resource : public http_resource { + public: + const std::shared_ptr render(const http_request&) { + return std::shared_ptr(new string_response("Hello, World!")); + } + }; + + class handling_multiple_resource : public http_resource { + public: + const std::shared_ptr render(const http_request& req) { + return std::shared_ptr(new string_response("Your URL: " + req.get_path())); + } + }; + + class url_args_resource : public http_resource { + public: + const std::shared_ptr render(const http_request& req) { + return std::shared_ptr(new string_response("ARGS: " + req.get_arg("arg1") + " and " + req.get_arg("arg2"))); + } + }; + + int main(int argc, char** argv) { + webserver ws = create_webserver(8080); + + hello_world_resource hwr; + ws.register_resource("/hello", &hwr); + + handling_multiple_resource hmr; + ws.register_resource("/family", &hmr, true); + ws.register_resource("/with_regex_[0-9]+", &hmr); + + url_args_resource uar; + ws.register_resource("/url/with/{arg1}/and/{arg2}", &uar); + ws.register_resource("/url/with/parametric/args/{arg1|[0-9]+}/and/{arg2|[A-Z]+}", &uar); + + ws.start(true); + + return 0; + } + +To test the above example, you can run the following commands from a terminal: + +* `curl -XGET -v http://localhost:8080/hello`: will return the `Hello, World!` message. +* `curl -XGET -v http://localhost:8080/family`: will return the `Your URL: /family` message. +* `curl -XGET -v http://localhost:8080/family/with/suffix`: will return the `Your URL: /family/with/suffix` message. +* `curl -XGET -v http://localhost:8080/with_regex_10`: will return the `Your URL: /with_regex_10` message. +* `curl -XGET -v http://localhost:8080/url/with/AA/and/BB`: will return the `ARGS: AA and BB` message. You can change `AA` and `BB` with any value and observe how the URL is still matched and parameters are read. +* `curl -XGET -v http://localhost:8080/url/with/parametric/args/10/and/AA`: will return the `ARGS: 10 and AA` message. You can change `10` and `AA` with any value matching the regexes and observe how the URL is still matched and parameters are read. + +Conversely, you can observe how these URL will not be matched (al the following will give you a `not found` message): +* `curl -XGET -v http://localhost:8080/with_regex_A` +* `curl -XGET -v http://localhost:8080/url/with/parametric/args/AA/and/BB` + +You can also check this example on [github](https://github.com/etr/libhttpserver/blob/master/examples/url_registration.cpp). + +[Back to TOC](#table-of-contents) + +## Parsing requests +As seen in the documentation of [http_resource](#the-resource-object), every extensible method takes in input a `http_request` object. The webserver takes the responsibility to extract the data from the HTTP request on the network and does all the heavy lifting to build the instance of `http_request`. + +The `http_request` class has a set of methods you will have access to when implementing your handlers: +* _**const std::string&** get_path() **const**:_ Returns the path as requested from the HTTP client. +* _**const std::vector\&** get_path_pieces() **const**:_ Returns the components of the path requested by the HTTP client (each piece of the path split by `'/'`. +* _**const std::string&** get_path_piece(int index) **const**:_ Returns one piece of the path requested by the HTTP client. The piece is selected through the `index` parameter (0-indexed). +* _**const std::string&** get_method() **const**:_ Returns the method requested by the HTTP client. +* _**const std::string&** get_header(**const std::string&** key) **const**:_ Returns the header with name equal to `key` if present in the HTTP request. Returns an `empty string` otherwise. +* _**const std::string&** get_cookie(**const std::string&** key) **const**:_ Returns the cookie with name equal to `key` if present in the HTTP request. Returns an `empty string` otherwise. +* _**const std::string&** get_footer(**const std::string&** key) **const**:_ Returns the footer with name equal to `key` if present in the HTTP request (only for http 1.1 chunked encodings). Returns an `empty string` otherwise. +* _**const std::string&** get_arg(**const std::string&** key) **const**:_ Returns the argument with name equal to `key` if present in the HTTP request. Arguments can be (1) querystring parameters, (2) path argument (in case of parametric endpoint, (3) parameters parsed from the HTTP request body if the body is in `application/x-www-form-urlencoded` or `multipart/form-data` formats and the postprocessor is enabled in the webserver (enabled by default). +* _**const std::map&** get_headers() **const**:_ Returns a map containing all the headers present in the HTTP request. +* _**const std::map&** get_cookies() **const**:_ Returns a map containing all the cookies present in the HTTP request. +* _**const std::map&** get_footers() **const**:_ Returns a map containing all the footers present in the HTTP request (only for http 1.1 chunked encodings). +* _**const std::map&** get_args() **const**:_ Returns all the arguments present in the HTTP request. Arguments can be (1) querystring parameters, (2) path argument (in case of parametric endpoint, (3) parameters parsed from the HTTP request body if the body is in `application/x-www-form-urlencoded` or `multipart/form-data` formats and the postprocessor is enabled in the webserver (enabled by default). +* _**const std::string&** get_content() **const**:_ Returns the body of the HTTP request. +* _**bool** content_too_large() **const**:_ Returns `true` if the body length of the HTTP request sent by the client is longer than the max allowed on the server. +* _**const std::string&** get_querystring() **const**:_ Returns the `querystring` of the HTTP request. +* _**const std::string&** get_version() **const**:_ Returns the HTTP version of the client request. +* _**const std::string&** get_requestor() **const**:_ Returns the IP from which the client is sending the request. +* _**unsigned short** get_requestor_port() **const**:_ Returns the port from which the client is sending the request. +* _**const std::string&** get_user() **const**:_ Returns the `user` as self-identified through basic authentication. The content of the user header will be parsed only if basic authentication is enabled on the server (enabled by default). +* _**const std::string&** get_pass() **const**:_ Returns the `password` as self-identified through basic authentication. The content of the password header will be parsed only if basic authentication is enabled on the server (enabled by default). +* _**const std::string&** get_digested_user() **const**:_ Returns the `digested user` as self-identified through digest authentication. The content of the user header will be parsed only if digest authentication is enabled on the server (enabled by default). +* _**bool** check_digest_auth(**const std::string&** realm, **const std::string&** password, **int** nonce_timeout, **bool&** reload_nonce) **const**:_ Allows to check the validity of the authentication token sent through digest authentication (if the provided values in the WWW-Authenticate header are valid and sound according to RFC2716). Takes in input the `realm` of validity of the authentication, the `password` as known to the server to compare against, the `nonce_timeout` to indicate how long the nonce is valid and `reload_nonce` a boolean that will be set by the method to indicate a nonce being reloaded. The method returns `true` if the authentication is valid, `false` otherwise. + +#### Example of handler reading arguments from a request + #include + + using namespace httpserver; + + class hello_world_resource : public http_resource { + public: + const std::shared_ptr render(const http_request& req) { + return std::shared_ptr(new string_response("Hello: " + req.get_arg("name"))); + } + }; + + int main(int argc, char** argv) { + webserver ws = create_webserver(8080); + + hello_world_resource hwr; + ws.register_resource("/hello", &hwr); + ws.start(true); + + return 0; + } + +To test the above example, you can run the following command from a terminal: + + curl -XGET -v "http://localhost:8080/hello?name=John" + +You will receive the message `Hello: John` in reply. Given that the body post processing is enabled, you can also run `curl -d "name=John" -X POST http://localhost:8080/hello` to obtain the same result. + +You can also check this example on [github](https://github.com/etr/libhttpserver/blob/master/examples/hello_with_get_arg.cpp). + +[Back to TOC](#table-of-contents) + +## Building responses to requests +As seen in the documentation of [http_resource](#the-resource-object), every extensible method returns in output a `http_response` object. The webserver takes the responsibility to convert the `http_response` object you create into a response on the network. + +There are 5 types of response that you can create - we will describe them here through their constructors: +* _string_response(**const std::string&** content, **int** response_code = `200`, **const std::string&** content_type = `"text/plain"`):_ The most basic type of response. It uses the `content` string passed in construction as body of the HTTP response. The other two optional parameters are the `response_code` and the `content_type`. You can find constant definition for the various response codes within the [http_utils](https://github.com/etr/libhttpserver/blob/master/src/httpserver/http_utils.hpp) library file. +* _file_response(**const std::string&** filename, **int** response_code = `200`, **const std::string&** content_type = `"text/plain"`):_ Uses the `filename` passed in construction as pointer to a file on disk. The body of the HTTP response will be set using the content of the file. The other two optional parameters are the `response_code` and the `content_type`. You can find constant definition for the various response codes within the [http_utils](https://github.com/etr/libhttpserver/blob/master/src/httpserver/http_utils.hpp) library file. +* _basic_auth_fail_response(**const std::string&** content, **const std::string&** realm = `""`, **int** response_code = `200`, **const std::string&** content_type = `"text/plain"`):_ A response in return to a failure during basic authentication. It allows to specify a `content` string as a message to send back to the client. The `realm` parameter should contain your realm of authentication (if any). The other two optional parameters are the `response_code` and the `content_type`. You can find constant definition for the various response codes within the [http_utils](https://github.com/etr/libhttpserver/blob/master/src/httpserver/http_utils.hpp) library file. +* _digest_auth_fail_response(**const std::string&** content, **const std::string&** realm = `""`, **const std::string&** opaque = `""`, **bool** reload_nonce = `false`, **int** response_code = `200`, **const std::string&** content_type = `"text/plain"`):_ A response in return to a failure during digest authentication. It allows to specify a `content` string as a message to send back to the client. The `realm` parameter should contain your realm of authentication (if any). The `opaque` represents a value that gets passed to the client and expected to be passed again to the server as-is. This value can be a hexadecimal or base64 string. The `reload_nonce` parameter tells the server to reload the nonce (you should use the value returned by the `check_digest_auth` method on the `http_request`. The other two optional parameters are the `response_code` and the `content_type`. You can find constant definition for the various response codes within the [http_utils](https://github.com/etr/libhttpserver/blob/master/src/httpserver/http_utils.hpp) library file. +* _deferred_response(**ssize_t(*cycle_callback_ptr)(char*, size_t)** cycle_callback, **const std::string&** content = `""`, **int** response_code = `200`, **const std::string&** content_type = `"text/plain"`):_ A response that obtains additional content from a callback executed in a deferred way. It leaves the client in pending state (returning a `100 CONTINUE` message) and suspends the connection. Besides the callback, optionally, you can provide a `content` parameter that sets the initial message sent immediately to the client. The other two optional parameters are the `response_code` and the `content_type`. You can find constant definition for the various response codes within the [http_utils](https://github.com/etr/libhttpserver/blob/master/src/httpserver/http_utils.hpp) library file. To use `deferred_response` you need to have the `deferred` option active on your webserver (enabled by default). + * The `cycle_callback_ptr` has this shape: + _**ssize_t** cycle_callback(**char*** buf, **size_t** max_size)_. + You are supposed to implement a function in this shape and provide it to the `deferred_repsonse` method. The webserver will provide a `char*` to the function. It is responsibility of the function to allocate it and fill its content. The method is supposed to respect the `max_size` parameter passed in input. The function must return a `ssize_t` value representing the actual size you filled the `buf` with. Any value different from `-1` will keep the resume the connection, deliver the content and suspend it again (with a `100 CONTINUE`). If the method returns `-1`, the webserver will complete the communication with the client and close the connection. + +### Setting additional properties of the response +The `http_response` class offers an additional set of methods to "decorate" your responses. This set of methods is: +* _**void** with_header(**const std::string&** key, **const std::string&** value):_ Sets an HTTP header with name set to `key` and value set to `value`. +* _**void** with_footer(**const std::string&** key, **const std::string&** value):_ Sets an HTTP footer with name set to `key` and value set to `value`. +* _**void** with_cookie(**const std::string&** key, **const std::string&** value):_ Sets an HTTP cookie with name set to `key` and value set to `value` (only for http 1.1 chunked encodings). +* _**void** shoutCAST():_ Mark the response as a `shoutCAST` one. + +### Example of response setting headers + #include + + using namespace httpserver; + + class hello_world_resource : public http_resource { + public: + const std::shared_ptr render(const http_request&) { + std::shared_ptr response = std::shared_ptr(new string_response("Hello, World!")); + response->with_header("MyHeader", "MyValue"); + return response; + } + }; + + int main(int argc, char** argv) { + webserver ws = create_webserver(8080); + + hello_world_resource hwr; + ws.register_resource("/hello", &hwr); + ws.start(true); + + return 0; + } + +To test the above example, you could run the following command from a terminal: + + curl -XGET -v "http://localhost:8080/hello" + +You will receive the message custom header in reply. + +You can also check this example on [github](https://github.com/etr/libhttpserver/blob/master/examples/setting_headers.cpp). + +[Back to TOC](#table-of-contents) + +## IP Blacklisting and Whitelisting +libhttpserver provides natively a system to blacklist and whitelist IP addresses. To enable/disable the system, it is possible to use the `ban_system` and `no_ban_system` methods on the `create_webserver` class. In the same way, you can specify what you want to be your "default behavior" (allow by default or disallow by default) by using the `default_policy` method (see [here](#create-and-work-with-a-webserver)). +The system supports both IPV4 and IPV6 and manages them transparently. The only requirement is for ipv6 to be enabled on your server - you'll have to enable this by using the `use_ipv6` method on `create_webserver`. + +You can explicitly ban or allow an IP address using the following methods on the `webserver` class: +* _**void** ban_ip(**const std::string&** ip):_ Adds one IP (or a range of IPs) to the list of the banned ones. Takes in input a `string` that contains the IP (or range of IPs) to ban. To use when the `default_policy` is `ACCEPT`. +* _**void** allow_ip(**const std::string&** ip):_ Adds one IP (or a range of IPs) to the list of the allowed ones. Takes in input a `string` that contains the IP (or range of IPs) to allow. To use when the `default_policy` is `REJECT`. +* _**void** unban_ip(**const std::string&** ip):_ Removes one IP (or a range of IPs) from the list of the banned ones. Takes in input a `string` that contains the IP (or range of IPs) to remove from the list. To use when the `default_policy` is `REJECT`. +* _**void** disallow_ip(**const std::string&** ip):_ Removes one IP (or a range of IPs) from the list of the allowed ones. Takes in input a `string` that contains the IP (or range of IPs) to remove from the list. To use when the `default_policy` is `REJECT`. + +### IP String Format +The IP string format can represent both IPV4 and IPV6. Addresses will be normalized by the webserver to operate in the same sapce. Any valid IPV4 or IPV6 textual representation works. +It is also possible to specify ranges of IPs. To do so, omit the octect you want to express as a range and specify a `'*'` in its place. +Examples of valid IPs include: +* `"192.168.5.5"`: standard IPV4 +* `"192.168.*.*"`: range of IPV4 addresses. In the example, everything between `192.168.0.0` and `192.168.255.255`. +* `"2001:db8:8714:3a90::12"`: standard IPV6 - clustered empty ranges are fully supported. +* `"2001:db8:8714:3a90:*:*"`: range of IPV6 addresses. +* `"::ffff:192.0.2.128"`: IPV4 IPs nested into IPV6. +* `"::192.0.2.128"`: IPV4 IPs nested into IPV6 (without `'ffff'` prefix) +* `"::ffff:192.0.*.*"`: ranges of IPV4 IPs nested into IPV6. + +#### Example of IP Whitelisting/Blacklisting + #include + + using namespace httpserver; + + class hello_world_resource : public http_resource { + public: + const std::shared_ptr render(const http_request&) { + return std::shared_ptr(new string_response("Hello, World!")); + } + }; + + int main(int argc, char** argv) { + webserver ws = create_webserver(8080) + .default_policy(http::http_utils::REJECT); + + ws.allow_ip("127.0.0.1"); + + hello_world_resource hwr; + ws.register_resource("/hello", &hwr); + ws.start(true); + + return 0; + } + +To test the above example, you could run the following command from a terminal: + + curl -XGET -v "http://localhost:8080/hello" + +You can also check this example on [github](https://github.com/etr/libhttpserver/blob/master/examples/minimal_ip_ban.cpp). + +[Back to TOC](#table-of-contents) + +## Authentication +libhttpserver support three types of client authentication. + +Basic authentication uses a simple authentication method based on BASE64 algorithm. Username and password are exchanged in clear between the client and the server, so this method must only be used for non-sensitive content or when the session is protected with https. When using basic authentication libhttpserver will have access to the clear password, possibly allowing to create a chained authentication toward an external authentication server. You can enable/disable support for Basic authentication through the `basic_auth` and `no_basic_auth` methods of the `create_webserver` class. + +Digest authentication uses a one-way authentication method based on MD5 hash algorithm. Only the hash will transit over the network, hence protecting the user password. The nonce will prevent replay attacks. This method is appropriate for general use, especially when https is not used to encrypt the session. You can enable/disable support for Digest authentication through the `digest_auth` and `no_digest_auth` methods of the `create_webserver` class. + +Client certificate authentication uses a X.509 certificate from the client. This is the strongest authentication mechanism but it requires the use of HTTPS. Client certificate authentication can be used simultaneously with Basic or Digest Authentication in order to provide a two levels authentication (like for instance separate machine and user authentication). You can enable/disable support for Certificate authentication through the `use_ssl` and `no_ssl` methods of the `create_webserver` class. + +### Using Basic Authentication + #include + + using namespace httpserver; + + class user_pass_resource : public httpserver::http_resource { + public: + const std::shared_ptr render_GET(const http_request& req) { + if (req.get_user() != "myuser" || req.get_pass() != "mypass") { + return std::shared_ptr(new basic_auth_fail_response("FAIL", "test@example.com")); + } + return std::shared_ptr(new string_response(req.get_user() + " " + req.get_pass(), 200, "text/plain")); + } + }; + + int main(int argc, char** argv) { + webserver ws = create_webserver(8080); + + user_pass_resource hwr; + ws.register_resource("/hello", &hwr); + ws.start(true); + + return 0; + } + +To test the above example, you can run the following command from a terminal: + + curl -XGET -v -u myuser:mypass "http://localhost:8080/hello" + +You will receive back the user and password you passed in input. Try to pass the wrong credentials to see the failure. + +You can also check this example on [github](https://github.com/etr/libhttpserver/blob/master/examples/basic_authentication.cpp). + +### Using Digest Authentication + #include + + #define MY_OPAQUE "11733b200778ce33060f31c9af70a870ba96ddd4" + + using namespace httpserver; + + class digest_resource : public httpserver::http_resource { + public: + const std::shared_ptr render_GET(const http_request& req) { + if (req.get_digested_user() == "") { + return std::shared_ptr(new digest_auth_fail_response("FAIL", "test@example.com", MY_OPAQUE, true)); + } + else { + bool reload_nonce = false; + if(!req.check_digest_auth("test@example.com", "mypass", 300, reload_nonce)) { + return std::shared_ptr(new digest_auth_fail_response("FAIL", "test@example.com", MY_OPAQUE, reload_nonce)); + } + } + return std::shared_ptr(new string_response("SUCCESS", 200, "text/plain")); + } + }; + + int main(int argc, char** argv) { + webserver ws = create_webserver(8080); + + digest_resource hwr; + ws.register_resource("/hello", &hwr); + ws.start(true); + + return 0; + } + +To test the above example, you can run the following command from a terminal: + + curl -XGET -v --digest --user myuser:mypass localhost:8080/hello + +You will receive a `SUCCESS` in response (observe the response message from the server in detail and you'll see the full interaction). Try to pass the wrong credentials or send a request without `digest` active to see the failure. + +You can also check this example on [github](https://github.com/etr/libhttpserver/blob/master/examples/digest_authentication.cpp). + +[Back to TOC](#table-of-contents) + +## HTTP Utils +libhttpserver provides a set of constants to help you develop your HTTP server. It would be redudant to list them here; so, please, consult the list directly [here](https://github.com/etr/libhttpserver/blob/master/src/httpserver/http_utils.hpp). + +[Back to TOC](#table-of-contents) + +## Other Examples + +#### Example of returning a response from a file + #include + + using namespace httpserver; + + class file_response_resource : public http_resource { + public: + const std::shared_ptr render_GET(const http_request& req) { + return std::shared_ptr(new file_response("test_content", 200, "text/plain")); + } + }; + + int main(int argc, char** argv) { + webserver ws = create_webserver(8080); + + file_response_resource hwr; + ws.register_resource("/hello", &hwr); + ws.start(true); + + return 0; + } + +To test the above example, you can run the following command from a terminal: + + curl -XGET -v localhost:8080/hello + +You can also check this example on [github](https://github.com/etr/libhttpserver/blob/master/examples/minimal_file_response.cpp). + +#### Example of a deferred response through callback + #include + + using namespace httpserver; + + static int counter = 0; + + ssize_t test_callback (char* buf, size_t max) { + if (counter == 2) { + return -1; + } else { + memset(buf, 0, max); + strcat(buf, " test "); + counter++; + return std::string(buf).size(); + } + } + + class deferred_resource : public http_resource { + public: + const std::shared_ptr render_GET(const http_request& req) { + return std::shared_ptr(new deferred_response(test_callback, "cycle callback response")); + } + }; + + int main(int argc, char** argv) { + webserver ws = create_webserver(8080); + + deferred_resource hwr; + ws.register_resource("/hello", &hwr); + ws.start(true); + + return 0; + } + +To test the above example, you can run the following command from a terminal: + + curl -XGET -v localhost:8080/hello + +You can also check this example on [github](https://github.com/etr/libhttpserver/blob/master/examples/minimal_deferred.cpp). + +[Back to TOC](#table-of-contents) + +## Copying +This manual is for libhttpserver, C++ library for creating an embedded Rest HTTP server (and more). + +> Permission is granted to copy, distribute and/or modify this document +> under the terms of the GNU Free Documentation License, Version 1.3 +> or any later version published by the Free Software Foundation; +> with no Invariant Sections, no Front-Cover Texts, and no Back-Cover +> Texts. A copy of the license is included in the section entitled GNU +> Free Documentation License. + +[Back to TOC](#table-of-contents) + +## GNU Lesser General Public License Version 2.1, February 1999 Copyright © 1991, 1999 Free Software Foundation, Inc. @@ -682,8 +1371,9 @@ necessary. Here is a sample; alter the names: That's all there is to it! -GNU Free Documentation License -============================== +[Back to TOC](#table-of-contents) + +## GNU Free Documentation License Version 1.3, 3 November 2008 @@ -1133,3 +1823,59 @@ If your document contains nontrivial examples of program code, we recommend releasing these examples in parallel under your choice of free software license, such as the GNU General Public License, to permit their use in free software. + +[Back to TOC](#table-of-contents) + +## Thanks + +This library has been originally developed under the zencoders flags and this community has always supported me all along this work so I am happy to put the logo on this readme. + + When you see this tree, know that you've came across ZenCoders.org + + with open('ZenCoders. + `num` in numbers synchronized + datetime d glob. sys.argv[2] . + def myclass `..` @@oscla org. . class { + displ hooks( public static void ma functor: + $myclass->method( impport sys, os.pipe ` @param name` + fcl if(system(cmd) myc. /de ` $card( array("a" srand + format lists: ++: conc ++ "my an WHERE for( == myi + `sys: myvalue(myvalue) sys.t Console.W try{ rais using + connec SELECT * FROM table mycnf acco desc and or selector::clas at + openldap string sys. print "zenc der " { 'a': `ls -l` > appe &firs + import Tkinter paste( $obh &a or it myval bro roll: :: [] require a + case `` super. +y expr say " %rooms 1 --account fb- yy + proc meth Animate => send(D, open) putd EndIf 10 whi myc` cont + and main (--) import loop $$ or end onload UNION WITH tab timer 150 *2 + end. begin True GtkLabel *label doto partition te let auto i<- (i + d ); + .mushup ``/. ^/zenc/ myclass->her flv op <> element >> 71 or + QFileDi : and .. with myc toA channel::bo myc isEmpty a not bodt; + class T public pol str mycalc d pt &&a *i fc add ^ac + ::ZenCoders::core::namespac boost::function st f = std: ;; int assert + cout << endl public genera #include "b ost ::ac myna const cast mys + ac size_t return ran int (*getNextValue)(void) ff double sa_family_t famil + pu a do puts(" ac int main(int argc, char* "%5d struct nam + cs float for typedef enum puts getchar() + if( else #define fp FILE* f char* s + i++ strcat( %s int + 31] total+= do + }do while(1) sle + getc strcpy( a for + prin scanf(%d, & get + int void myfunc(int pa retu + BEQ BNEQZ R1 10 ANDI R1 R2 SYS + XOR SYSCALL 5 SLTIU MFLO 15 SW JAL + BNE BLTZAL R1 1 LUI 001 NOOP MULTU SLLV + MOV R1 ADD R1 R2 JUMP 10 1001 BEQ R1 R2 1 ANDI + 1101 1010001100 111 001 01 1010 101100 1001 100 + 110110 100 0 01 101 01100 100 100 1000100011 + 11101001001 00 11 100 11 10100010 + 000101001001 10 1001 101000101 + 010010010010110101001010 + +For further information: +visit our website www.zencoders.org + +**Author:** Sebastiano Merlino + +[Back to TOC](#table-of-contents) From f97ea124f69045245d2748a8e471dac23fca3948 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 13 Jan 2019 19:13:42 -0800 Subject: [PATCH 411/623] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f974588d..bacb5ba1 100644 --- a/README.md +++ b/README.md @@ -337,7 +337,7 @@ libhttpserver supports IP blacklisting and whitelisting as an internal feature. * _.ban_system() and .no_ban_system:_ Can be used to enable/disable the ban system. `on` by default. * _.default_policy(**const http::http_utils::policy_T&** default_policy):_ Specifies what should be the default behavior when receiving a request. Possible values are `ACCEPT` and `REJECT`. Default is `ACCEPT`. -### Authentication +### Authentication Parameters * _.basic_auth() and .no_basic_auth:_ Can be used to enable/disable parsing of the basic authorization header sent by the client. `on` by default. * _.digest_auth() and .no_digest_auth:_ Can be used to enable/disable parsing of the digested authentication data sent by the client. `on` by default. * _.nonce_nc_size(**int** nonce_size):_ Size of an array of nonce and nonce counter map. This option represents the size (number of elements) of a map of a nonce and a nonce-counter. If this option is not specified, a default value of 4 will be used (which might be too small for servers handling many requests). From 012d014a7c52ef91a35c521d5d4f83ee11de5068 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Tue, 15 Jan 2019 07:19:53 +0000 Subject: [PATCH 412/623] Making examples building optional --- Makefile.am | 10 ++++++++-- configure.ac | 7 +++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/Makefile.am b/Makefile.am index 4deb3bf8..14604f4f 100644 --- a/Makefile.am +++ b/Makefile.am @@ -24,8 +24,14 @@ LIBTOOL_DEPS = @LIBTOOL_DEPS@ AUTOMAKE_OPTIONS = foreign 1.4 ACLOCAL_AMFLAGS = -I m4 -SUBDIRS = src test examples -DIST_SUBDIRS = src test examples +SUBDIRS = src test +DIST_SUBDIRS = src test + +if BUILD_EXAMPLES +SUBDIRS += examples +DIST_SUBDIRS += examples +endif + EXTRA_DIST = libhttpserver.pc.in $(DX_CONFIG) MOSTLYCLEANFILES = $(DX_CLEANFILES) *.gcda *.gcno *.gcov diff --git a/configure.ac b/configure.ac index 2d859e7f..03e5634f 100644 --- a/configure.ac +++ b/configure.ac @@ -286,6 +286,12 @@ if test x"$coverit" = x"yes"; then esac fi +AC_ARG_ENABLE([[examples]], + [AS_HELP_STRING([[--disable-examples]], [do not build any examples])], , + [enable_examples=yes]) +test "x$enable_examples" = "xno" || enable_examples=yes +AM_CONDITIONAL([BUILD_EXAMPLES], [test "x$enable_examples" = "xyes"]) + if test "$CROSS_COMPILE" == "1"; then if test "$ARM_ARCH_DIR" == "aarch64-linux-gnu"; then AM_CXXFLAGS="$AM_CXXFLAGS --prefix=${ARM_LD_PATH}/" @@ -355,4 +361,5 @@ AC_MSG_NOTICE([Configuration Summary: poll support : ${enable_poll=no} epoll support : ${enable_epoll=no} Static : ${static} + Build examples : ${enable_examples} ]) From 34f8219854b1282973375fee56eb3f1cb62b8862 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sat, 19 Jan 2019 23:34:21 +0000 Subject: [PATCH 413/623] Add valgrind suppressions. gnutls_session_get_data has a leak fixed in later versions (out-of-scope for the library). --- configure.ac | 1 + test/Makefile.am | 4 ++-- test/libhttpserver.supp | 5 +++++ 3 files changed, 8 insertions(+), 2 deletions(-) create mode 100644 test/libhttpserver.supp diff --git a/configure.ac b/configure.ac index 03e5634f..987bb9f0 100644 --- a/configure.ac +++ b/configure.ac @@ -338,6 +338,7 @@ AC_CONFIG_LINKS([test/test_content:test/test_content]) AC_CONFIG_LINKS([test/cert.pem:test/cert.pem]) AC_CONFIG_LINKS([test/key.pem:test/key.pem]) AC_CONFIG_LINKS([test/test_root_ca.pem:test/test_root_ca.pem]) +AC_CONFIG_LINKS([test/libhttpserver.supp:test/libhttpserver.supp]) AC_CONFIG_LINKS([examples/cert.pem:examples/cert.pem]) AC_CONFIG_LINKS([examples/key.pem:examples/key.pem]) AC_CONFIG_LINKS([examples/test_content:examples/test_content]) diff --git a/test/Makefile.am b/test/Makefile.am index 253391bd..a4ff3fce 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -45,5 +45,5 @@ endif TESTS = $(check_PROGRAMS) @VALGRIND_CHECK_RULES@ -#VALGRIND_SUPPRESSIONS_FILES = libhttpserver.supp -#EXTRA_DIST = libhttpserver.supp +VALGRIND_SUPPRESSIONS_FILES = libhttpserver.supp +EXTRA_DIST = libhttpserver.supp diff --git a/test/libhttpserver.supp b/test/libhttpserver.supp new file mode 100644 index 00000000..6da3d3c8 --- /dev/null +++ b/test/libhttpserver.supp @@ -0,0 +1,5 @@ +{ + gnutls_session_get_data + Memcheck:Cond + fun:gnutls_session_get_data +} From 270025e61eed70a450c1124e3458622e53a1869f Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 20 Jan 2019 20:45:10 +0000 Subject: [PATCH 414/623] Added benchmark tests using ab to travis --- .travis.yml | 24 ++++++++++++ examples/Makefile.am | 4 +- examples/benchmark.cpp | 68 ---------------------------------- examples/benchmark_select.cpp | 40 ++++++++++++++++++++ examples/benchmark_threads.cpp | 39 +++++++++++++++++++ 5 files changed, 106 insertions(+), 69 deletions(-) delete mode 100755 examples/benchmark.cpp create mode 100755 examples/benchmark_select.cpp create mode 100755 examples/benchmark_threads.cpp diff --git a/.travis.yml b/.travis.yml index 53aa4f06..bd56632e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -92,6 +92,10 @@ script: - ls -l /usr/local/lib/ - ls -l /usr/lib/ - if [ "$TRAVIS_OS_NAME" = "linux" ]; then cd ../src/; cppcheck --error-exitcode=1 .; cd ../build; fi + - if [ "$PERFORMANCE" = "select" ]; then cd examples; ./benchmark_select 8080 $(nproc) & ; fi; + - if [ "$PERFORMANCE" = "select" ]; then ab -n 10000000 -c 1000 localhost:8080/plaintext; fi; + - if [ "$PERFORMANCE" = "thread" ]; then cd examples; ./benchmark_threads 8080 & ; fi; + - if [ "$PERFORMANCE" = "thread" ]; then ab -n 10000000 -c 1000 localhost:8080/plaintext; fi; after_success: - if [ "$DEBUG" = "debug" ] && [ "$COVERAGE" = "coverage" ] && [ "$TRAVIS_OS_NAME" = "linux" ]; then bash <(curl -s https://codecov.io/bash); fi matrix: @@ -193,6 +197,26 @@ matrix: - valgrind-dbg env: - MATRIX_EVAL="CC=gcc-7 && CXX=g++-7 && VALGRIND=valgrind" + - os: linux + addons: + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - g++-7 + - apache2-utils + env: + - MATRIX_EVAL="CC=gcc-7 && CXX=g++-7 && PERFORMANCE=select" + - os: linux + addons: + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - g++-7 + - apache2-utils + env: + - MATRIX_EVAL="CC=gcc-7 && CXX=g++-7 && PERFORMANCE=threads" #- os: osx # osx_image: xcode8 # env: diff --git a/examples/Makefile.am b/examples/Makefile.am index d957e3c8..ee879c70 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -19,7 +19,7 @@ LDADD = $(top_builddir)/src/libhttpserver.la AM_CPPFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/src/httpserver/ METASOURCES = AUTO -noinst_PROGRAMS = hello_world service minimal_hello_world custom_error allowing_disallowing_methods handlers hello_with_get_arg setting_headers custom_access_log basic_authentication digest_authentication minimal_https minimal_file_response minimal_deferred url_registration minimal_ip_ban +noinst_PROGRAMS = hello_world service minimal_hello_world custom_error allowing_disallowing_methods handlers hello_with_get_arg setting_headers custom_access_log basic_authentication digest_authentication minimal_https minimal_file_response minimal_deferred url_registration minimal_ip_ban benchmark_select benchmark_threads hello_world_SOURCES = hello_world.cpp service_SOURCES = service.cpp @@ -37,3 +37,5 @@ minimal_file_response_SOURCES = minimal_file_response.cpp minimal_deferred_SOURCES = minimal_deferred.cpp url_registration_SOURCES = url_registration.cpp minimal_ip_ban_SOURCES = minimal_ip_ban.cpp +benchmark_select_SOURCES = benchmark_select.cpp +benchmark_threads_SOURCES = benchmark_threads.cpp diff --git a/examples/benchmark.cpp b/examples/benchmark.cpp deleted file mode 100755 index 94e4bc26..00000000 --- a/examples/benchmark.cpp +++ /dev/null @@ -1,68 +0,0 @@ -/* - This file is part of libhttpserver - Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 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 - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 - USA -*/ - -#include -#include - -#if defined(CPU_COUNT) && (CPU_COUNT+0) < 2 -#undef CPU_COUNT -#endif -#if !defined(CPU_COUNT) -#define CPU_COUNT 2 -#endif - -#define NUMBER_OF_THREADS CPU_COUNT - -#define PAGE "libmicrohttpd demolibhttpserver demo" - -using namespace httpserver; - -class hello_world_resource : public http_resource { - public: - const http_response render(const http_request&); -}; - -//using the render method you are able to catch each type of request you receive -const http_response hello_world_resource::render(const http_request& req) -{ - //it is possible to send a response initializing an http_string_response - //that reads the content to send in response from a string. - return http_response_builder(PAGE, 200).string_response(); -} - -int main() -{ - //it is possible to create a webserver passing a great number of parameters. - //In this case we are just passing the port and the number of thread running. - webserver ws = create_webserver(8080).start_method(http::http_utils::INTERNAL_SELECT).max_threads(NUMBER_OF_THREADS); - - hello_world_resource hwr; - //this way we are registering the hello_world_resource to answer for the endpoint - //"/hello". The requested method is called (if the request is a GET we call the render_GET - //method. In case that the specific render method is not implemented, the generic "render" - //method is called. - ws.register_resource("/", &hwr, true); - - //This way we are putting the created webserver in listen. We pass true in order to have - //a blocking call; if we want the call to be non-blocking we can just pass false to the - //method. - ws.start(true); - return 0; -} diff --git a/examples/benchmark_select.cpp b/examples/benchmark_select.cpp new file mode 100755 index 00000000..079b5395 --- /dev/null +++ b/examples/benchmark_select.cpp @@ -0,0 +1,40 @@ +#include +#include +#include + +#define PATH "/plaintext" +#define BODY "Hello, World!" + +using namespace httpserver; + +class hello_world_resource : public http_resource { + public: + hello_world_resource(const std::shared_ptr& resp): + resp(resp) + { + } + + const std::shared_ptr render(const http_request&) { + return resp; + } + + private: + std::shared_ptr resp; +}; + +int main(int argc, char** argv) +{ + webserver ws = create_webserver(atoi(argv[1])) + .start_method(http::http_utils::INTERNAL_SELECT) + .max_threads(atoi(argv[2])); + + std::shared_ptr hello = std::shared_ptr(new string_response(BODY, 200)); + hello->with_header("Server", "libhttpserver"); + + hello_world_resource hwr(hello); + ws.register_resource(PATH, &hwr, false); + + ws.start(true); + + return 0; +} diff --git a/examples/benchmark_threads.cpp b/examples/benchmark_threads.cpp new file mode 100755 index 00000000..ba806c3d --- /dev/null +++ b/examples/benchmark_threads.cpp @@ -0,0 +1,39 @@ +#include +#include +#include + +#define PATH "/plaintext" +#define BODY "Hello, World!" + +using namespace httpserver; + +class hello_world_resource : public http_resource { + public: + hello_world_resource(const std::shared_ptr& resp): + resp(resp) + { + } + + const std::shared_ptr render(const http_request&) { + return resp; + } + + private: + std::shared_ptr resp; +}; + +int main(int argc, char** argv) +{ + webserver ws = create_webserver(atoi(argv[1])) + .start_method(http::http_utils::THREAD_PER_CONNECTION); + + std::shared_ptr hello = std::shared_ptr(new string_response(BODY, 200)); + hello->with_header("Server", "libhttpserver"); + + hello_world_resource hwr(hello); + ws.register_resource(PATH, &hwr, false); + + ws.start(true); + + return 0; +} From 2955b7d9ff3f5da231ae0de841765611509b766e Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 20 Jan 2019 20:50:19 +0000 Subject: [PATCH 415/623] Fix travis script --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index bd56632e..6f588087 100644 --- a/.travis.yml +++ b/.travis.yml @@ -92,9 +92,9 @@ script: - ls -l /usr/local/lib/ - ls -l /usr/lib/ - if [ "$TRAVIS_OS_NAME" = "linux" ]; then cd ../src/; cppcheck --error-exitcode=1 .; cd ../build; fi - - if [ "$PERFORMANCE" = "select" ]; then cd examples; ./benchmark_select 8080 $(nproc) & ; fi; + - if [ "$PERFORMANCE" = "select" ]; then cd examples; ./benchmark_select 8080 $(nproc) & fi; - if [ "$PERFORMANCE" = "select" ]; then ab -n 10000000 -c 1000 localhost:8080/plaintext; fi; - - if [ "$PERFORMANCE" = "thread" ]; then cd examples; ./benchmark_threads 8080 & ; fi; + - if [ "$PERFORMANCE" = "thread" ]; then cd examples; ./benchmark_threads 8080 & fi; - if [ "$PERFORMANCE" = "thread" ]; then ab -n 10000000 -c 1000 localhost:8080/plaintext; fi; after_success: - if [ "$DEBUG" = "debug" ] && [ "$COVERAGE" = "coverage" ] && [ "$TRAVIS_OS_NAME" = "linux" ]; then bash <(curl -s https://codecov.io/bash); fi From ef33867e12fa47beabf9e076a0f4b261cb5d6083 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 20 Jan 2019 21:25:05 +0000 Subject: [PATCH 416/623] Use travis multi-line --- .travis.yml | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 6f588087..c89dd868 100644 --- a/.travis.yml +++ b/.travis.yml @@ -92,10 +92,18 @@ script: - ls -l /usr/local/lib/ - ls -l /usr/lib/ - if [ "$TRAVIS_OS_NAME" = "linux" ]; then cd ../src/; cppcheck --error-exitcode=1 .; cd ../build; fi - - if [ "$PERFORMANCE" = "select" ]; then cd examples; ./benchmark_select 8080 $(nproc) & fi; + - | + if [ "$PERFORMANCE" = "select" ]; then + cd examples + ./benchmark_select 8080 $(nproc) & + fi - if [ "$PERFORMANCE" = "select" ]; then ab -n 10000000 -c 1000 localhost:8080/plaintext; fi; - - if [ "$PERFORMANCE" = "thread" ]; then cd examples; ./benchmark_threads 8080 & fi; - - if [ "$PERFORMANCE" = "thread" ]; then ab -n 10000000 -c 1000 localhost:8080/plaintext; fi; + - | + if [ "$PERFORMANCE" = "threads" ]; then + cd examples + ./benchmark_threads 8080 & + fi + - if [ "$PERFORMANCE" = "threads" ]; then ab -n 10000000 -c 1000 localhost:8080/plaintext; fi; after_success: - if [ "$DEBUG" = "debug" ] && [ "$COVERAGE" = "coverage" ] && [ "$TRAVIS_OS_NAME" = "linux" ]; then bash <(curl -s https://codecov.io/bash); fi matrix: From d49b890765d0d904a13f82e26b8684d74224a777 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 20 Jan 2019 21:47:12 +0000 Subject: [PATCH 417/623] Sleep ahead of test with ab --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index c89dd868..8d88a5a5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -96,14 +96,14 @@ script: if [ "$PERFORMANCE" = "select" ]; then cd examples ./benchmark_select 8080 $(nproc) & + sleep 5 && ab -n 10000000 -c 1000 localhost:8080/plaintext fi - - if [ "$PERFORMANCE" = "select" ]; then ab -n 10000000 -c 1000 localhost:8080/plaintext; fi; - | if [ "$PERFORMANCE" = "threads" ]; then cd examples ./benchmark_threads 8080 & + sleep 5 && ab -n 10000000 -c 1000 localhost:8080/plaintext fi - - if [ "$PERFORMANCE" = "threads" ]; then ab -n 10000000 -c 1000 localhost:8080/plaintext; fi; after_success: - if [ "$DEBUG" = "debug" ] && [ "$COVERAGE" = "coverage" ] && [ "$TRAVIS_OS_NAME" = "linux" ]; then bash <(curl -s https://codecov.io/bash); fi matrix: From 4a752cfe1ec40f83de7be92b4e22b586a58bf0eb Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 20 Jan 2019 22:11:28 +0000 Subject: [PATCH 418/623] Reducing concurrency (to cope with travis limitations with threads) --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 8d88a5a5..68f2d125 100644 --- a/.travis.yml +++ b/.travis.yml @@ -96,13 +96,13 @@ script: if [ "$PERFORMANCE" = "select" ]; then cd examples ./benchmark_select 8080 $(nproc) & - sleep 5 && ab -n 10000000 -c 1000 localhost:8080/plaintext + sleep 5 && ab -n 10000000 -c 100 localhost:8080/plaintext fi - | if [ "$PERFORMANCE" = "threads" ]; then cd examples ./benchmark_threads 8080 & - sleep 5 && ab -n 10000000 -c 1000 localhost:8080/plaintext + sleep 5 && ab -n 10000000 -c 100 localhost:8080/plaintext fi after_success: - if [ "$DEBUG" = "debug" ] && [ "$COVERAGE" = "coverage" ] && [ "$TRAVIS_OS_NAME" = "linux" ]; then bash <(curl -s https://codecov.io/bash); fi From 2f74cdf70df94cd62488f7d2222a1deca0dfdd0f Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 20 Jan 2019 22:15:21 +0000 Subject: [PATCH 419/623] Reduce number of total requests to speed-up builds --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 68f2d125..f4f66852 100644 --- a/.travis.yml +++ b/.travis.yml @@ -96,13 +96,13 @@ script: if [ "$PERFORMANCE" = "select" ]; then cd examples ./benchmark_select 8080 $(nproc) & - sleep 5 && ab -n 10000000 -c 100 localhost:8080/plaintext + sleep 5 && ab -n 1000000 -c 100 localhost:8080/plaintext fi - | if [ "$PERFORMANCE" = "threads" ]; then cd examples ./benchmark_threads 8080 & - sleep 5 && ab -n 10000000 -c 100 localhost:8080/plaintext + sleep 5 && ab -n 1000000 -c 100 localhost:8080/plaintext fi after_success: - if [ "$DEBUG" = "debug" ] && [ "$COVERAGE" = "coverage" ] && [ "$TRAVIS_OS_NAME" = "linux" ]; then bash <(curl -s https://codecov.io/bash); fi From daa221b3f0beca14b548313759bf24450c558ff5 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 20 Jan 2019 22:45:23 +0000 Subject: [PATCH 420/623] Re-upping the number of total runs (travis seems fine) --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index f4f66852..68f2d125 100644 --- a/.travis.yml +++ b/.travis.yml @@ -96,13 +96,13 @@ script: if [ "$PERFORMANCE" = "select" ]; then cd examples ./benchmark_select 8080 $(nproc) & - sleep 5 && ab -n 1000000 -c 100 localhost:8080/plaintext + sleep 5 && ab -n 10000000 -c 100 localhost:8080/plaintext fi - | if [ "$PERFORMANCE" = "threads" ]; then cd examples ./benchmark_threads 8080 & - sleep 5 && ab -n 1000000 -c 100 localhost:8080/plaintext + sleep 5 && ab -n 10000000 -c 100 localhost:8080/plaintext fi after_success: - if [ "$DEBUG" = "debug" ] && [ "$COVERAGE" = "coverage" ] && [ "$TRAVIS_OS_NAME" = "linux" ]; then bash <(curl -s https://codecov.io/bash); fi From 4282ca03f68db37bbd8b0d3ac13f808061bc31b8 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Mon, 21 Jan 2019 07:00:15 +0000 Subject: [PATCH 421/623] Change http request parsing to be lazy --- examples/allowing_disallowing_methods.cpp | 2 +- examples/basic_authentication.cpp | 2 +- examples/custom_access_log.cpp | 2 +- examples/custom_error.cpp | 6 +- examples/digest_authentication.cpp | 2 +- examples/handlers.cpp | 4 +- examples/hello_with_get_arg.cpp | 2 +- examples/hello_world.cpp | 4 +- examples/minimal_deferred.cpp | 2 +- examples/minimal_file_response.cpp | 2 +- examples/minimal_hello_world.cpp | 2 +- examples/minimal_https.cpp | 2 +- examples/minimal_ip_ban.cpp | 2 +- examples/service.cpp | 32 +-- examples/setting_headers.cpp | 2 +- examples/url_registration.cpp | 6 +- src/http_request.cpp | 318 +++++++++++++++++++++- src/http_resource.cpp | 2 +- src/http_utils.cpp | 12 + src/httpserver/create_webserver.hpp | 3 +- src/httpserver/details/modded_request.hpp | 2 +- src/httpserver/http_request.hpp | 194 ++++++------- src/httpserver/http_resource.hpp | 20 +- src/httpserver/http_utils.hpp | 5 + src/httpserver/webserver.hpp | 19 +- src/webserver.cpp | 181 +----------- test/integ/authentication.cpp | 4 +- test/integ/ban_system.cpp | 2 +- test/integ/basic.cpp | 44 +-- test/integ/deferred.cpp | 2 +- test/integ/threaded.cpp | 2 +- test/integ/ws_start_stop.cpp | 6 +- 32 files changed, 515 insertions(+), 375 deletions(-) diff --git a/examples/allowing_disallowing_methods.cpp b/examples/allowing_disallowing_methods.cpp index 69295af4..eb84773c 100644 --- a/examples/allowing_disallowing_methods.cpp +++ b/examples/allowing_disallowing_methods.cpp @@ -24,7 +24,7 @@ using namespace httpserver; class hello_world_resource : public http_resource { public: - const std::shared_ptr render(const http_request&) { + const std::shared_ptr render(http_request&) { return std::shared_ptr(new string_response("Hello, World!")); } }; diff --git a/examples/basic_authentication.cpp b/examples/basic_authentication.cpp index 84de823e..e7d28b82 100644 --- a/examples/basic_authentication.cpp +++ b/examples/basic_authentication.cpp @@ -25,7 +25,7 @@ using namespace httpserver; class user_pass_resource : public httpserver::http_resource { public: - const std::shared_ptr render_GET(const http_request& req) + const std::shared_ptr render_GET(http_request& req) { if (req.get_user() != "myuser" || req.get_pass() != "mypass") { diff --git a/examples/custom_access_log.cpp b/examples/custom_access_log.cpp index 7e5f1ef5..8158e329 100644 --- a/examples/custom_access_log.cpp +++ b/examples/custom_access_log.cpp @@ -29,7 +29,7 @@ void custom_access_log(const std::string& url) { class hello_world_resource : public http_resource { public: - const std::shared_ptr render(const http_request&) { + const std::shared_ptr render(http_request&) { return std::shared_ptr(new string_response("Hello, World!")); } }; diff --git a/examples/custom_error.cpp b/examples/custom_error.cpp index 034f334c..6271a180 100644 --- a/examples/custom_error.cpp +++ b/examples/custom_error.cpp @@ -22,19 +22,19 @@ using namespace httpserver; -const std::shared_ptr not_found_custom(const http_request& req) +const std::shared_ptr not_found_custom( http_request& req) { return std::shared_ptr(new string_response("Not found custom", 404, "text/plain")); } -const std::shared_ptr not_allowed_custom(const http_request& req) +const std::shared_ptr not_allowed_custom(http_request& req) { return std::shared_ptr(new string_response("Not allowed custom", 405, "text/plain")); } class hello_world_resource : public http_resource { public: - const std::shared_ptr render(const http_request&) { + const std::shared_ptr render(http_request&) { return std::shared_ptr(new string_response("Hello, World!")); } }; diff --git a/examples/digest_authentication.cpp b/examples/digest_authentication.cpp index 55f119a0..406e823b 100644 --- a/examples/digest_authentication.cpp +++ b/examples/digest_authentication.cpp @@ -26,7 +26,7 @@ using namespace httpserver; class digest_resource : public httpserver::http_resource { public: - const std::shared_ptr render_GET(const http_request& req) { + const std::shared_ptr render_GET(http_request& req) { if (req.get_digested_user() == "") { return std::shared_ptr(new digest_auth_fail_response("FAIL", "test@example.com", MY_OPAQUE, true)); } diff --git a/examples/handlers.cpp b/examples/handlers.cpp index 4ddad426..610e69fa 100644 --- a/examples/handlers.cpp +++ b/examples/handlers.cpp @@ -24,11 +24,11 @@ using namespace httpserver; class hello_world_resource : public http_resource { public: - const std::shared_ptr render_GET(const http_request&) { + const std::shared_ptr render_GET(http_request&) { return std::shared_ptr(new string_response("GET: Hello, World!")); } - const std::shared_ptr render(const http_request&) { + const std::shared_ptr render(http_request&) { return std::shared_ptr(new string_response("OTHER: Hello, World!")); } }; diff --git a/examples/hello_with_get_arg.cpp b/examples/hello_with_get_arg.cpp index 07ae90c1..67fbfb75 100644 --- a/examples/hello_with_get_arg.cpp +++ b/examples/hello_with_get_arg.cpp @@ -24,7 +24,7 @@ using namespace httpserver; class hello_world_resource : public http_resource { public: - const std::shared_ptr render(const http_request& req) { + const std::shared_ptr render(http_request& req) { return std::shared_ptr(new string_response("Hello: " + req.get_arg("name"))); } }; diff --git a/examples/hello_world.cpp b/examples/hello_world.cpp index f9675b32..c8e701cc 100755 --- a/examples/hello_world.cpp +++ b/examples/hello_world.cpp @@ -25,13 +25,13 @@ using namespace httpserver; class hello_world_resource : public http_resource { public: - const std::shared_ptr render(const http_request&); + const std::shared_ptr render(http_request&); void set_some_data(const std::string &s) {data = s;} std::string data; }; //using the render method you are able to catch each type of request you receive -const std::shared_ptr hello_world_resource::render(const http_request& req) +const std::shared_ptr hello_world_resource::render(http_request& req) { //it is possible to store data inside the resource object that can be altered //through the requests diff --git a/examples/minimal_deferred.cpp b/examples/minimal_deferred.cpp index a08599c2..dbe11865 100644 --- a/examples/minimal_deferred.cpp +++ b/examples/minimal_deferred.cpp @@ -38,7 +38,7 @@ ssize_t test_callback (char* buf, size_t max) { class deferred_resource : public http_resource { public: - const std::shared_ptr render_GET(const http_request& req) { + const std::shared_ptr render_GET(http_request& req) { return std::shared_ptr(new deferred_response(test_callback, "cycle callback response")); } }; diff --git a/examples/minimal_file_response.cpp b/examples/minimal_file_response.cpp index b82d2929..bf6a70ff 100644 --- a/examples/minimal_file_response.cpp +++ b/examples/minimal_file_response.cpp @@ -25,7 +25,7 @@ using namespace httpserver; class file_response_resource : public http_resource { public: - const std::shared_ptr render_GET(const http_request& req) + const std::shared_ptr render_GET(http_request& req) { return std::shared_ptr(new file_response("test_content", 200, "text/plain")); } diff --git a/examples/minimal_hello_world.cpp b/examples/minimal_hello_world.cpp index 76489a0e..fd124532 100644 --- a/examples/minimal_hello_world.cpp +++ b/examples/minimal_hello_world.cpp @@ -24,7 +24,7 @@ using namespace httpserver; class hello_world_resource : public http_resource { public: - const std::shared_ptr render(const http_request&) { + const std::shared_ptr render(http_request&) { return std::shared_ptr(new string_response("Hello, World!")); } }; diff --git a/examples/minimal_https.cpp b/examples/minimal_https.cpp index def0452a..640b4c38 100644 --- a/examples/minimal_https.cpp +++ b/examples/minimal_https.cpp @@ -24,7 +24,7 @@ using namespace httpserver; class hello_world_resource : public http_resource { public: - const std::shared_ptr render(const http_request&) { + const std::shared_ptr render(http_request&) { return std::shared_ptr(new string_response("Hello, World!")); } }; diff --git a/examples/minimal_ip_ban.cpp b/examples/minimal_ip_ban.cpp index ea0923e7..63f9952a 100644 --- a/examples/minimal_ip_ban.cpp +++ b/examples/minimal_ip_ban.cpp @@ -24,7 +24,7 @@ using namespace httpserver; class hello_world_resource : public http_resource { public: - const std::shared_ptr render(const http_request&) { + const std::shared_ptr render(http_request&) { return std::shared_ptr(new string_response("Hello, World!")); } }; diff --git a/examples/service.cpp b/examples/service.cpp index b2d365fc..74783127 100644 --- a/examples/service.cpp +++ b/examples/service.cpp @@ -33,14 +33,14 @@ class service_resource: public http_resource { ~service_resource(); - const std::shared_ptr render_GET(const http_request &req); - const std::shared_ptr render_PUT(const http_request &req); - const std::shared_ptr render_POST(const http_request &req); - const std::shared_ptr render(const http_request &req); - const std::shared_ptr render_HEAD(const http_request &req); - const std::shared_ptr render_OPTIONS(const http_request &req); - const std::shared_ptr render_CONNECT(const http_request &req); - const std::shared_ptr render_DELETE(const http_request &req); + const std::shared_ptr render_GET(http_request &req); + const std::shared_ptr render_PUT(http_request &req); + const std::shared_ptr render_POST(http_request &req); + const std::shared_ptr render(http_request &req); + const std::shared_ptr render_HEAD(http_request &req); + const std::shared_ptr render_OPTIONS(http_request &req); + const std::shared_ptr render_CONNECT(http_request &req); + const std::shared_ptr render_DELETE(http_request &req); private: @@ -54,7 +54,7 @@ service_resource::~service_resource() {} const std::shared_ptr -service_resource::render_GET(const http_request &req) +service_resource::render_GET(http_request &req) { std::cout << "service_resource::render_GET()" << std::endl; @@ -68,7 +68,7 @@ service_resource::render_GET(const http_request &req) const std::shared_ptr -service_resource::render_PUT(const http_request &req) +service_resource::render_PUT(http_request &req) { std::cout << "service_resource::render_PUT()" << std::endl; @@ -83,7 +83,7 @@ service_resource::render_PUT(const http_request &req) const std::shared_ptr -service_resource::render_POST(const http_request &req) +service_resource::render_POST(http_request &req) { std::cout << "service_resource::render_POST()" << std::endl; @@ -97,7 +97,7 @@ service_resource::render_POST(const http_request &req) } const std::shared_ptr -service_resource::render(const http_request &req) +service_resource::render(http_request &req) { std::cout << "service_resource::render()" << std::endl; @@ -112,7 +112,7 @@ service_resource::render(const http_request &req) const std::shared_ptr -service_resource::render_HEAD(const http_request &req) +service_resource::render_HEAD(http_request &req) { std::cout << "service_resource::render_HEAD()" << std::endl; @@ -126,7 +126,7 @@ service_resource::render_HEAD(const http_request &req) } const std::shared_ptr -service_resource::render_OPTIONS(const http_request &req) +service_resource::render_OPTIONS(http_request &req) { std::cout << "service_resource::render_OPTIONS()" << std::endl; @@ -140,7 +140,7 @@ service_resource::render_OPTIONS(const http_request &req) } const std::shared_ptr -service_resource::render_CONNECT(const http_request &req) +service_resource::render_CONNECT(http_request &req) { std::cout << "service_resource::render_CONNECT()" << std::endl; @@ -154,7 +154,7 @@ service_resource::render_CONNECT(const http_request &req) } const std::shared_ptr -service_resource::render_DELETE(const http_request &req) +service_resource::render_DELETE(http_request &req) { std::cout << "service_resource::render_DELETE()" << std::endl; diff --git a/examples/setting_headers.cpp b/examples/setting_headers.cpp index 0fb73c79..8d7e2323 100644 --- a/examples/setting_headers.cpp +++ b/examples/setting_headers.cpp @@ -24,7 +24,7 @@ using namespace httpserver; class hello_world_resource : public http_resource { public: - const std::shared_ptr render(const http_request&) { + const std::shared_ptr render(http_request&) { std::shared_ptr response = std::shared_ptr(new string_response("Hello, World!")); response->with_header("MyHeader", "MyValue"); return response; diff --git a/examples/url_registration.cpp b/examples/url_registration.cpp index 0f8da6ad..7cb0cf2f 100644 --- a/examples/url_registration.cpp +++ b/examples/url_registration.cpp @@ -24,21 +24,21 @@ using namespace httpserver; class hello_world_resource : public http_resource { public: - const std::shared_ptr render(const http_request&) { + const std::shared_ptr render(http_request&) { return std::shared_ptr(new string_response("Hello, World!")); } }; class handling_multiple_resource : public http_resource { public: - const std::shared_ptr render(const http_request& req) { + const std::shared_ptr render(http_request& req) { return std::shared_ptr(new string_response("Your URL: " + req.get_path())); } }; class url_args_resource : public http_resource { public: - const std::shared_ptr render(const http_request& req) { + const std::shared_ptr render(http_request& req) { return std::shared_ptr(new string_response("ARGS: " + req.get_arg("arg1") + " and " + req.get_arg("arg2"))); } }; diff --git a/src/http_request.cpp b/src/http_request.cpp index dc1127be..00bf84ab 100644 --- a/src/http_request.cpp +++ b/src/http_request.cpp @@ -41,8 +41,10 @@ bool http_request::check_digest_auth( const std::string& password, int nonce_timeout, bool& reload_nonce -) const +) { + digest_auth_parse(); + int val = MHD_digest_auth_check( underlying_connection, realm.c_str(), @@ -65,21 +67,317 @@ bool http_request::check_digest_auth( return true; } -std::ostream &operator<< (std::ostream &os, const http_request &r) + +const std::string& http_request::get_header(const std::string& key) +{ + check_or_fill_headers(); + + std::map::const_iterator it = this->headers.find(key); + if(it != this->headers.end()) + { + return it->second; + } + else + { + return EMPTY; + } +} + +const std::map& http_request::get_headers() +{ + check_or_fill_headers(); + + return this->headers; +} + +const std::string& http_request::get_footer(const std::string& key) +{ + check_or_fill_footers(); + + std::map::const_iterator it = this->footers.find(key); + if(it != this->footers.end()) + { + return it->second; + } + else + { + return EMPTY; + } +} + +const std::map& http_request::get_footers() +{ + check_or_fill_footers(); + + return this->footers; +} + +const std::string& http_request::get_cookie(const std::string& key) +{ + check_or_fill_cookies(); + + std::map::const_iterator it = this->cookies.find(key); + if(it != this->cookies.end()) + { + return it->second; + } + else + { + return EMPTY; + } +} + +const std::map& http_request::get_cookies() { - os << r.method << " Request [user:\"" << r.user << "\" pass:\"" << r.pass << "\"] path:\"" - << r.path << "\"" << std::endl; + check_or_fill_cookies(); - http::dump_header_map(os,"Headers",r.headers); - http::dump_header_map(os,"Footers",r.footers); - http::dump_header_map(os,"Cookies",r.cookies); - http::dump_arg_map(os,"Query Args",r.args); + return this->cookies; +} - os << " Version [ " << r.version << " ] Requestor [ " << r.requestor - << " ] Port [ " << r.requestor_port << " ]" << std::endl; +const std::string& http_request::get_arg(const std::string& key) +{ + check_or_fill_args(); + + std::map::const_iterator it = this->args.find(key); + if(it != this->args.end()) + { + return it->second; + } + else + { + return EMPTY; + } +} + +const std::map& http_request::get_args() +{ + check_or_fill_args(); + + return this->args; +} + +const std::string& http_request::get_querystring() +{ + check_or_fill_args(); + + return this->querystring; +} + +std::ostream &operator<< (std::ostream &os, http_request &r) +{ + os << r.get_method() << " Request [user:\"" << r.get_user() << "\" pass:\"" << r.get_pass() << "\"] path:\"" + << r.get_path() << "\"" << std::endl; + + http::dump_header_map(os,"Headers",r.get_headers()); + http::dump_header_map(os,"Footers",r.get_footers()); + http::dump_header_map(os,"Cookies",r.get_cookies()); + http::dump_arg_map(os,"Query Args",r.get_args()); + + os << " Version [ " << r.get_version() << " ] Requestor [ " << r.get_requestor() + << " ] Port [ " << r.get_requestor_port() << " ]" << std::endl; return os; } +void http_request::check_or_fill_headers() +{ + if (this->headers_loaded) return; + + MHD_get_connection_values ( + this->underlying_connection, + MHD_HEADER_KIND, + &build_request_header, + (void*) this + ); + + this->headers_loaded = true; +} + +void http_request::check_or_fill_cookies() +{ + if (this->cookies_loaded) return; + + MHD_get_connection_values ( + this->underlying_connection, + MHD_COOKIE_KIND, + &build_request_cookie, + (void*) this + ); + + this->cookies_loaded = true; +} + +void http_request::check_or_fill_footers() +{ + if (this->footers_loaded) return; + + MHD_get_connection_values ( + this->underlying_connection, + MHD_FOOTER_KIND, + &build_request_footer, + (void*) this + ); + + this->footers_loaded = true; +} + +void http_request::check_or_fill_args() +{ + if (this->args_loaded) return; + + MHD_get_connection_values ( + this->underlying_connection, + MHD_GET_ARGUMENT_KIND, + &build_request_args, + (void*) this + ); + + this->args_loaded = true; +} + +int http_request::build_request_header( + void *cls, + enum MHD_ValueKind kind, + const char *key, + const char *value +) +{ + http_request* dhr = static_cast(cls); + dhr->set_header(key, value); + return MHD_YES; +} + +int http_request::build_request_cookie ( + void *cls, + enum MHD_ValueKind kind, + const char *key, + const char *value +) +{ + http_request* dhr = static_cast(cls); + dhr->set_cookie(key, value); + return MHD_YES; +} + +int http_request::build_request_footer( + void *cls, + enum MHD_ValueKind kind, + const char *key, + const char *value +) +{ + http_request* dhr = static_cast(cls); + dhr->set_footer(key, value); + return MHD_YES; +} + +int http_request::build_request_args( + void *cls, + enum MHD_ValueKind kind, + const char *key, + const char *arg_value +) +{ + http_request* dhr = static_cast(cls); + std::string value = ((arg_value == NULL) ? "" : arg_value); + { + char buf[std::string(key).size() + value.size() + 3]; + if(dhr->querystring == "") + { + snprintf(buf, sizeof buf, "?%s=%s", key, value.c_str()); + dhr->querystring = buf; + } + else + { + snprintf(buf, sizeof buf, "&%s=%s", key, value.c_str()); + dhr->querystring += string(buf); + } + } + http::base_unescaper(value, dhr->unescaper); + dhr->set_arg(key, value); + return MHD_YES; +} + +const std::string& http_request::get_user() +{ + basic_auth_parse(); + return this->user; +} + +const std::string& http_request::get_pass() +{ + basic_auth_parse(); + return this->pass; +} + +void http_request::basic_auth_parse() +{ + if (this->basic_auth_loaded) return; + + char* username = 0x0; + char* password = 0x0; + username = MHD_basic_auth_get_username_password(underlying_connection, &password); + + if (username != 0x0) + { + this->user = username; + free(username); + } + if (password != 0x0) + { + this->pass = password; + free(password); + } + + this->basic_auth_loaded = true; +} + +const std::string& http_request::get_digested_user() +{ + digest_auth_parse(); + return this->digested_user; +} + +void http_request::digest_auth_parse() +{ + if (this->digest_auth_loaded) return; + + char* digested_user_c = 0x0; + digested_user_c = MHD_digest_auth_get_username(underlying_connection); + + if (digested_user_c != 0x0) + { + this->digested_user = digested_user_c; + free(digested_user_c); + } + + this->digest_auth_loaded = true; +} + +const std::string& http_request::get_requestor() +{ + parse_requestor_info(); + return this->requestor; +} + +unsigned short http_request::get_requestor_port() +{ + parse_requestor_info(); + return this->requestor_port; +} + +void http_request::parse_requestor_info() +{ + if (this->requestor_loaded) return; + + const MHD_ConnectionInfo * conninfo = MHD_get_connection_info( + underlying_connection, + MHD_CONNECTION_INFO_CLIENT_ADDRESS + ); + + std::string ip_str = http::get_ip_str(conninfo->client_addr); + this->set_requestor(ip_str); + this->set_requestor_port(http::get_port(conninfo->client_addr)); +} } diff --git a/src/http_resource.cpp b/src/http_resource.cpp index a9ded860..a46d0b38 100644 --- a/src/http_resource.cpp +++ b/src/http_resource.cpp @@ -48,7 +48,7 @@ void resource_init(map& allowed_methods) namespace details { -shared_ptr empty_render(const http_request& r) +shared_ptr empty_render(http_request& r) { return shared_ptr(new string_response()); } diff --git a/src/http_utils.cpp b/src/http_utils.cpp index 62fdbba0..a7106527 100644 --- a/src/http_utils.cpp +++ b/src/http_utils.cpp @@ -589,6 +589,18 @@ void dump_arg_map(std::ostream &os, const std::string &prefix, } } +size_t base_unescaper(std::string& s, unescaper_ptr unescaper) +{ + if(s[0] == 0) return 0; + + if(unescaper != 0x0) + { + unescaper(s); + return s.size(); + } + + return http_unescape(s); +} }; }; diff --git a/src/httpserver/create_webserver.hpp b/src/httpserver/create_webserver.hpp index c0ecb189..ad975941 100644 --- a/src/httpserver/create_webserver.hpp +++ b/src/httpserver/create_webserver.hpp @@ -37,9 +37,8 @@ namespace httpserver { class webserver; class http_request; -typedef const std::shared_ptr(*render_ptr)(const http_request&); +typedef const std::shared_ptr(*render_ptr)(http_request&); typedef bool(*validator_ptr)(const std::string&); -typedef void(*unescaper_ptr)(std::string&); typedef void(*log_access_ptr)(const std::string&); typedef void(*log_error_ptr)(const std::string&); diff --git a/src/httpserver/details/modded_request.hpp b/src/httpserver/details/modded_request.hpp index 74bb79df..1c44f1ca 100644 --- a/src/httpserver/details/modded_request.hpp +++ b/src/httpserver/details/modded_request.hpp @@ -38,7 +38,7 @@ struct modded_request std::string* standardized_url; webserver* ws; - const std::shared_ptr (httpserver::http_resource::*callback)(const httpserver::http_request&); + const std::shared_ptr (httpserver::http_resource::*callback)(httpserver::http_request&); http_request* dhr; std::shared_ptr dhrs; diff --git a/src/httpserver/http_request.hpp b/src/httpserver/http_request.hpp index 93a23f7b..71b831cb 100644 --- a/src/httpserver/http_request.hpp +++ b/src/httpserver/http_request.hpp @@ -56,29 +56,20 @@ class http_request * Method used to get the username eventually passed through basic authentication. * @return string representation of the username. **/ - const std::string& get_user() const - { - return this->user; - } + const std::string& get_user(); /** * Method used to get the username extracted from a digest authentication * @return the username **/ - const std::string& get_digested_user() const - { - return this->digested_user; - } + const std::string& get_digested_user(); /** * Method used to get the password eventually passed through basic authentication. * @return string representation of the password. **/ - const std::string& get_pass() const - { - return this->pass; - } - + const std::string& get_pass(); + /** * Method used to get the path requested * @return string representing the path requested. @@ -123,96 +114,52 @@ class http_request * @param result a map > that will be filled with all headers * @result the size of the map **/ - const std::map& get_headers() const - { - return this->headers; - } + const std::map& get_headers(); /** * Method used to get all footers passed with the request. * @param result a map > that will be filled with all footers * @result the size of the map **/ - const std::map& get_footers() const - { - return this->footers; - } + const std::map& get_footers(); /** * Method used to get all cookies passed with the request. * @param result a map > that will be filled with all cookies * @result the size of the map **/ - const std::map& get_cookies() const - { - return this->cookies; - } + const std::map& get_cookies(); /** * Method used to get all args passed with the request. * @param result a map > that will be filled with all args * @result the size of the map **/ - const std::map& get_args() const - { - return this->args; - } + const std::map& get_args(); /** * Method used to get a specific header passed with the request. * @param key the specific header to get the value from * @return the value of the header. **/ - const std::string& get_header(const std::string& key) const - { - std::map::const_iterator it = - this->headers.find(key); - if(it != this->headers.end()) - return it->second; - else - return EMPTY; - } + const std::string& get_header(const std::string& key); + + const std::string& get_cookie(const std::string& key); - const std::string& get_cookie(const std::string& key) const - { - std::map::const_iterator it = - this->cookies.find(key); - if(it != this->cookies.end()) - return it->second; - else - return EMPTY; - } - /** * Method used to get a specific footer passed with the request. * @param key the specific footer to get the value from * @return the value of the footer. **/ - const std::string& get_footer(const std::string& key) const - { - std::map::const_iterator it = - this->footers.find(key); - if(it != this->footers.end()) - return it->second; - else - return EMPTY; - } - + const std::string& get_footer(const std::string& key); + /** * Method used to get a specific argument passed with the request. * @param ket the specific argument to get the value from * @return the value of the arg. **/ - const std::string& get_arg(const std::string& key) const - { - std::map::const_iterator it = - this->args.find(key); - if(it != this->args.end()) - return it->second; - else - return EMPTY; - } - + const std::string& get_arg(const std::string& key); + /** * Method used to get the content of the request. * @return the content in string representation @@ -221,7 +168,7 @@ class http_request { return this->content; } - + /** * Method to check whether the size of the content reached or exceeded content_size_limit. * @return boolean @@ -234,11 +181,8 @@ class http_request * Method used to get the content of the query string.. * @return the query string in string representation **/ - const std::string& get_querystring() const - { - return this->querystring; - } - + const std::string& get_querystring(); + /** * Method used to get the version of the request. * @return the version in string representation @@ -247,37 +191,57 @@ class http_request { return this->version; } - + /** * Method used to get the requestor. * @return the requestor **/ - const std::string& get_requestor() const - { - return this->requestor; - } - + const std::string& get_requestor(); + /** * Method used to get the requestor port used. * @return the requestor port **/ - unsigned short get_requestor_port() const - { - return this->requestor_port; - } + unsigned short get_requestor_port(); bool check_digest_auth(const std::string& realm, const std::string& password, int nonce_timeout, bool& reload_nonce - ) const; + ); + + friend std::ostream &operator<< (std::ostream &os, http_request &r); - friend std::ostream &operator<< (std::ostream &os, const http_request &r); private: /** * Default constructor of the class. It is a specific responsibility of apis to initialize this type of objects. **/ http_request(): - content(""), content_size_limit(static_cast(-1)) + content(""), + content_size_limit(static_cast(-1)), + underlying_connection(0x0), + headers_loaded(false), + footers_loaded(false), + cookies_loaded(false), + args_loaded(false), + basic_auth_loaded(false), + digest_auth_loaded(false), + requestor_loaded(false), + unescaper(0x0) + { + } + + http_request(MHD_Connection* underlying_connection, unescaper_ptr unescaper): + content(""), + content_size_limit(static_cast(-1)), + underlying_connection(underlying_connection), + headers_loaded(false), + footers_loaded(false), + cookies_loaded(false), + args_loaded(false), + basic_auth_loaded(false), + digest_auth_loaded(false), + requestor_loaded(false), + unescaper(unescaper) { } @@ -301,9 +265,18 @@ class http_request content_size_limit(b.content_size_limit), version(b.version), requestor(b.requestor), - underlying_connection(b.underlying_connection) + underlying_connection(b.underlying_connection), + headers_loaded(b.headers_loaded), + footers_loaded(b.footers_loaded), + cookies_loaded(b.cookies_loaded), + args_loaded(b.args_loaded), + basic_auth_loaded(b.basic_auth_loaded), + digest_auth_loaded(b.digest_auth_loaded), + requestor_loaded(b.requestor_loaded), + unescaper(b.unescaper) { } + std::string user; std::string pass; std::string path; @@ -323,10 +296,33 @@ class http_request unsigned short requestor_port; struct MHD_Connection* underlying_connection; - void set_underlying_connection(struct MHD_Connection* conn) - { - this->underlying_connection = conn; - } + bool headers_loaded; + bool footers_loaded; + bool cookies_loaded; + bool args_loaded; + + bool basic_auth_loaded; + bool digest_auth_loaded; + + bool requestor_loaded; + + unescaper_ptr unescaper; + + static int build_request_header(void *cls, enum MHD_ValueKind kind, + const char *key, const char *value + ); + + static int build_request_footer(void *cls, enum MHD_ValueKind kind, + const char *key, const char *value + ); + + static int build_request_cookie(void *cls, enum MHD_ValueKind kind, + const char *key, const char *value + ); + + static int build_request_args(void *cls, enum MHD_ValueKind kind, + const char *key, const char *value + ); /** * Method used to set an header value by key. @@ -338,6 +334,8 @@ class http_request this->headers[key] = value; } + void check_or_fill_headers(); + /** * Method used to set a footer value by key. * @param key The name identifying the footer @@ -348,6 +346,8 @@ class http_request this->footers[key] = value; } + void check_or_fill_footers(); + /** * Method used to set a cookie value by key. * @param key The name identifying the cookie @@ -358,6 +358,8 @@ class http_request this->cookies[key] = value; } + void check_or_fill_cookies(); + /** * Method used to set an argument value by key. * @param key The name identifying the argument @@ -380,6 +382,8 @@ class http_request std::min(size, content_size_limit)); } + void check_or_fill_args(); + /** * Method used to set the content of the request * @param content The content to set. @@ -535,10 +539,14 @@ class http_request this->pass = pass; } + void basic_auth_parse(); + void digest_auth_parse(); + void parse_requestor_info(); + friend class webserver; }; -std::ostream &operator<< (std::ostream &os, const http_request &r); +std::ostream &operator<< (std::ostream &os, http_request &r); }; #endif diff --git a/src/httpserver/http_resource.hpp b/src/httpserver/http_resource.hpp index 0460f92b..1addc4b4 100644 --- a/src/httpserver/http_resource.hpp +++ b/src/httpserver/http_resource.hpp @@ -43,7 +43,7 @@ class http_request; namespace details { -std::shared_ptr empty_render(const http_request& r); +std::shared_ptr empty_render(http_request& r); }; @@ -69,7 +69,7 @@ class http_resource * @param req Request passed through http * @return A http_response object **/ - virtual const std::shared_ptr render(const http_request& req) + virtual const std::shared_ptr render(http_request& req) { return details::empty_render(req); } @@ -78,7 +78,7 @@ class http_resource * @param req Request passed through http * @return A http_response object **/ - virtual const std::shared_ptr render_GET(const http_request& req) + virtual const std::shared_ptr render_GET(http_request& req) { return render(req); } @@ -87,7 +87,7 @@ class http_resource * @param req Request passed through http * @return A http_response object **/ - virtual const std::shared_ptr render_POST(const http_request& req) + virtual const std::shared_ptr render_POST(http_request& req) { return render(req); } @@ -96,7 +96,7 @@ class http_resource * @param req Request passed through http * @return A http_response object **/ - virtual const std::shared_ptr render_PUT(const http_request& req) + virtual const std::shared_ptr render_PUT(http_request& req) { return render(req); } @@ -105,7 +105,7 @@ class http_resource * @param req Request passed through http * @return A http_response object **/ - virtual const std::shared_ptr render_HEAD(const http_request& req) + virtual const std::shared_ptr render_HEAD(http_request& req) { return render(req); } @@ -114,7 +114,7 @@ class http_resource * @param req Request passed through http * @return A http_response object **/ - virtual const std::shared_ptr render_DELETE(const http_request& req) + virtual const std::shared_ptr render_DELETE(http_request& req) { return render(req); } @@ -123,7 +123,7 @@ class http_resource * @param req Request passed through http * @return A http_response object **/ - virtual const std::shared_ptr render_TRACE(const http_request& req) + virtual const std::shared_ptr render_TRACE( http_request& req) { return render(req); } @@ -132,7 +132,7 @@ class http_resource * @param req Request passed through http * @return A http_response object **/ - virtual const std::shared_ptr render_OPTIONS(const http_request& req) + virtual const std::shared_ptr render_OPTIONS(http_request& req) { return render(req); } @@ -141,7 +141,7 @@ class http_resource * @param req Request passed through http * @return A http_response object **/ - virtual const std::shared_ptr render_CONNECT(const http_request& req) + virtual const std::shared_ptr render_CONNECT(http_request& req) { return render(req); } diff --git a/src/httpserver/http_utils.hpp b/src/httpserver/http_utils.hpp index f9608f6f..bd3df177 100644 --- a/src/httpserver/http_utils.hpp +++ b/src/httpserver/http_utils.hpp @@ -40,6 +40,9 @@ #define DEFAULT_MASK_VALUE 0xFFFF namespace httpserver { + +typedef void(*unescaper_ptr)(std::string&); + namespace http { class http_utils @@ -359,6 +362,8 @@ size_t http_unescape (std::string& val); const std::string load_file (const std::string& filename); +size_t base_unescaper(std::string&, unescaper_ptr unescaper); + }; }; #endif diff --git a/src/httpserver/webserver.hpp b/src/httpserver/webserver.hpp index 049c94f0..2660c703 100644 --- a/src/httpserver/webserver.hpp +++ b/src/httpserver/webserver.hpp @@ -200,18 +200,7 @@ class webserver struct MHD_Connection *connection, void **con_cls, enum MHD_RequestTerminationCode toe ); - static int build_request_header (void *cls, enum MHD_ValueKind kind, - const char *key, const char *value - ); - static int build_request_footer (void *cls, enum MHD_ValueKind kind, - const char *key, const char *value - ); - static int build_request_cookie (void *cls, enum MHD_ValueKind kind, - const char *key, const char *value - ); - static int build_request_args (void *cls, enum MHD_ValueKind kind, - const char *key, const char *value - ); + static int answer_to_connection ( void* cls, MHD_Connection* connection, @@ -250,11 +239,6 @@ class webserver size_t* upload_data_size, struct details::modded_request* mr ); - void end_request_construction(MHD_Connection* connection, - struct details::modded_request* mr, const char* version, - const char* method, char* &user, char* &pass, char* &digested_user - ); - int finalize_answer(MHD_Connection* connection, struct details::modded_request* mr, const char* method ); @@ -273,7 +257,6 @@ class webserver friend size_t unescaper_func(void * cls, struct MHD_Connection *c, char *s ); - friend size_t internal_unescaper(void * cls, std::string& s); friend class http_response; }; diff --git a/src/webserver.cpp b/src/webserver.cpp index 090f10d2..d235d6d0 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -73,7 +73,6 @@ void error_log(void*, const char*, va_list); void* uri_log(void*, const char*); void access_log(webserver*, string); size_t unescaper_func(void*, struct MHD_Connection*, char*); -size_t internal_unescaper(void*, std::string&); struct compare_value { @@ -238,8 +237,6 @@ bool webserver::start(bool blocking) this) ); iov.push_back(gen(MHD_OPTION_CONNECTION_TIMEOUT, connection_timeout)); - //if(bind_address != 0x0) - // iov.push_back(gen(MHD_OPTION_SOCK_ADDR, (intptr_t) bind_address)); if(bind_socket != 0) iov.push_back(gen(MHD_OPTION_LISTEN_SOCKET, bind_socket)); if(start_method == http_utils::THREAD_PER_CONNECTION && (max_threads != 0 || max_thread_stack_size != 0)) @@ -411,69 +408,6 @@ void webserver::disallow_ip(const string& ip) this->allowances.erase(ip); } -int webserver::build_request_header ( - void *cls, - enum MHD_ValueKind kind, - const char *key, - const char *value -) -{ - http_request* dhr = static_cast(cls); - dhr->set_header(key, value); - return MHD_YES; -} - -int webserver::build_request_cookie ( - void *cls, - enum MHD_ValueKind kind, - const char *key, - const char *value -) -{ - http_request* dhr = static_cast(cls); - dhr->set_cookie(key, value); - return MHD_YES; -} - -int webserver::build_request_footer ( - void *cls, - enum MHD_ValueKind kind, - const char *key, - const char *value -) -{ - http_request* dhr = static_cast(cls); - dhr->set_footer(key, value); - return MHD_YES; -} - -int webserver::build_request_args ( - void *cls, - enum MHD_ValueKind kind, - const char *key, - const char *arg_value -) -{ - details::modded_request* mr = static_cast(cls); - std::string value = ((arg_value == NULL) ? "" : arg_value); - { - char buf[std::string(key).size() + value.size() + 3]; - if(mr->dhr->querystring == "") - { - snprintf(buf, sizeof buf, "?%s=%s", key, value.c_str()); - mr->dhr->querystring = buf; - } - else - { - snprintf(buf, sizeof buf, "&%s=%s", key, value.c_str()); - mr->dhr->querystring += string(buf); - } - } - internal_unescaper((void*) mr->ws, value); - mr->dhr->set_arg(key, value); - return MHD_YES; -} - int policy_callback (void *cls, const struct sockaddr* addr, socklen_t addrlen) { if(!(static_cast(cls))->ban_system_enabled) return MHD_YES; @@ -521,20 +455,6 @@ size_t unescaper_func(void * cls, struct MHD_Connection *c, char *s) return std::string(s).size(); } -size_t internal_unescaper(void* cls, std::string& s) -{ - if(s[0] == 0) return 0; - - webserver* dws = static_cast(cls); - if(dws->unescaper != 0x0) - { - dws->unescaper(s); - return s.size(); - } - - return http_unescape(s); -} - int webserver::post_iterator (void *cls, enum MHD_ValueKind kind, const char *key, const char *filename, @@ -594,7 +514,7 @@ int webserver::bodyless_requests_answer( const char* version, struct details::modded_request* mr ) { - http_request req; + http_request req(connection, unescaper); mr->dhr = &(req); return complete_request(connection, mr, version, method); } @@ -605,7 +525,7 @@ int webserver::bodyfull_requests_answer_first_step( ) { mr->second = true; - mr->dhr = new http_request(); + mr->dhr = new http_request(connection, unescaper); mr->dhr->set_content_size_limit(content_size_limit); const char *encoding = MHD_lookup_connection_value ( connection, @@ -664,72 +584,6 @@ int webserver::bodyfull_requests_answer_second_step( return MHD_YES; } -void webserver::end_request_construction( - MHD_Connection* connection, - struct details::modded_request* mr, - const char* version, - const char* method, - char* &user, - char* &pass, - char* &digested_user -) -{ - mr->ws = this; - MHD_get_connection_values ( - connection, - MHD_GET_ARGUMENT_KIND, - &build_request_args, - (void*) mr - ); - MHD_get_connection_values ( - connection, - MHD_HEADER_KIND, - &build_request_header, - (void*) mr->dhr - ); - MHD_get_connection_values ( - connection, - MHD_FOOTER_KIND, - &build_request_footer, - (void*) mr->dhr - ); - MHD_get_connection_values ( - connection, - MHD_COOKIE_KIND, - &build_request_cookie, - (void*) mr->dhr - ); - - mr->dhr->set_path(mr->standardized_url->c_str()); - mr->dhr->set_method(method); - - if(basic_auth_enabled) - { - user = MHD_basic_auth_get_username_password(connection, &pass); - } - if(digest_auth_enabled) - { - digested_user = MHD_digest_auth_get_username(connection); - } - mr->dhr->set_version(version); - const MHD_ConnectionInfo * conninfo = MHD_get_connection_info( - connection, - MHD_CONNECTION_INFO_CLIENT_ADDRESS - ); - std::string ip_str = get_ip_str(conninfo->client_addr); - mr->dhr->set_requestor(ip_str); - mr->dhr->set_requestor_port(get_port(conninfo->client_addr)); - if(pass != 0x0) - { - mr->dhr->set_pass(pass); - mr->dhr->set_user(user); - } - if(digested_user != 0x0) - { - mr->dhr->set_digested_user(digested_user); - } -} - int webserver::finalize_answer( MHD_Connection* connection, struct details::modded_request* mr, @@ -803,8 +657,6 @@ int webserver::finalize_answer( found = true; } - mr->dhr->set_underlying_connection(connection); - if(found) { try @@ -876,30 +728,13 @@ int webserver::complete_request( const char* method ) { - char* pass = 0x0; - char* user = 0x0; - char* digested_user = 0x0; - - end_request_construction( - connection, - mr, - version, - method, - pass, - user, - digested_user - ); - - int to_ret = finalize_answer(connection, mr, method); + mr->ws = this; - if (user != 0x0) - free (user); - if (pass != 0x0) - free (pass); - if (digested_user != 0x0) - free (digested_user); + mr->dhr->set_path(mr->standardized_url->c_str()); + mr->dhr->set_method(method); + mr->dhr->set_version(version); - return to_ret; + return finalize_answer(connection, mr, method); } int webserver::answer_to_connection(void* cls, MHD_Connection* connection, @@ -926,7 +761,7 @@ int webserver::answer_to_connection(void* cls, MHD_Connection* connection, std::string t_url = url; - internal_unescaper((void*) static_cast(cls), t_url); + base_unescaper(t_url, static_cast(cls)->unescaper); mr->standardized_url = new string(http_utils::standardize_url(t_url)); bool body = false; diff --git a/test/integ/authentication.cpp b/test/integ/authentication.cpp index 79193fd9..74ac7828 100644 --- a/test/integ/authentication.cpp +++ b/test/integ/authentication.cpp @@ -48,7 +48,7 @@ size_t writefunc(void *ptr, size_t size, size_t nmemb, std::string *s) class user_pass_resource : public httpserver::http_resource { public: - const shared_ptr render_GET(const http_request& req) + const shared_ptr render_GET(http_request& req) { if (req.get_user() != "myuser" || req.get_pass() != "mypass") { @@ -61,7 +61,7 @@ class user_pass_resource : public httpserver::http_resource class digest_resource : public httpserver::http_resource { public: - const shared_ptr render_GET(const http_request& req) + const shared_ptr render_GET(http_request& req) { if (req.get_digested_user() == "") { return shared_ptr(new digest_auth_fail_response("FAIL", "test@example.com", MY_OPAQUE, true)); diff --git a/test/integ/ban_system.cpp b/test/integ/ban_system.cpp index 505c7638..73164ec5 100644 --- a/test/integ/ban_system.cpp +++ b/test/integ/ban_system.cpp @@ -38,7 +38,7 @@ size_t writefunc(void *ptr, size_t size, size_t nmemb, std::string *s) class ok_resource : public http_resource { public: - const shared_ptr render_GET(const http_request& req) + const shared_ptr render_GET(http_request& req) { return shared_ptr(new string_response("OK", 200, "text/plain")); } diff --git a/test/integ/basic.cpp b/test/integ/basic.cpp index 27b99977..714c0f1f 100644 --- a/test/integ/basic.cpp +++ b/test/integ/basic.cpp @@ -49,11 +49,11 @@ size_t headerfunc(void *ptr, size_t size, size_t nmemb, map* ss) class simple_resource : public http_resource { public: - const shared_ptr render_GET(const http_request& req) + const shared_ptr render_GET(http_request& req) { return shared_ptr(new string_response("OK", 200, "text/plain")); } - const shared_ptr render_POST(const http_request& req) + const shared_ptr render_POST(http_request& req) { return shared_ptr(new string_response(req.get_arg("arg1")+req.get_arg("arg2"), 200, "text/plain")); } @@ -62,7 +62,7 @@ class simple_resource : public http_resource class args_resource : public http_resource { public: - const shared_ptr render_GET(const http_request& req) + const shared_ptr render_GET(http_request& req) { return shared_ptr(new string_response(req.get_arg("arg") + req.get_arg("arg2"), 200, "text/plain")); } @@ -71,7 +71,7 @@ class args_resource : public http_resource class long_content_resource : public http_resource { public: - const shared_ptr render_GET(const http_request& req) + const shared_ptr render_GET(http_request& req) { return shared_ptr(new string_response(lorem_ipsum, 200, "text/plain")); } @@ -80,7 +80,7 @@ class long_content_resource : public http_resource class header_set_test_resource : public http_resource { public: - const shared_ptr render_GET(const http_request& req) + const shared_ptr render_GET(http_request& req) { shared_ptr hrb(new string_response("OK", 200, "text/plain")); hrb->with_header("KEY", "VALUE"); @@ -91,7 +91,7 @@ class header_set_test_resource : public http_resource class cookie_set_test_resource : public http_resource { public: - const shared_ptr render_GET(const http_request& req) + const shared_ptr render_GET(http_request& req) { shared_ptr hrb(new string_response("OK", 200, "text/plain")); hrb->with_cookie("MyCookie", "CookieValue"); @@ -102,7 +102,7 @@ class cookie_set_test_resource : public http_resource class cookie_reading_resource : public http_resource { public: - const shared_ptr render_GET(const http_request& req) + const shared_ptr render_GET(http_request& req) { return shared_ptr(new string_response(req.get_cookie("name"), 200, "text/plain")); } @@ -111,7 +111,7 @@ class cookie_reading_resource : public http_resource class header_reading_resource : public http_resource { public: - const shared_ptr render_GET(const http_request& req) + const shared_ptr render_GET(http_request& req) { return shared_ptr(new string_response(req.get_header("MyHeader"), 200, "text/plain")); } @@ -120,23 +120,23 @@ class header_reading_resource : public http_resource class complete_test_resource : public http_resource { public: - const shared_ptr render_GET(const http_request& req) + const shared_ptr render_GET(http_request& req) { return shared_ptr(new string_response("OK", 200, "text/plain")); } - const shared_ptr render_POST(const http_request& req) + const shared_ptr render_POST(http_request& req) { return shared_ptr(new string_response("OK", 200, "text/plain")); } - const shared_ptr render_PUT(const http_request& req) + const shared_ptr render_PUT(http_request& req) { return shared_ptr(new string_response("OK", 200, "text/plain")); } - const shared_ptr render_DELETE(const http_request& req) + const shared_ptr render_DELETE(http_request& req) { return shared_ptr(new string_response("OK", 200, "text/plain")); } - const shared_ptr render_CONNECT(const http_request& req) + const shared_ptr render_CONNECT(http_request& req) { return shared_ptr(new string_response("OK", 200, "text/plain")); } @@ -145,7 +145,7 @@ class complete_test_resource : public http_resource class only_render_resource : public http_resource { public: - const shared_ptr render(const http_request& req) + const shared_ptr render(http_request& req) { return shared_ptr(new string_response("OK", 200, "text/plain")); } @@ -154,7 +154,7 @@ class only_render_resource : public http_resource class ok_resource : public http_resource { public: - const shared_ptr render_GET(const http_request& req) + const shared_ptr render_GET(http_request& req) { return shared_ptr(new string_response("OK", 200, "text/plain")); } @@ -163,7 +163,7 @@ class ok_resource : public http_resource class nok_resource : public http_resource { public: - const shared_ptr render_GET(const http_request& req) + const shared_ptr render_GET(http_request& req) { return shared_ptr(new string_response("NOK", 200, "text/plain")); } @@ -172,7 +172,7 @@ class nok_resource : public http_resource class no_response_resource : public http_resource { public: - const shared_ptr render_GET(const http_request& req) + const shared_ptr render_GET(http_request& req) { return shared_ptr(new http_response()); } @@ -181,7 +181,7 @@ class no_response_resource : public http_resource class file_response_resource : public http_resource { public: - const shared_ptr render_GET(const http_request& req) + const shared_ptr render_GET(http_request& req) { return shared_ptr(new file_response("test_content", 200, "text/plain")); } @@ -190,7 +190,7 @@ class file_response_resource : public http_resource class exception_resource : public http_resource { public: - const shared_ptr render_GET(const http_request& req) + const shared_ptr render_GET(http_request& req) { throw std::domain_error("invalid"); } @@ -199,7 +199,7 @@ class exception_resource : public http_resource class error_resource : public http_resource { public: - const shared_ptr render_GET(const http_request& req) + const shared_ptr render_GET(http_request& req) { throw "invalid"; } @@ -213,7 +213,7 @@ class print_request_resource : public http_resource this->ss = ss; } - const shared_ptr render_GET(const http_request& req) + const shared_ptr render_GET(http_request& req) { (*ss) << req; return shared_ptr(new string_response("OK", 200, "text/plain")); @@ -231,7 +231,7 @@ class print_response_resource : public http_resource this->ss = ss; } - const shared_ptr render_GET(const http_request& req) + const shared_ptr render_GET(http_request& req) { shared_ptr hresp(new string_response("OK", 200, "text/plain")); diff --git a/test/integ/deferred.cpp b/test/integ/deferred.cpp index c853ab06..fa1dadfa 100644 --- a/test/integ/deferred.cpp +++ b/test/integ/deferred.cpp @@ -65,7 +65,7 @@ ssize_t test_callback (char* buf, size_t max) class deferred_resource : public http_resource { public: - const shared_ptr render_GET(const http_request& req) + const shared_ptr render_GET(http_request& req) { return shared_ptr(new deferred_response(test_callback, "cycle callback response")); } diff --git a/test/integ/threaded.cpp b/test/integ/threaded.cpp index 14cb4c35..cd1793b7 100644 --- a/test/integ/threaded.cpp +++ b/test/integ/threaded.cpp @@ -30,7 +30,7 @@ using namespace std; class ok_resource : public http_resource { public: - const shared_ptr render_GET(const http_request& req) + const shared_ptr render_GET(http_request& req) { return shared_ptr(new string_response("OK", 200, "text/plain")); } diff --git a/test/integ/ws_start_stop.cpp b/test/integ/ws_start_stop.cpp index ad8652dd..3515f4fc 100644 --- a/test/integ/ws_start_stop.cpp +++ b/test/integ/ws_start_stop.cpp @@ -47,18 +47,18 @@ size_t writefunc(void *ptr, size_t size, size_t nmemb, std::string *s) class ok_resource : public http_resource { public: - const shared_ptr render_GET(const http_request& req) + const shared_ptr render_GET(http_request& req) { return shared_ptr(new string_response("OK", 200, "text/plain")); } }; -const shared_ptr not_found_custom(const http_request& req) +const shared_ptr not_found_custom(http_request& req) { return shared_ptr(new string_response("Not found custom", 404, "text/plain")); } -const shared_ptr not_allowed_custom(const http_request& req) +const shared_ptr not_allowed_custom(http_request& req) { return shared_ptr(new string_response("Not allowed custom", 405, "text/plain")); } From a145c1869a3a6be7690429bfdc5ac83dcdeeac0f Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Tue, 22 Jan 2019 02:50:49 +0000 Subject: [PATCH 422/623] Align with new interface --- examples/benchmark_select.cpp | 2 +- examples/benchmark_threads.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/benchmark_select.cpp b/examples/benchmark_select.cpp index 079b5395..b18497ad 100755 --- a/examples/benchmark_select.cpp +++ b/examples/benchmark_select.cpp @@ -14,7 +14,7 @@ class hello_world_resource : public http_resource { { } - const std::shared_ptr render(const http_request&) { + const std::shared_ptr render(http_request&) { return resp; } diff --git a/examples/benchmark_threads.cpp b/examples/benchmark_threads.cpp index ba806c3d..8c2867a1 100755 --- a/examples/benchmark_threads.cpp +++ b/examples/benchmark_threads.cpp @@ -14,7 +14,7 @@ class hello_world_resource : public http_resource { { } - const std::shared_ptr render(const http_request&) { + const std::shared_ptr render(http_request&) { return resp; } From 0b4dcf430758917d48be751982418246df5ff19c Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Tue, 22 Jan 2019 08:21:28 +0000 Subject: [PATCH 423/623] Avoid regex in standardize_url --- src/http_utils.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/http_utils.cpp b/src/http_utils.cpp index 62fdbba0..6dbedd93 100644 --- a/src/http_utils.cpp +++ b/src/http_utils.cpp @@ -223,7 +223,11 @@ std::vector http_utils::tokenize_url( std::string http_utils::standardize_url(const std::string& url) { - std::string n_url = string_utilities::regex_replace(url, "(\\/)+", "/"); + std::string n_url = url; + + std::string::iterator new_end = std::unique(n_url.begin(), n_url.end(), [](char a, char b) { return (a == b) && (a == ' '); }); + n_url.erase(new_end, n_url.end()); + std::string::size_type n_url_length = n_url.length(); std::string result; From 8083b797b18e8d5dcd01f21364ed55b943efad51 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Tue, 22 Jan 2019 08:26:01 +0000 Subject: [PATCH 424/623] Fix separator character --- src/http_utils.cpp | 2 +- test/unit/http_utils_test.cpp | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/http_utils.cpp b/src/http_utils.cpp index 6dbedd93..0f75a079 100644 --- a/src/http_utils.cpp +++ b/src/http_utils.cpp @@ -225,7 +225,7 @@ std::string http_utils::standardize_url(const std::string& url) { std::string n_url = url; - std::string::iterator new_end = std::unique(n_url.begin(), n_url.end(), [](char a, char b) { return (a == b) && (a == ' '); }); + std::string::iterator new_end = std::unique(n_url.begin(), n_url.end(), [](char a, char b) { return (a == b) && (a == '/'); }); n_url.erase(new_end, n_url.end()); std::string::size_type n_url_length = n_url.length(); diff --git a/test/unit/http_utils_test.cpp b/test/unit/http_utils_test.cpp index 240bf5c8..5f7a9c7e 100644 --- a/test/unit/http_utils_test.cpp +++ b/test/unit/http_utils_test.cpp @@ -128,6 +128,10 @@ LT_BEGIN_AUTO_TEST(http_utils_suite, standardize_url) url = "/abc/pqr", result = ""; result = http::http_utils::standardize_url(url); LT_CHECK_EQ(result, "/abc/pqr"); + + url = "/abc//pqr", result = ""; + result = http::http_utils::standardize_url(url); + LT_CHECK_EQ(result, "/abc/pqr"); LT_END_AUTO_TEST(standardize_url) LT_BEGIN_AUTO_TEST(http_utils_suite, ip_to_str) From b5c2e9ccc50a269aecce9d140a437eb7ea6af5b0 Mon Sep 17 00:00:00 2001 From: Steven 'Steve' Kendall Date: Tue, 22 Jan 2019 19:48:27 +0000 Subject: [PATCH 425/623] s/buildt/built --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bacb5ba1..ee50eb42 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ Copyright (C) 2011-2019 Sebastiano Merlino. ## Tl;dr libhttpserver is a C++ library for building high performance RESTful web servers. -libhttpserver is buildt upon [libmicrohttpd](https://www.gnu.org/software/libmicrohttpd/) to provide a simple API for developers to create HTTP services in C++. +libhttpserver is built upon [libmicrohttpd](https://www.gnu.org/software/libmicrohttpd/) to provide a simple API for developers to create HTTP services in C++. **Features:** - HTTP 1.1 compatible request parser From 5fb093a289f96ad4837de3dd0b6d9e78323510b2 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Tue, 22 Jan 2019 08:21:28 +0000 Subject: [PATCH 426/623] Avoid regex in standardize_url --- src/http_utils.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/http_utils.cpp b/src/http_utils.cpp index a7106527..bb7d5bf5 100644 --- a/src/http_utils.cpp +++ b/src/http_utils.cpp @@ -223,7 +223,11 @@ std::vector http_utils::tokenize_url( std::string http_utils::standardize_url(const std::string& url) { - std::string n_url = string_utilities::regex_replace(url, "(\\/)+", "/"); + std::string n_url = url; + + std::string::iterator new_end = std::unique(n_url.begin(), n_url.end(), [](char a, char b) { return (a == b) && (a == ' '); }); + n_url.erase(new_end, n_url.end()); + std::string::size_type n_url_length = n_url.length(); std::string result; From 6aaa01823782bfa9951f8308615686f41c984af9 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Tue, 22 Jan 2019 08:26:01 +0000 Subject: [PATCH 427/623] Fix separator character --- src/http_utils.cpp | 2 +- test/unit/http_utils_test.cpp | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/http_utils.cpp b/src/http_utils.cpp index bb7d5bf5..726b217a 100644 --- a/src/http_utils.cpp +++ b/src/http_utils.cpp @@ -225,7 +225,7 @@ std::string http_utils::standardize_url(const std::string& url) { std::string n_url = url; - std::string::iterator new_end = std::unique(n_url.begin(), n_url.end(), [](char a, char b) { return (a == b) && (a == ' '); }); + std::string::iterator new_end = std::unique(n_url.begin(), n_url.end(), [](char a, char b) { return (a == b) && (a == '/'); }); n_url.erase(new_end, n_url.end()); std::string::size_type n_url_length = n_url.length(); diff --git a/test/unit/http_utils_test.cpp b/test/unit/http_utils_test.cpp index 240bf5c8..5f7a9c7e 100644 --- a/test/unit/http_utils_test.cpp +++ b/test/unit/http_utils_test.cpp @@ -128,6 +128,10 @@ LT_BEGIN_AUTO_TEST(http_utils_suite, standardize_url) url = "/abc/pqr", result = ""; result = http::http_utils::standardize_url(url); LT_CHECK_EQ(result, "/abc/pqr"); + + url = "/abc//pqr", result = ""; + result = http::http_utils::standardize_url(url); + LT_CHECK_EQ(result, "/abc/pqr"); LT_END_AUTO_TEST(standardize_url) LT_BEGIN_AUTO_TEST(http_utils_suite, ip_to_str) From 9aafc0ea87818b52e296ee26e495d15fd4839c16 Mon Sep 17 00:00:00 2001 From: Steven 'Steve' Kendall Date: Tue, 22 Jan 2019 19:48:27 +0000 Subject: [PATCH 428/623] s/buildt/built --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bacb5ba1..ee50eb42 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ Copyright (C) 2011-2019 Sebastiano Merlino. ## Tl;dr libhttpserver is a C++ library for building high performance RESTful web servers. -libhttpserver is buildt upon [libmicrohttpd](https://www.gnu.org/software/libmicrohttpd/) to provide a simple API for developers to create HTTP services in C++. +libhttpserver is built upon [libmicrohttpd](https://www.gnu.org/software/libmicrohttpd/) to provide a simple API for developers to create HTTP services in C++. **Features:** - HTTP 1.1 compatible request parser From b01e308ba0af88ce415ccde458f636c7939ed3ba Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 27 Jan 2019 05:38:31 +0000 Subject: [PATCH 429/623] Added move semantics to main classes in the library --- src/httpserver/basic_auth_fail_response.hpp | 16 ++ src/httpserver/create_webserver.hpp | 172 +++++++++++++++++++ src/httpserver/deferred_response.hpp | 19 ++ src/httpserver/details/modded_request.hpp | 49 ++++++ src/httpserver/digest_auth_fail_response.hpp | 20 +++ src/httpserver/file_response.hpp | 16 ++ src/httpserver/http_request.hpp | 85 ++++++++- src/httpserver/http_resource.hpp | 12 ++ src/httpserver/http_response.hpp | 20 +++ src/httpserver/string_response.hpp | 16 ++ 10 files changed, 417 insertions(+), 8 deletions(-) diff --git a/src/httpserver/basic_auth_fail_response.hpp b/src/httpserver/basic_auth_fail_response.hpp index a7c7b418..c9182dce 100644 --- a/src/httpserver/basic_auth_fail_response.hpp +++ b/src/httpserver/basic_auth_fail_response.hpp @@ -56,6 +56,12 @@ class basic_auth_fail_response : public string_response { } + basic_auth_fail_response(basic_auth_fail_response&& other) noexcept: + string_response(std::move(other)), + realm(std::move(other.realm)) + { + } + basic_auth_fail_response& operator=(const basic_auth_fail_response& b) { if (this == &b) return *this; @@ -66,6 +72,16 @@ class basic_auth_fail_response : public string_response return *this; } + basic_auth_fail_response& operator=(basic_auth_fail_response&& b) + { + if (this == &b) return *this; + + (string_response&) (*this) = std::move(b); + this->realm = std::move(b.realm); + + return *this; + } + ~basic_auth_fail_response() { } diff --git a/src/httpserver/create_webserver.hpp b/src/httpserver/create_webserver.hpp index c0ecb189..43a59f9d 100644 --- a/src/httpserver/create_webserver.hpp +++ b/src/httpserver/create_webserver.hpp @@ -87,6 +87,178 @@ class create_webserver { } + create_webserver(const create_webserver& b): + _port(b._port), + _start_method(b._start_method), + _max_threads(b._max_threads), + _max_connections(b._max_connections), + _memory_limit(b._memory_limit), + _content_size_limit(b._content_size_limit), + _connection_timeout(b._connection_timeout), + _per_IP_connection_limit(b._per_IP_connection_limit), + _log_access(b._log_access), + _log_error(b._log_error), + _validator(b._validator), + _unescaper(b._unescaper), + _bind_address(b._bind_address), + _bind_socket(b._bind_socket), + _max_thread_stack_size(b._max_thread_stack_size), + _use_ssl(b._use_ssl), + _use_ipv6(b._use_ipv6), + _debug(b._debug), + _pedantic(b._pedantic), + _https_mem_key(b._https_mem_key), + _https_mem_cert(b._https_mem_cert), + _https_mem_trust(b._https_mem_trust), + _https_priorities(b._https_priorities), + _cred_type(b._cred_type), + _digest_auth_random(b._digest_auth_random), + _nonce_nc_size(b._nonce_nc_size), + _default_policy(b._default_policy), + _basic_auth_enabled(b._basic_auth_enabled), + _digest_auth_enabled(b._digest_auth_enabled), + _regex_checking(b._regex_checking), + _ban_system_enabled(b._ban_system_enabled), + _post_process_enabled(b._post_process_enabled), + _deferred_enabled(b._deferred_enabled), + _single_resource(b._single_resource), + _not_found_resource(b._not_found_resource), + _method_not_allowed_resource(b._method_not_allowed_resource), + _internal_error_resource(b._internal_error_resource) + { + } + + create_webserver(create_webserver&& b): + _port(b._port), + _start_method(b._start_method), + _max_threads(b._max_threads), + _max_connections(b._max_connections), + _memory_limit(b._memory_limit), + _content_size_limit(b._content_size_limit), + _connection_timeout(b._connection_timeout), + _per_IP_connection_limit(b._per_IP_connection_limit), + _log_access(std::move(b._log_access)), + _log_error(std::move(b._log_error)), + _validator(std::move(b._validator)), + _unescaper(std::move(b._unescaper)), + _bind_address(std::move(b._bind_address)), + _bind_socket(b._bind_socket), + _max_thread_stack_size(b._max_thread_stack_size), + _use_ssl(b._use_ssl), + _use_ipv6(b._use_ipv6), + _debug(b._debug), + _pedantic(b._pedantic), + _https_mem_key(std::move(b._https_mem_key)), + _https_mem_cert(std::move(b._https_mem_cert)), + _https_mem_trust(std::move(b._https_mem_trust)), + _https_priorities(std::move(b._https_priorities)), + _cred_type(b._cred_type), + _digest_auth_random(std::move(b._digest_auth_random)), + _nonce_nc_size(b._nonce_nc_size), + _default_policy(b._default_policy), + _basic_auth_enabled(b._basic_auth_enabled), + _digest_auth_enabled(b._digest_auth_enabled), + _regex_checking(b._regex_checking), + _ban_system_enabled(b._ban_system_enabled), + _post_process_enabled(b._post_process_enabled), + _deferred_enabled(b._deferred_enabled), + _single_resource(b._single_resource), + _not_found_resource(std::move(b._not_found_resource)), + _method_not_allowed_resource(std::move(b._method_not_allowed_resource)), + _internal_error_resource(std::move(b._internal_error_resource)) + { + } + + create_webserver& operator=(const create_webserver& b) + { + if (this == &b) return *this; + + this->_port = b._port; + this->_start_method = b._start_method; + this->_max_threads = b._max_threads; + this->_max_connections = b._max_connections; + this->_memory_limit = b._memory_limit; + this->_content_size_limit = b._content_size_limit; + this->_connection_timeout = b._connection_timeout; + this->_per_IP_connection_limit = b._per_IP_connection_limit; + this->_log_access = b._log_access; + this->_log_error = b._log_error; + this->_validator = b._validator; + this->_unescaper = b._unescaper; + this->_bind_address = b._bind_address; + this->_bind_socket = b._bind_socket; + this->_max_thread_stack_size = b._max_thread_stack_size; + this->_use_ssl = b._use_ssl; + this->_use_ipv6 = b._use_ipv6; + this->_debug = b._debug; + this->_pedantic = b._pedantic; + this->_https_mem_key = b._https_mem_key; + this->_https_mem_cert = b._https_mem_cert; + this->_https_mem_trust = b._https_mem_trust; + this->_https_priorities = b._https_priorities; + this->_cred_type = b._cred_type; + this->_digest_auth_random = b._digest_auth_random; + this->_nonce_nc_size = b._nonce_nc_size; + this->_default_policy = b._default_policy; + this->_basic_auth_enabled = b._basic_auth_enabled; + this->_digest_auth_enabled = b._digest_auth_enabled; + this->_regex_checking = b._regex_checking; + this->_ban_system_enabled = b._ban_system_enabled; + this->_post_process_enabled = b._post_process_enabled; + this->_deferred_enabled = b._deferred_enabled; + this->_single_resource = b._single_resource; + this->_not_found_resource = b._not_found_resource; + this->_method_not_allowed_resource = b._method_not_allowed_resource; + this->_internal_error_resource = b._internal_error_resource; + + return *this; + } + + create_webserver& operator=(create_webserver&& b) + { + if (this == &b) return *this; + + this->_port = b._port; + this->_start_method = b._start_method; + this->_max_threads = b._max_threads; + this->_max_connections = b._max_connections; + this->_memory_limit = b._memory_limit; + this->_content_size_limit = b._content_size_limit; + this->_connection_timeout = b._connection_timeout; + this->_per_IP_connection_limit = b._per_IP_connection_limit; + this->_log_access = std::move(b._log_access); + this->_log_error = std::move(b._log_error); + this->_validator = std::move(b._validator); + this->_unescaper = std::move(b._unescaper); + this->_bind_address = std::move(b._bind_address); + this->_bind_socket = b._bind_socket; + this->_max_thread_stack_size = b._max_thread_stack_size; + this->_use_ssl = b._use_ssl; + this->_use_ipv6 = b._use_ipv6; + this->_debug = b._debug; + this->_pedantic = b._pedantic; + this->_https_mem_key = std::move(b._https_mem_key); + this->_https_mem_cert = std::move(b._https_mem_cert); + this->_https_mem_trust = std::move(b._https_mem_trust); + this->_https_priorities = std::move(b._https_priorities); + this->_cred_type = b._cred_type; + this->_digest_auth_random = std::move(b._digest_auth_random); + this->_nonce_nc_size = b._nonce_nc_size; + this->_default_policy = b._default_policy; + this->_basic_auth_enabled = b._basic_auth_enabled; + this->_digest_auth_enabled = b._digest_auth_enabled; + this->_regex_checking = b._regex_checking; + this->_ban_system_enabled = b._ban_system_enabled; + this->_post_process_enabled = b._post_process_enabled; + this->_deferred_enabled = b._deferred_enabled; + this->_single_resource = b._single_resource; + this->_not_found_resource = std::move(b._not_found_resource); + this->_method_not_allowed_resource = std::move(b._method_not_allowed_resource); + this->_internal_error_resource = std::move(b._internal_error_resource); + + return *this; + } + explicit create_webserver(uint16_t port): _port(port), _start_method(http::http_utils::INTERNAL_SELECT), diff --git a/src/httpserver/deferred_response.hpp b/src/httpserver/deferred_response.hpp index ab46a306..c5a9f1ff 100644 --- a/src/httpserver/deferred_response.hpp +++ b/src/httpserver/deferred_response.hpp @@ -59,12 +59,31 @@ class deferred_response : public string_response { } + deferred_response(deferred_response&& other) noexcept: + string_response(std::move(other)), + cycle_callback(std::move(other.cycle_callback)), + completed(other.completed) + { + } + deferred_response& operator=(const deferred_response& b) { if (this == &b) return *this; (string_response&) (*this) = b; this->cycle_callback = b.cycle_callback; + this->completed = b.completed; + + return *this; + } + + deferred_response& operator=(deferred_response&& b) + { + if (this == &b) return *this; + + (string_response&) (*this) = std::move(b); + this->cycle_callback = std::move(b.cycle_callback); + this->completed = b.completed; return *this; } diff --git a/src/httpserver/details/modded_request.hpp b/src/httpserver/details/modded_request.hpp index 74bb79df..5948e1bd 100644 --- a/src/httpserver/details/modded_request.hpp +++ b/src/httpserver/details/modded_request.hpp @@ -53,6 +53,55 @@ struct modded_request second(false) { } + + modded_request(const modded_request& b): + pp(b.pp), + complete_uri(b.complete_uri), + standardized_url(b.standardized_url), + ws(b.ws), + dhr(b.dhr), + second(b.second) + { + } + + modded_request(modded_request&& b): + pp(std::move(b.pp)), + complete_uri(std::move(b.complete_uri)), + standardized_url(std::move(b.standardized_url)), + ws(std::move(b.ws)), + dhr(std::move(b.dhr)), + second(b.second) + { + } + + modded_request& operator=(const modded_request& b) + { + if (this == &b) return *this; + + this->pp = b.pp; + this->complete_uri = b.complete_uri; + this->standardized_url = b.standardized_url; + this->ws = b.ws; + this->dhr = b.dhr; + this->second = b.second; + + return *this; + } + + modded_request& operator=(modded_request&& b) + { + if (this == &b) return *this; + + this->pp = std::move(b.pp); + this->complete_uri = std::move(b.complete_uri); + this->standardized_url = std::move(b.standardized_url); + this->ws = std::move(b.ws); + this->dhr = std::move(b.dhr); + this->second = b.second; + + return *this; + } + ~modded_request() { if (NULL != pp) diff --git a/src/httpserver/digest_auth_fail_response.hpp b/src/httpserver/digest_auth_fail_response.hpp index 2b67d268..63d02378 100644 --- a/src/httpserver/digest_auth_fail_response.hpp +++ b/src/httpserver/digest_auth_fail_response.hpp @@ -64,6 +64,14 @@ class digest_auth_fail_response : public string_response { } + digest_auth_fail_response(digest_auth_fail_response&& other) noexcept: + string_response(std::move(other)), + realm(std::move(other.realm)), + opaque(std::move(other.opaque)), + reload_nonce(other.reload_nonce) + { + } + digest_auth_fail_response& operator=(const digest_auth_fail_response& b) { if (this == &b) return *this; @@ -76,6 +84,18 @@ class digest_auth_fail_response : public string_response return *this; } + digest_auth_fail_response& operator=(digest_auth_fail_response&& b) + { + if (this == &b) return *this; + + (string_response&) (*this) = std::move(b); + this->realm = std::move(b.realm); + this->opaque = std::move(b.opaque); + this->reload_nonce = b.reload_nonce; + + return *this; + } + ~digest_auth_fail_response() { } diff --git a/src/httpserver/file_response.hpp b/src/httpserver/file_response.hpp index 9f7a3977..1192d91a 100644 --- a/src/httpserver/file_response.hpp +++ b/src/httpserver/file_response.hpp @@ -55,6 +55,12 @@ class file_response : public http_response { } + file_response(file_response&& other) noexcept: + http_response(std::move(other)), + filename(std::move(other.filename)) + { + } + file_response& operator=(const file_response& b) { if (this == &b) return *this; @@ -65,6 +71,16 @@ class file_response : public http_response return *this; } + file_response& operator=(file_response&& b) + { + if (this == &b) return *this; + + (http_response&) (*this) = std::move(b); + this->filename = std::move(b.filename); + + return *this; + } + ~file_response() { } diff --git a/src/httpserver/http_request.hpp b/src/httpserver/http_request.hpp index 93a23f7b..96b49833 100644 --- a/src/httpserver/http_request.hpp +++ b/src/httpserver/http_request.hpp @@ -78,7 +78,7 @@ class http_request { return this->pass; } - + /** * Method used to get the path requested * @return string representing the path requested. @@ -182,7 +182,7 @@ class http_request else return EMPTY; } - + /** * Method used to get a specific footer passed with the request. * @param key the specific footer to get the value from @@ -197,7 +197,7 @@ class http_request else return EMPTY; } - + /** * Method used to get a specific argument passed with the request. * @param ket the specific argument to get the value from @@ -212,7 +212,7 @@ class http_request else return EMPTY; } - + /** * Method used to get the content of the request. * @return the content in string representation @@ -221,7 +221,7 @@ class http_request { return this->content; } - + /** * Method to check whether the size of the content reached or exceeded content_size_limit. * @return boolean @@ -238,7 +238,7 @@ class http_request { return this->querystring; } - + /** * Method used to get the version of the request. * @return the version in string representation @@ -247,7 +247,7 @@ class http_request { return this->version; } - + /** * Method used to get the requestor. * @return the requestor @@ -256,7 +256,7 @@ class http_request { return this->requestor; } - + /** * Method used to get the requestor port used. * @return the requestor port @@ -304,6 +304,75 @@ class http_request underlying_connection(b.underlying_connection) { } + + http_request(http_request&& b) noexcept: + user(std::move(b.user)), + pass(std::move(b.pass)), + path(std::move(b.path)), + digested_user(std::move(b.digested_user)), + method(std::move(b.method)), + post_path(std::move(b.post_path)), + headers(std::move(b.headers)), + footers(std::move(b.footers)), + cookies(std::move(b.cookies)), + args(std::move(b.args)), + querystring(std::move(b.querystring)), + content(std::move(b.content)), + content_size_limit(b.content_size_limit), + version(std::move(b.version)), + requestor(std::move(b.requestor)), + underlying_connection(std::move(b.underlying_connection)) + { + } + + http_request& operator=(const http_request& b) + { + if (this == &b) return *this; + + this->user = b.user; + this->pass = b.pass; + this->path = b.path; + this->digested_user = b.digested_user; + this->method = b.method; + this->post_path = b.post_path; + this->headers = b.headers; + this->footers = b.footers; + this->cookies = b.cookies; + this->args = b.args; + this->querystring = b.querystring; + this->content = b.content; + this->content_size_limit = b.content_size_limit; + this->version = b.version; + this->requestor = b.requestor; + this->underlying_connection = b.underlying_connection; + + return *this; + } + + http_request& operator=(http_request&& b) + { + if (this == &b) return *this; + + this->user = std::move(b.user); + this->pass = std::move(b.pass); + this->path = std::move(b.path); + this->digested_user = std::move(b.digested_user); + this->method = std::move(b.method); + this->post_path = std::move(b.post_path); + this->headers = std::move(b.headers); + this->footers = std::move(b.footers); + this->cookies = std::move(b.cookies); + this->args = std::move(b.args); + this->querystring = std::move(b.querystring); + this->content = std::move(b.content); + this->content_size_limit = b.content_size_limit; + this->version = std::move(b.version); + this->requestor = std::move(b.requestor); + this->underlying_connection = std::move(b.underlying_connection); + + return *this; + } + std::string user; std::string pass; std::string path; diff --git a/src/httpserver/http_resource.hpp b/src/httpserver/http_resource.hpp index 0460f92b..faaa3463 100644 --- a/src/httpserver/http_resource.hpp +++ b/src/httpserver/http_resource.hpp @@ -212,12 +212,24 @@ class http_resource **/ http_resource(const http_resource& b) : allowed_methods(b.allowed_methods) { } + http_resource(http_resource&& b) noexcept: allowed_methods(std::move(b.allowed_methods)) { } + http_resource& operator=(const http_resource& b) { + if (this == &b) return *this; + allowed_methods = b.allowed_methods; return (*this); } + http_resource& operator=(http_resource&& b) + { + if (this == &b) return *this; + + allowed_methods = std::move(b.allowed_methods); + return (*this); + } + private: friend class webserver; friend void resource_init(std::map& res); diff --git a/src/httpserver/http_response.hpp b/src/httpserver/http_response.hpp index 0846878f..b7f9b9a9 100644 --- a/src/httpserver/http_response.hpp +++ b/src/httpserver/http_response.hpp @@ -68,6 +68,14 @@ class http_response { } + http_response(http_response&& other) noexcept: + response_code(other.response_code), + headers(std::move(other.headers)), + footers(std::move(other.footers)), + cookies(std::move(other.cookies)) + { + } + http_response& operator=(const http_response& b) { if (this == &b) return *this; @@ -80,6 +88,18 @@ class http_response return *this; } + http_response& operator=(http_response&& b) + { + if (this == &b) return *this; + + this->response_code = b.response_code; + this->headers = std::move(b.headers); + this->footers = std::move(b.footers); + this->cookies = std::move(b.cookies); + + return *this; + } + virtual ~http_response() { } diff --git a/src/httpserver/string_response.hpp b/src/httpserver/string_response.hpp index c0177a74..0397f38c 100644 --- a/src/httpserver/string_response.hpp +++ b/src/httpserver/string_response.hpp @@ -55,6 +55,12 @@ class string_response : public http_response { } + string_response(string_response&& other) noexcept: + http_response(std::move(other)), + content(std::move(other.content)) + { + } + string_response& operator=(const string_response& b) { if (this == &b) return *this; @@ -65,6 +71,16 @@ class string_response : public http_response return *this; } + string_response& operator=(string_response&& b) + { + if (this == &b) return *this; + + (http_response&) (*this) = std::move(b); + this->content = std::move(b.content); + + return *this; + } + ~string_response() { } From 9549b3f8fe31502cd0b7cc3043311dfa42eae572 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 27 Jan 2019 05:38:31 +0000 Subject: [PATCH 430/623] Added move semantics to main classes in the library --- src/httpserver/basic_auth_fail_response.hpp | 16 ++ src/httpserver/create_webserver.hpp | 172 +++++++++++++++++++ src/httpserver/deferred_response.hpp | 19 ++ src/httpserver/details/modded_request.hpp | 49 ++++++ src/httpserver/digest_auth_fail_response.hpp | 20 +++ src/httpserver/file_response.hpp | 16 ++ src/httpserver/http_request.hpp | 68 ++++++++ src/httpserver/http_resource.hpp | 12 ++ src/httpserver/http_response.hpp | 20 +++ src/httpserver/string_response.hpp | 16 ++ 10 files changed, 408 insertions(+) diff --git a/src/httpserver/basic_auth_fail_response.hpp b/src/httpserver/basic_auth_fail_response.hpp index a7c7b418..c9182dce 100644 --- a/src/httpserver/basic_auth_fail_response.hpp +++ b/src/httpserver/basic_auth_fail_response.hpp @@ -56,6 +56,12 @@ class basic_auth_fail_response : public string_response { } + basic_auth_fail_response(basic_auth_fail_response&& other) noexcept: + string_response(std::move(other)), + realm(std::move(other.realm)) + { + } + basic_auth_fail_response& operator=(const basic_auth_fail_response& b) { if (this == &b) return *this; @@ -66,6 +72,16 @@ class basic_auth_fail_response : public string_response return *this; } + basic_auth_fail_response& operator=(basic_auth_fail_response&& b) + { + if (this == &b) return *this; + + (string_response&) (*this) = std::move(b); + this->realm = std::move(b.realm); + + return *this; + } + ~basic_auth_fail_response() { } diff --git a/src/httpserver/create_webserver.hpp b/src/httpserver/create_webserver.hpp index ad975941..3cc52a6d 100644 --- a/src/httpserver/create_webserver.hpp +++ b/src/httpserver/create_webserver.hpp @@ -86,6 +86,178 @@ class create_webserver { } + create_webserver(const create_webserver& b): + _port(b._port), + _start_method(b._start_method), + _max_threads(b._max_threads), + _max_connections(b._max_connections), + _memory_limit(b._memory_limit), + _content_size_limit(b._content_size_limit), + _connection_timeout(b._connection_timeout), + _per_IP_connection_limit(b._per_IP_connection_limit), + _log_access(b._log_access), + _log_error(b._log_error), + _validator(b._validator), + _unescaper(b._unescaper), + _bind_address(b._bind_address), + _bind_socket(b._bind_socket), + _max_thread_stack_size(b._max_thread_stack_size), + _use_ssl(b._use_ssl), + _use_ipv6(b._use_ipv6), + _debug(b._debug), + _pedantic(b._pedantic), + _https_mem_key(b._https_mem_key), + _https_mem_cert(b._https_mem_cert), + _https_mem_trust(b._https_mem_trust), + _https_priorities(b._https_priorities), + _cred_type(b._cred_type), + _digest_auth_random(b._digest_auth_random), + _nonce_nc_size(b._nonce_nc_size), + _default_policy(b._default_policy), + _basic_auth_enabled(b._basic_auth_enabled), + _digest_auth_enabled(b._digest_auth_enabled), + _regex_checking(b._regex_checking), + _ban_system_enabled(b._ban_system_enabled), + _post_process_enabled(b._post_process_enabled), + _deferred_enabled(b._deferred_enabled), + _single_resource(b._single_resource), + _not_found_resource(b._not_found_resource), + _method_not_allowed_resource(b._method_not_allowed_resource), + _internal_error_resource(b._internal_error_resource) + { + } + + create_webserver(create_webserver&& b): + _port(b._port), + _start_method(b._start_method), + _max_threads(b._max_threads), + _max_connections(b._max_connections), + _memory_limit(b._memory_limit), + _content_size_limit(b._content_size_limit), + _connection_timeout(b._connection_timeout), + _per_IP_connection_limit(b._per_IP_connection_limit), + _log_access(std::move(b._log_access)), + _log_error(std::move(b._log_error)), + _validator(std::move(b._validator)), + _unescaper(std::move(b._unescaper)), + _bind_address(std::move(b._bind_address)), + _bind_socket(b._bind_socket), + _max_thread_stack_size(b._max_thread_stack_size), + _use_ssl(b._use_ssl), + _use_ipv6(b._use_ipv6), + _debug(b._debug), + _pedantic(b._pedantic), + _https_mem_key(std::move(b._https_mem_key)), + _https_mem_cert(std::move(b._https_mem_cert)), + _https_mem_trust(std::move(b._https_mem_trust)), + _https_priorities(std::move(b._https_priorities)), + _cred_type(b._cred_type), + _digest_auth_random(std::move(b._digest_auth_random)), + _nonce_nc_size(b._nonce_nc_size), + _default_policy(b._default_policy), + _basic_auth_enabled(b._basic_auth_enabled), + _digest_auth_enabled(b._digest_auth_enabled), + _regex_checking(b._regex_checking), + _ban_system_enabled(b._ban_system_enabled), + _post_process_enabled(b._post_process_enabled), + _deferred_enabled(b._deferred_enabled), + _single_resource(b._single_resource), + _not_found_resource(std::move(b._not_found_resource)), + _method_not_allowed_resource(std::move(b._method_not_allowed_resource)), + _internal_error_resource(std::move(b._internal_error_resource)) + { + } + + create_webserver& operator=(const create_webserver& b) + { + if (this == &b) return *this; + + this->_port = b._port; + this->_start_method = b._start_method; + this->_max_threads = b._max_threads; + this->_max_connections = b._max_connections; + this->_memory_limit = b._memory_limit; + this->_content_size_limit = b._content_size_limit; + this->_connection_timeout = b._connection_timeout; + this->_per_IP_connection_limit = b._per_IP_connection_limit; + this->_log_access = b._log_access; + this->_log_error = b._log_error; + this->_validator = b._validator; + this->_unescaper = b._unescaper; + this->_bind_address = b._bind_address; + this->_bind_socket = b._bind_socket; + this->_max_thread_stack_size = b._max_thread_stack_size; + this->_use_ssl = b._use_ssl; + this->_use_ipv6 = b._use_ipv6; + this->_debug = b._debug; + this->_pedantic = b._pedantic; + this->_https_mem_key = b._https_mem_key; + this->_https_mem_cert = b._https_mem_cert; + this->_https_mem_trust = b._https_mem_trust; + this->_https_priorities = b._https_priorities; + this->_cred_type = b._cred_type; + this->_digest_auth_random = b._digest_auth_random; + this->_nonce_nc_size = b._nonce_nc_size; + this->_default_policy = b._default_policy; + this->_basic_auth_enabled = b._basic_auth_enabled; + this->_digest_auth_enabled = b._digest_auth_enabled; + this->_regex_checking = b._regex_checking; + this->_ban_system_enabled = b._ban_system_enabled; + this->_post_process_enabled = b._post_process_enabled; + this->_deferred_enabled = b._deferred_enabled; + this->_single_resource = b._single_resource; + this->_not_found_resource = b._not_found_resource; + this->_method_not_allowed_resource = b._method_not_allowed_resource; + this->_internal_error_resource = b._internal_error_resource; + + return *this; + } + + create_webserver& operator=(create_webserver&& b) + { + if (this == &b) return *this; + + this->_port = b._port; + this->_start_method = b._start_method; + this->_max_threads = b._max_threads; + this->_max_connections = b._max_connections; + this->_memory_limit = b._memory_limit; + this->_content_size_limit = b._content_size_limit; + this->_connection_timeout = b._connection_timeout; + this->_per_IP_connection_limit = b._per_IP_connection_limit; + this->_log_access = std::move(b._log_access); + this->_log_error = std::move(b._log_error); + this->_validator = std::move(b._validator); + this->_unescaper = std::move(b._unescaper); + this->_bind_address = std::move(b._bind_address); + this->_bind_socket = b._bind_socket; + this->_max_thread_stack_size = b._max_thread_stack_size; + this->_use_ssl = b._use_ssl; + this->_use_ipv6 = b._use_ipv6; + this->_debug = b._debug; + this->_pedantic = b._pedantic; + this->_https_mem_key = std::move(b._https_mem_key); + this->_https_mem_cert = std::move(b._https_mem_cert); + this->_https_mem_trust = std::move(b._https_mem_trust); + this->_https_priorities = std::move(b._https_priorities); + this->_cred_type = b._cred_type; + this->_digest_auth_random = std::move(b._digest_auth_random); + this->_nonce_nc_size = b._nonce_nc_size; + this->_default_policy = b._default_policy; + this->_basic_auth_enabled = b._basic_auth_enabled; + this->_digest_auth_enabled = b._digest_auth_enabled; + this->_regex_checking = b._regex_checking; + this->_ban_system_enabled = b._ban_system_enabled; + this->_post_process_enabled = b._post_process_enabled; + this->_deferred_enabled = b._deferred_enabled; + this->_single_resource = b._single_resource; + this->_not_found_resource = std::move(b._not_found_resource); + this->_method_not_allowed_resource = std::move(b._method_not_allowed_resource); + this->_internal_error_resource = std::move(b._internal_error_resource); + + return *this; + } + explicit create_webserver(uint16_t port): _port(port), _start_method(http::http_utils::INTERNAL_SELECT), diff --git a/src/httpserver/deferred_response.hpp b/src/httpserver/deferred_response.hpp index ab46a306..c5a9f1ff 100644 --- a/src/httpserver/deferred_response.hpp +++ b/src/httpserver/deferred_response.hpp @@ -59,12 +59,31 @@ class deferred_response : public string_response { } + deferred_response(deferred_response&& other) noexcept: + string_response(std::move(other)), + cycle_callback(std::move(other.cycle_callback)), + completed(other.completed) + { + } + deferred_response& operator=(const deferred_response& b) { if (this == &b) return *this; (string_response&) (*this) = b; this->cycle_callback = b.cycle_callback; + this->completed = b.completed; + + return *this; + } + + deferred_response& operator=(deferred_response&& b) + { + if (this == &b) return *this; + + (string_response&) (*this) = std::move(b); + this->cycle_callback = std::move(b.cycle_callback); + this->completed = b.completed; return *this; } diff --git a/src/httpserver/details/modded_request.hpp b/src/httpserver/details/modded_request.hpp index 1c44f1ca..a8962818 100644 --- a/src/httpserver/details/modded_request.hpp +++ b/src/httpserver/details/modded_request.hpp @@ -53,6 +53,55 @@ struct modded_request second(false) { } + + modded_request(const modded_request& b): + pp(b.pp), + complete_uri(b.complete_uri), + standardized_url(b.standardized_url), + ws(b.ws), + dhr(b.dhr), + second(b.second) + { + } + + modded_request(modded_request&& b): + pp(std::move(b.pp)), + complete_uri(std::move(b.complete_uri)), + standardized_url(std::move(b.standardized_url)), + ws(std::move(b.ws)), + dhr(std::move(b.dhr)), + second(b.second) + { + } + + modded_request& operator=(const modded_request& b) + { + if (this == &b) return *this; + + this->pp = b.pp; + this->complete_uri = b.complete_uri; + this->standardized_url = b.standardized_url; + this->ws = b.ws; + this->dhr = b.dhr; + this->second = b.second; + + return *this; + } + + modded_request& operator=(modded_request&& b) + { + if (this == &b) return *this; + + this->pp = std::move(b.pp); + this->complete_uri = std::move(b.complete_uri); + this->standardized_url = std::move(b.standardized_url); + this->ws = std::move(b.ws); + this->dhr = std::move(b.dhr); + this->second = b.second; + + return *this; + } + ~modded_request() { if (NULL != pp) diff --git a/src/httpserver/digest_auth_fail_response.hpp b/src/httpserver/digest_auth_fail_response.hpp index 2b67d268..63d02378 100644 --- a/src/httpserver/digest_auth_fail_response.hpp +++ b/src/httpserver/digest_auth_fail_response.hpp @@ -64,6 +64,14 @@ class digest_auth_fail_response : public string_response { } + digest_auth_fail_response(digest_auth_fail_response&& other) noexcept: + string_response(std::move(other)), + realm(std::move(other.realm)), + opaque(std::move(other.opaque)), + reload_nonce(other.reload_nonce) + { + } + digest_auth_fail_response& operator=(const digest_auth_fail_response& b) { if (this == &b) return *this; @@ -76,6 +84,18 @@ class digest_auth_fail_response : public string_response return *this; } + digest_auth_fail_response& operator=(digest_auth_fail_response&& b) + { + if (this == &b) return *this; + + (string_response&) (*this) = std::move(b); + this->realm = std::move(b.realm); + this->opaque = std::move(b.opaque); + this->reload_nonce = b.reload_nonce; + + return *this; + } + ~digest_auth_fail_response() { } diff --git a/src/httpserver/file_response.hpp b/src/httpserver/file_response.hpp index 9f7a3977..1192d91a 100644 --- a/src/httpserver/file_response.hpp +++ b/src/httpserver/file_response.hpp @@ -55,6 +55,12 @@ class file_response : public http_response { } + file_response(file_response&& other) noexcept: + http_response(std::move(other)), + filename(std::move(other.filename)) + { + } + file_response& operator=(const file_response& b) { if (this == &b) return *this; @@ -65,6 +71,16 @@ class file_response : public http_response return *this; } + file_response& operator=(file_response&& b) + { + if (this == &b) return *this; + + (http_response&) (*this) = std::move(b); + this->filename = std::move(b.filename); + + return *this; + } + ~file_response() { } diff --git a/src/httpserver/http_request.hpp b/src/httpserver/http_request.hpp index 71b831cb..a5ac0710 100644 --- a/src/httpserver/http_request.hpp +++ b/src/httpserver/http_request.hpp @@ -277,6 +277,74 @@ class http_request { } + http_request(http_request&& b) noexcept: + user(std::move(b.user)), + pass(std::move(b.pass)), + path(std::move(b.path)), + digested_user(std::move(b.digested_user)), + method(std::move(b.method)), + post_path(std::move(b.post_path)), + headers(std::move(b.headers)), + footers(std::move(b.footers)), + cookies(std::move(b.cookies)), + args(std::move(b.args)), + querystring(std::move(b.querystring)), + content(std::move(b.content)), + content_size_limit(b.content_size_limit), + version(std::move(b.version)), + requestor(std::move(b.requestor)), + underlying_connection(std::move(b.underlying_connection)) + { + } + + http_request& operator=(const http_request& b) + { + if (this == &b) return *this; + + this->user = b.user; + this->pass = b.pass; + this->path = b.path; + this->digested_user = b.digested_user; + this->method = b.method; + this->post_path = b.post_path; + this->headers = b.headers; + this->footers = b.footers; + this->cookies = b.cookies; + this->args = b.args; + this->querystring = b.querystring; + this->content = b.content; + this->content_size_limit = b.content_size_limit; + this->version = b.version; + this->requestor = b.requestor; + this->underlying_connection = b.underlying_connection; + + return *this; + } + + http_request& operator=(http_request&& b) + { + if (this == &b) return *this; + + this->user = std::move(b.user); + this->pass = std::move(b.pass); + this->path = std::move(b.path); + this->digested_user = std::move(b.digested_user); + this->method = std::move(b.method); + this->post_path = std::move(b.post_path); + this->headers = std::move(b.headers); + this->footers = std::move(b.footers); + this->cookies = std::move(b.cookies); + this->args = std::move(b.args); + this->querystring = std::move(b.querystring); + this->content = std::move(b.content); + this->content_size_limit = b.content_size_limit; + this->version = std::move(b.version); + this->requestor = std::move(b.requestor); + this->underlying_connection = std::move(b.underlying_connection); + + return *this; + } + std::string user; std::string pass; std::string path; diff --git a/src/httpserver/http_resource.hpp b/src/httpserver/http_resource.hpp index 1addc4b4..182d891c 100644 --- a/src/httpserver/http_resource.hpp +++ b/src/httpserver/http_resource.hpp @@ -212,12 +212,24 @@ class http_resource **/ http_resource(const http_resource& b) : allowed_methods(b.allowed_methods) { } + http_resource(http_resource&& b) noexcept: allowed_methods(std::move(b.allowed_methods)) { } + http_resource& operator=(const http_resource& b) { + if (this == &b) return *this; + allowed_methods = b.allowed_methods; return (*this); } + http_resource& operator=(http_resource&& b) + { + if (this == &b) return *this; + + allowed_methods = std::move(b.allowed_methods); + return (*this); + } + private: friend class webserver; friend void resource_init(std::map& res); diff --git a/src/httpserver/http_response.hpp b/src/httpserver/http_response.hpp index 0846878f..b7f9b9a9 100644 --- a/src/httpserver/http_response.hpp +++ b/src/httpserver/http_response.hpp @@ -68,6 +68,14 @@ class http_response { } + http_response(http_response&& other) noexcept: + response_code(other.response_code), + headers(std::move(other.headers)), + footers(std::move(other.footers)), + cookies(std::move(other.cookies)) + { + } + http_response& operator=(const http_response& b) { if (this == &b) return *this; @@ -80,6 +88,18 @@ class http_response return *this; } + http_response& operator=(http_response&& b) + { + if (this == &b) return *this; + + this->response_code = b.response_code; + this->headers = std::move(b.headers); + this->footers = std::move(b.footers); + this->cookies = std::move(b.cookies); + + return *this; + } + virtual ~http_response() { } diff --git a/src/httpserver/string_response.hpp b/src/httpserver/string_response.hpp index c0177a74..0397f38c 100644 --- a/src/httpserver/string_response.hpp +++ b/src/httpserver/string_response.hpp @@ -55,6 +55,12 @@ class string_response : public http_response { } + string_response(string_response&& other) noexcept: + http_response(std::move(other)), + content(std::move(other.content)) + { + } + string_response& operator=(const string_response& b) { if (this == &b) return *this; @@ -65,6 +71,16 @@ class string_response : public http_response return *this; } + string_response& operator=(string_response&& b) + { + if (this == &b) return *this; + + (http_response&) (*this) = std::move(b); + this->content = std::move(b.content); + + return *this; + } + ~string_response() { } From 44bc3db52efae06c2808648f01950c5bcd147486 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Mon, 28 Jan 2019 02:18:28 +0000 Subject: [PATCH 431/623] Re-make getter methods immutable --- examples/allowing_disallowing_methods.cpp | 2 +- examples/basic_authentication.cpp | 2 +- examples/benchmark_select.cpp | 2 +- examples/benchmark_threads.cpp | 2 +- examples/custom_access_log.cpp | 2 +- examples/custom_error.cpp | 6 +- examples/digest_authentication.cpp | 2 +- examples/handlers.cpp | 4 +- examples/hello_with_get_arg.cpp | 2 +- examples/hello_world.cpp | 4 +- examples/minimal_deferred.cpp | 2 +- examples/minimal_file_response.cpp | 2 +- examples/minimal_hello_world.cpp | 2 +- examples/minimal_https.cpp | 2 +- examples/minimal_ip_ban.cpp | 2 +- examples/service.cpp | 32 +-- examples/setting_headers.cpp | 2 +- examples/url_registration.cpp | 6 +- src/http_request.cpp | 329 +++++++++------------- src/http_resource.cpp | 2 +- src/httpserver/create_webserver.hpp | 2 +- src/httpserver/details/modded_request.hpp | 2 +- src/httpserver/http_request.hpp | 236 ++-------------- src/httpserver/http_resource.hpp | 20 +- src/webserver.cpp | 3 +- test/integ/authentication.cpp | 4 +- test/integ/ban_system.cpp | 2 +- test/integ/basic.cpp | 44 +-- test/integ/deferred.cpp | 2 +- test/integ/threaded.cpp | 2 +- test/integ/ws_start_stop.cpp | 6 +- 31 files changed, 233 insertions(+), 499 deletions(-) diff --git a/examples/allowing_disallowing_methods.cpp b/examples/allowing_disallowing_methods.cpp index eb84773c..69295af4 100644 --- a/examples/allowing_disallowing_methods.cpp +++ b/examples/allowing_disallowing_methods.cpp @@ -24,7 +24,7 @@ using namespace httpserver; class hello_world_resource : public http_resource { public: - const std::shared_ptr render(http_request&) { + const std::shared_ptr render(const http_request&) { return std::shared_ptr(new string_response("Hello, World!")); } }; diff --git a/examples/basic_authentication.cpp b/examples/basic_authentication.cpp index e7d28b82..84de823e 100644 --- a/examples/basic_authentication.cpp +++ b/examples/basic_authentication.cpp @@ -25,7 +25,7 @@ using namespace httpserver; class user_pass_resource : public httpserver::http_resource { public: - const std::shared_ptr render_GET(http_request& req) + const std::shared_ptr render_GET(const http_request& req) { if (req.get_user() != "myuser" || req.get_pass() != "mypass") { diff --git a/examples/benchmark_select.cpp b/examples/benchmark_select.cpp index b18497ad..079b5395 100755 --- a/examples/benchmark_select.cpp +++ b/examples/benchmark_select.cpp @@ -14,7 +14,7 @@ class hello_world_resource : public http_resource { { } - const std::shared_ptr render(http_request&) { + const std::shared_ptr render(const http_request&) { return resp; } diff --git a/examples/benchmark_threads.cpp b/examples/benchmark_threads.cpp index 8c2867a1..ba806c3d 100755 --- a/examples/benchmark_threads.cpp +++ b/examples/benchmark_threads.cpp @@ -14,7 +14,7 @@ class hello_world_resource : public http_resource { { } - const std::shared_ptr render(http_request&) { + const std::shared_ptr render(const http_request&) { return resp; } diff --git a/examples/custom_access_log.cpp b/examples/custom_access_log.cpp index 8158e329..7e5f1ef5 100644 --- a/examples/custom_access_log.cpp +++ b/examples/custom_access_log.cpp @@ -29,7 +29,7 @@ void custom_access_log(const std::string& url) { class hello_world_resource : public http_resource { public: - const std::shared_ptr render(http_request&) { + const std::shared_ptr render(const http_request&) { return std::shared_ptr(new string_response("Hello, World!")); } }; diff --git a/examples/custom_error.cpp b/examples/custom_error.cpp index 6271a180..034f334c 100644 --- a/examples/custom_error.cpp +++ b/examples/custom_error.cpp @@ -22,19 +22,19 @@ using namespace httpserver; -const std::shared_ptr not_found_custom( http_request& req) +const std::shared_ptr not_found_custom(const http_request& req) { return std::shared_ptr(new string_response("Not found custom", 404, "text/plain")); } -const std::shared_ptr not_allowed_custom(http_request& req) +const std::shared_ptr not_allowed_custom(const http_request& req) { return std::shared_ptr(new string_response("Not allowed custom", 405, "text/plain")); } class hello_world_resource : public http_resource { public: - const std::shared_ptr render(http_request&) { + const std::shared_ptr render(const http_request&) { return std::shared_ptr(new string_response("Hello, World!")); } }; diff --git a/examples/digest_authentication.cpp b/examples/digest_authentication.cpp index 406e823b..55f119a0 100644 --- a/examples/digest_authentication.cpp +++ b/examples/digest_authentication.cpp @@ -26,7 +26,7 @@ using namespace httpserver; class digest_resource : public httpserver::http_resource { public: - const std::shared_ptr render_GET(http_request& req) { + const std::shared_ptr render_GET(const http_request& req) { if (req.get_digested_user() == "") { return std::shared_ptr(new digest_auth_fail_response("FAIL", "test@example.com", MY_OPAQUE, true)); } diff --git a/examples/handlers.cpp b/examples/handlers.cpp index 610e69fa..4ddad426 100644 --- a/examples/handlers.cpp +++ b/examples/handlers.cpp @@ -24,11 +24,11 @@ using namespace httpserver; class hello_world_resource : public http_resource { public: - const std::shared_ptr render_GET(http_request&) { + const std::shared_ptr render_GET(const http_request&) { return std::shared_ptr(new string_response("GET: Hello, World!")); } - const std::shared_ptr render(http_request&) { + const std::shared_ptr render(const http_request&) { return std::shared_ptr(new string_response("OTHER: Hello, World!")); } }; diff --git a/examples/hello_with_get_arg.cpp b/examples/hello_with_get_arg.cpp index 67fbfb75..07ae90c1 100644 --- a/examples/hello_with_get_arg.cpp +++ b/examples/hello_with_get_arg.cpp @@ -24,7 +24,7 @@ using namespace httpserver; class hello_world_resource : public http_resource { public: - const std::shared_ptr render(http_request& req) { + const std::shared_ptr render(const http_request& req) { return std::shared_ptr(new string_response("Hello: " + req.get_arg("name"))); } }; diff --git a/examples/hello_world.cpp b/examples/hello_world.cpp index c8e701cc..f9675b32 100755 --- a/examples/hello_world.cpp +++ b/examples/hello_world.cpp @@ -25,13 +25,13 @@ using namespace httpserver; class hello_world_resource : public http_resource { public: - const std::shared_ptr render(http_request&); + const std::shared_ptr render(const http_request&); void set_some_data(const std::string &s) {data = s;} std::string data; }; //using the render method you are able to catch each type of request you receive -const std::shared_ptr hello_world_resource::render(http_request& req) +const std::shared_ptr hello_world_resource::render(const http_request& req) { //it is possible to store data inside the resource object that can be altered //through the requests diff --git a/examples/minimal_deferred.cpp b/examples/minimal_deferred.cpp index dbe11865..a08599c2 100644 --- a/examples/minimal_deferred.cpp +++ b/examples/minimal_deferred.cpp @@ -38,7 +38,7 @@ ssize_t test_callback (char* buf, size_t max) { class deferred_resource : public http_resource { public: - const std::shared_ptr render_GET(http_request& req) { + const std::shared_ptr render_GET(const http_request& req) { return std::shared_ptr(new deferred_response(test_callback, "cycle callback response")); } }; diff --git a/examples/minimal_file_response.cpp b/examples/minimal_file_response.cpp index bf6a70ff..b82d2929 100644 --- a/examples/minimal_file_response.cpp +++ b/examples/minimal_file_response.cpp @@ -25,7 +25,7 @@ using namespace httpserver; class file_response_resource : public http_resource { public: - const std::shared_ptr render_GET(http_request& req) + const std::shared_ptr render_GET(const http_request& req) { return std::shared_ptr(new file_response("test_content", 200, "text/plain")); } diff --git a/examples/minimal_hello_world.cpp b/examples/minimal_hello_world.cpp index fd124532..76489a0e 100644 --- a/examples/minimal_hello_world.cpp +++ b/examples/minimal_hello_world.cpp @@ -24,7 +24,7 @@ using namespace httpserver; class hello_world_resource : public http_resource { public: - const std::shared_ptr render(http_request&) { + const std::shared_ptr render(const http_request&) { return std::shared_ptr(new string_response("Hello, World!")); } }; diff --git a/examples/minimal_https.cpp b/examples/minimal_https.cpp index 640b4c38..def0452a 100644 --- a/examples/minimal_https.cpp +++ b/examples/minimal_https.cpp @@ -24,7 +24,7 @@ using namespace httpserver; class hello_world_resource : public http_resource { public: - const std::shared_ptr render(http_request&) { + const std::shared_ptr render(const http_request&) { return std::shared_ptr(new string_response("Hello, World!")); } }; diff --git a/examples/minimal_ip_ban.cpp b/examples/minimal_ip_ban.cpp index 63f9952a..ea0923e7 100644 --- a/examples/minimal_ip_ban.cpp +++ b/examples/minimal_ip_ban.cpp @@ -24,7 +24,7 @@ using namespace httpserver; class hello_world_resource : public http_resource { public: - const std::shared_ptr render(http_request&) { + const std::shared_ptr render(const http_request&) { return std::shared_ptr(new string_response("Hello, World!")); } }; diff --git a/examples/service.cpp b/examples/service.cpp index 74783127..b2d365fc 100644 --- a/examples/service.cpp +++ b/examples/service.cpp @@ -33,14 +33,14 @@ class service_resource: public http_resource { ~service_resource(); - const std::shared_ptr render_GET(http_request &req); - const std::shared_ptr render_PUT(http_request &req); - const std::shared_ptr render_POST(http_request &req); - const std::shared_ptr render(http_request &req); - const std::shared_ptr render_HEAD(http_request &req); - const std::shared_ptr render_OPTIONS(http_request &req); - const std::shared_ptr render_CONNECT(http_request &req); - const std::shared_ptr render_DELETE(http_request &req); + const std::shared_ptr render_GET(const http_request &req); + const std::shared_ptr render_PUT(const http_request &req); + const std::shared_ptr render_POST(const http_request &req); + const std::shared_ptr render(const http_request &req); + const std::shared_ptr render_HEAD(const http_request &req); + const std::shared_ptr render_OPTIONS(const http_request &req); + const std::shared_ptr render_CONNECT(const http_request &req); + const std::shared_ptr render_DELETE(const http_request &req); private: @@ -54,7 +54,7 @@ service_resource::~service_resource() {} const std::shared_ptr -service_resource::render_GET(http_request &req) +service_resource::render_GET(const http_request &req) { std::cout << "service_resource::render_GET()" << std::endl; @@ -68,7 +68,7 @@ service_resource::render_GET(http_request &req) const std::shared_ptr -service_resource::render_PUT(http_request &req) +service_resource::render_PUT(const http_request &req) { std::cout << "service_resource::render_PUT()" << std::endl; @@ -83,7 +83,7 @@ service_resource::render_PUT(http_request &req) const std::shared_ptr -service_resource::render_POST(http_request &req) +service_resource::render_POST(const http_request &req) { std::cout << "service_resource::render_POST()" << std::endl; @@ -97,7 +97,7 @@ service_resource::render_POST(http_request &req) } const std::shared_ptr -service_resource::render(http_request &req) +service_resource::render(const http_request &req) { std::cout << "service_resource::render()" << std::endl; @@ -112,7 +112,7 @@ service_resource::render(http_request &req) const std::shared_ptr -service_resource::render_HEAD(http_request &req) +service_resource::render_HEAD(const http_request &req) { std::cout << "service_resource::render_HEAD()" << std::endl; @@ -126,7 +126,7 @@ service_resource::render_HEAD(http_request &req) } const std::shared_ptr -service_resource::render_OPTIONS(http_request &req) +service_resource::render_OPTIONS(const http_request &req) { std::cout << "service_resource::render_OPTIONS()" << std::endl; @@ -140,7 +140,7 @@ service_resource::render_OPTIONS(http_request &req) } const std::shared_ptr -service_resource::render_CONNECT(http_request &req) +service_resource::render_CONNECT(const http_request &req) { std::cout << "service_resource::render_CONNECT()" << std::endl; @@ -154,7 +154,7 @@ service_resource::render_CONNECT(http_request &req) } const std::shared_ptr -service_resource::render_DELETE(http_request &req) +service_resource::render_DELETE(const http_request &req) { std::cout << "service_resource::render_DELETE()" << std::endl; diff --git a/examples/setting_headers.cpp b/examples/setting_headers.cpp index 8d7e2323..0fb73c79 100644 --- a/examples/setting_headers.cpp +++ b/examples/setting_headers.cpp @@ -24,7 +24,7 @@ using namespace httpserver; class hello_world_resource : public http_resource { public: - const std::shared_ptr render(http_request&) { + const std::shared_ptr render(const http_request&) { std::shared_ptr response = std::shared_ptr(new string_response("Hello, World!")); response->with_header("MyHeader", "MyValue"); return response; diff --git a/examples/url_registration.cpp b/examples/url_registration.cpp index 7cb0cf2f..0f8da6ad 100644 --- a/examples/url_registration.cpp +++ b/examples/url_registration.cpp @@ -24,21 +24,21 @@ using namespace httpserver; class hello_world_resource : public http_resource { public: - const std::shared_ptr render(http_request&) { + const std::shared_ptr render(const http_request&) { return std::shared_ptr(new string_response("Hello, World!")); } }; class handling_multiple_resource : public http_resource { public: - const std::shared_ptr render(http_request& req) { + const std::shared_ptr render(const http_request& req) { return std::shared_ptr(new string_response("Your URL: " + req.get_path())); } }; class url_args_resource : public http_resource { public: - const std::shared_ptr render(http_request& req) { + const std::shared_ptr render(const http_request& req) { return std::shared_ptr(new string_response("ARGS: " + req.get_arg("arg1") + " and " + req.get_arg("arg2"))); } }; diff --git a/src/http_request.cpp b/src/http_request.cpp index 00bf84ab..973ab338 100644 --- a/src/http_request.cpp +++ b/src/http_request.cpp @@ -31,6 +31,12 @@ namespace httpserver const std::string http_request::EMPTY = ""; +struct arguments_accumulator +{ + unescaper_ptr unescaper; + std::map* arguments; +}; + void http_request::set_method(const std::string& method) { this->method = string_utilities::to_upper_copy(method); @@ -41,9 +47,9 @@ bool http_request::check_digest_auth( const std::string& password, int nonce_timeout, bool& reload_nonce -) +) const { - digest_auth_parse(); + std::string digested_user = get_digested_user(); int val = MHD_digest_auth_check( underlying_connection, @@ -67,317 +73,242 @@ bool http_request::check_digest_auth( return true; } - -const std::string& http_request::get_header(const std::string& key) +const std::string http_request::get_connection_value(const std::string& key, enum MHD_ValueKind kind) const { - check_or_fill_headers(); - - std::map::const_iterator it = this->headers.find(key); - if(it != this->headers.end()) - { - return it->second; - } - else - { - return EMPTY; - } -} + const char* header_c = MHD_lookup_connection_value( + this->underlying_connection, + kind, + key.c_str() + ); -const std::map& http_request::get_headers() -{ - check_or_fill_headers(); + if (header_c == NULL) return EMPTY; - return this->headers; + return header_c; } -const std::string& http_request::get_footer(const std::string& key) +int http_request::build_request_header( + void *cls, + enum MHD_ValueKind kind, + const char *key, + const char *value +) { - check_or_fill_footers(); - - std::map::const_iterator it = this->footers.find(key); - if(it != this->footers.end()) - { - return it->second; - } - else - { - return EMPTY; - } + std::map* dhr = static_cast*>(cls); + (*dhr)[key] = value; + return MHD_YES; } -const std::map& http_request::get_footers() +const std::map http_request::get_headerlike_values(enum MHD_ValueKind kind) const { - check_or_fill_footers(); + std::map headers; - return this->footers; -} - -const std::string& http_request::get_cookie(const std::string& key) -{ - check_or_fill_cookies(); + MHD_get_connection_values( + this->underlying_connection, + kind, + &build_request_header, + (void*) &headers + ); - std::map::const_iterator it = this->cookies.find(key); - if(it != this->cookies.end()) - { - return it->second; - } - else - { - return EMPTY; - } + return headers; } -const std::map& http_request::get_cookies() +const std::string http_request::get_header(const std::string& key) const { - check_or_fill_cookies(); - - return this->cookies; + return get_connection_value(key, MHD_HEADER_KIND); } -const std::string& http_request::get_arg(const std::string& key) +const std::map http_request::get_headers() const { - check_or_fill_args(); - - std::map::const_iterator it = this->args.find(key); - if(it != this->args.end()) - { - return it->second; - } - else - { - return EMPTY; - } + return get_headerlike_values(MHD_HEADER_KIND); } -const std::map& http_request::get_args() +const std::string http_request::get_footer(const std::string& key) const { - check_or_fill_args(); - - return this->args; + return get_connection_value(key, MHD_FOOTER_KIND); } -const std::string& http_request::get_querystring() +const std::map http_request::get_footers() const { - check_or_fill_args(); - - return this->querystring; + return get_headerlike_values(MHD_FOOTER_KIND); } -std::ostream &operator<< (std::ostream &os, http_request &r) +const std::string http_request::get_cookie(const std::string& key) const { - os << r.get_method() << " Request [user:\"" << r.get_user() << "\" pass:\"" << r.get_pass() << "\"] path:\"" - << r.get_path() << "\"" << std::endl; - - http::dump_header_map(os,"Headers",r.get_headers()); - http::dump_header_map(os,"Footers",r.get_footers()); - http::dump_header_map(os,"Cookies",r.get_cookies()); - http::dump_arg_map(os,"Query Args",r.get_args()); - - os << " Version [ " << r.get_version() << " ] Requestor [ " << r.get_requestor() - << " ] Port [ " << r.get_requestor_port() << " ]" << std::endl; - - return os; + return get_connection_value(key, MHD_COOKIE_KIND); } -void http_request::check_or_fill_headers() +const std::map http_request::get_cookies() const { - if (this->headers_loaded) return; - - MHD_get_connection_values ( - this->underlying_connection, - MHD_HEADER_KIND, - &build_request_header, - (void*) this - ); - - this->headers_loaded = true; + return get_headerlike_values(MHD_COOKIE_KIND); } -void http_request::check_or_fill_cookies() +const std::string http_request::get_arg(const std::string& key) const { - if (this->cookies_loaded) return; + std::map::const_iterator it = this->args.find(key); - MHD_get_connection_values ( - this->underlying_connection, - MHD_COOKIE_KIND, - &build_request_cookie, - (void*) this - ); + if(it != this->args.end()) + { + return it->second; + } - this->cookies_loaded = true; + return get_connection_value(key, MHD_GET_ARGUMENT_KIND); } -void http_request::check_or_fill_footers() +const std::map http_request::get_args() const { - if (this->footers_loaded) return; + std::map arguments; + arguments.insert(this->args.begin(), this->args.end()); - MHD_get_connection_values ( + arguments_accumulator aa; + aa.unescaper = this->unescaper; + aa.arguments = &arguments; + + MHD_get_connection_values( this->underlying_connection, - MHD_FOOTER_KIND, - &build_request_footer, - (void*) this + MHD_GET_ARGUMENT_KIND, + &build_request_args, + (void*) &aa ); - this->footers_loaded = true; + return arguments; } -void http_request::check_or_fill_args() +const std::string http_request::get_querystring() const { - if (this->args_loaded) return; + std::string querystring = ""; - MHD_get_connection_values ( + MHD_get_connection_values( this->underlying_connection, MHD_GET_ARGUMENT_KIND, - &build_request_args, - (void*) this + &build_request_querystring, + (void*) &querystring ); - this->args_loaded = true; -} - -int http_request::build_request_header( - void *cls, - enum MHD_ValueKind kind, - const char *key, - const char *value -) -{ - http_request* dhr = static_cast(cls); - dhr->set_header(key, value); - return MHD_YES; + return querystring; } -int http_request::build_request_cookie ( +int http_request::build_request_args( void *cls, enum MHD_ValueKind kind, const char *key, - const char *value + const char *arg_value ) { - http_request* dhr = static_cast(cls); - dhr->set_cookie(key, value); - return MHD_YES; -} + arguments_accumulator* aa = static_cast(cls); + std::string value = ((arg_value == NULL) ? "" : arg_value); -int http_request::build_request_footer( - void *cls, - enum MHD_ValueKind kind, - const char *key, - const char *value -) -{ - http_request* dhr = static_cast(cls); - dhr->set_footer(key, value); + http::base_unescaper(value, aa->unescaper); + (*aa->arguments)[key] = value; return MHD_YES; } -int http_request::build_request_args( +int http_request::build_request_querystring( void *cls, enum MHD_ValueKind kind, const char *key, const char *arg_value ) { - http_request* dhr = static_cast(cls); + std::string* querystring = static_cast(cls); std::string value = ((arg_value == NULL) ? "" : arg_value); { char buf[std::string(key).size() + value.size() + 3]; - if(dhr->querystring == "") + if(*querystring == "") { snprintf(buf, sizeof buf, "?%s=%s", key, value.c_str()); - dhr->querystring = buf; + *querystring = buf; } else { snprintf(buf, sizeof buf, "&%s=%s", key, value.c_str()); - dhr->querystring += string(buf); + *querystring += string(buf); } } - http::base_unescaper(value, dhr->unescaper); - dhr->set_arg(key, value); + return MHD_YES; } -const std::string& http_request::get_user() +const std::string http_request::get_user() const { - basic_auth_parse(); - return this->user; -} + char* username = 0x0; + char* password = 0x0; -const std::string& http_request::get_pass() -{ - basic_auth_parse(); - return this->pass; + username = MHD_basic_auth_get_username_password(underlying_connection, &password); + if (password != 0x0) free(password); + + std::string user; + if (username != 0x0) user = username; + + free(username); + + return user; } -void http_request::basic_auth_parse() +const std::string http_request::get_pass() const { - if (this->basic_auth_loaded) return; - char* username = 0x0; char* password = 0x0; + username = MHD_basic_auth_get_username_password(underlying_connection, &password); + if (username != 0x0) free(username); - if (username != 0x0) - { - this->user = username; - free(username); - } - if (password != 0x0) - { - this->pass = password; - free(password); - } + std::string pass; + if (password != 0x0) pass = password; - this->basic_auth_loaded = true; -} + free(password); -const std::string& http_request::get_digested_user() -{ - digest_auth_parse(); - return this->digested_user; + return pass; } -void http_request::digest_auth_parse() +const std::string http_request::get_digested_user() const { - if (this->digest_auth_loaded) return; - char* digested_user_c = 0x0; digested_user_c = MHD_digest_auth_get_username(underlying_connection); + std::string digested_user = EMPTY; if (digested_user_c != 0x0) { - this->digested_user = digested_user_c; + digested_user = digested_user_c; free(digested_user_c); } - this->digest_auth_loaded = true; + return digested_user; } -const std::string& http_request::get_requestor() +const std::string http_request::get_requestor() const { - parse_requestor_info(); - return this->requestor; -} + const MHD_ConnectionInfo * conninfo = MHD_get_connection_info( + underlying_connection, + MHD_CONNECTION_INFO_CLIENT_ADDRESS + ); -unsigned short http_request::get_requestor_port() -{ - parse_requestor_info(); - return this->requestor_port; + return http::get_ip_str(conninfo->client_addr); } -void http_request::parse_requestor_info() +unsigned short http_request::get_requestor_port() const { - if (this->requestor_loaded) return; - const MHD_ConnectionInfo * conninfo = MHD_get_connection_info( underlying_connection, MHD_CONNECTION_INFO_CLIENT_ADDRESS ); - std::string ip_str = http::get_ip_str(conninfo->client_addr); - this->set_requestor(ip_str); - this->set_requestor_port(http::get_port(conninfo->client_addr)); + return http::get_port(conninfo->client_addr); +} + +std::ostream &operator<< (std::ostream &os, const http_request &r) +{ + os << r.get_method() << " Request [user:\"" << r.get_user() << "\" pass:\"" << r.get_pass() << "\"] path:\"" + << r.get_path() << "\"" << std::endl; + + http::dump_header_map(os,"Headers",r.get_headers()); + http::dump_header_map(os,"Footers",r.get_footers()); + http::dump_header_map(os,"Cookies",r.get_cookies()); + http::dump_arg_map(os,"Query Args",r.get_args()); + + os << " Version [ " << r.get_version() << " ] Requestor [ " << r.get_requestor() + << " ] Port [ " << r.get_requestor_port() << " ]" << std::endl; + + return os; } } diff --git a/src/http_resource.cpp b/src/http_resource.cpp index a46d0b38..a9ded860 100644 --- a/src/http_resource.cpp +++ b/src/http_resource.cpp @@ -48,7 +48,7 @@ void resource_init(map& allowed_methods) namespace details { -shared_ptr empty_render(http_request& r) +shared_ptr empty_render(const http_request& r) { return shared_ptr(new string_response()); } diff --git a/src/httpserver/create_webserver.hpp b/src/httpserver/create_webserver.hpp index 3cc52a6d..6fc5d3c5 100644 --- a/src/httpserver/create_webserver.hpp +++ b/src/httpserver/create_webserver.hpp @@ -37,7 +37,7 @@ namespace httpserver { class webserver; class http_request; -typedef const std::shared_ptr(*render_ptr)(http_request&); +typedef const std::shared_ptr(*render_ptr)(const http_request&); typedef bool(*validator_ptr)(const std::string&); typedef void(*log_access_ptr)(const std::string&); typedef void(*log_error_ptr)(const std::string&); diff --git a/src/httpserver/details/modded_request.hpp b/src/httpserver/details/modded_request.hpp index a8962818..5948e1bd 100644 --- a/src/httpserver/details/modded_request.hpp +++ b/src/httpserver/details/modded_request.hpp @@ -38,7 +38,7 @@ struct modded_request std::string* standardized_url; webserver* ws; - const std::shared_ptr (httpserver::http_resource::*callback)(httpserver::http_request&); + const std::shared_ptr (httpserver::http_resource::*callback)(const httpserver::http_request&); http_request* dhr; std::shared_ptr dhrs; diff --git a/src/httpserver/http_request.hpp b/src/httpserver/http_request.hpp index a5ac0710..9ae77ccd 100644 --- a/src/httpserver/http_request.hpp +++ b/src/httpserver/http_request.hpp @@ -56,19 +56,19 @@ class http_request * Method used to get the username eventually passed through basic authentication. * @return string representation of the username. **/ - const std::string& get_user(); + const std::string get_user() const; /** * Method used to get the username extracted from a digest authentication * @return the username **/ - const std::string& get_digested_user(); + const std::string get_digested_user() const; /** * Method used to get the password eventually passed through basic authentication. * @return string representation of the password. **/ - const std::string& get_pass(); + const std::string get_pass() const; /** * Method used to get the path requested @@ -114,51 +114,51 @@ class http_request * @param result a map > that will be filled with all headers * @result the size of the map **/ - const std::map& get_headers(); + const std::map get_headers() const; /** * Method used to get all footers passed with the request. * @param result a map > that will be filled with all footers * @result the size of the map **/ - const std::map& get_footers(); + const std::map get_footers() const; /** * Method used to get all cookies passed with the request. * @param result a map > that will be filled with all cookies * @result the size of the map **/ - const std::map& get_cookies(); + const std::map get_cookies() const; /** * Method used to get all args passed with the request. * @param result a map > that will be filled with all args * @result the size of the map **/ - const std::map& get_args(); + const std::map get_args() const; /** * Method used to get a specific header passed with the request. * @param key the specific header to get the value from * @return the value of the header. **/ - const std::string& get_header(const std::string& key); + const std::string get_header(const std::string& key) const; - const std::string& get_cookie(const std::string& key); + const std::string get_cookie(const std::string& key) const; /** * Method used to get a specific footer passed with the request. * @param key the specific footer to get the value from * @return the value of the footer. **/ - const std::string& get_footer(const std::string& key); + const std::string get_footer(const std::string& key) const; /** * Method used to get a specific argument passed with the request. * @param ket the specific argument to get the value from * @return the value of the arg. **/ - const std::string& get_arg(const std::string& key); + const std::string get_arg(const std::string& key) const; /** * Method used to get the content of the request. @@ -181,7 +181,7 @@ class http_request * Method used to get the content of the query string.. * @return the query string in string representation **/ - const std::string& get_querystring(); + const std::string get_querystring() const; /** * Method used to get the version of the request. @@ -196,18 +196,18 @@ class http_request * Method used to get the requestor. * @return the requestor **/ - const std::string& get_requestor(); + const std::string get_requestor() const; /** * Method used to get the requestor port used. * @return the requestor port **/ - unsigned short get_requestor_port(); + unsigned short get_requestor_port() const; bool check_digest_auth(const std::string& realm, const std::string& password, int nonce_timeout, bool& reload_nonce - ); + ) const; friend std::ostream &operator<< (std::ostream &os, http_request &r); @@ -219,13 +219,6 @@ class http_request content(""), content_size_limit(static_cast(-1)), underlying_connection(0x0), - headers_loaded(false), - footers_loaded(false), - cookies_loaded(false), - args_loaded(false), - basic_auth_loaded(false), - digest_auth_loaded(false), - requestor_loaded(false), unescaper(0x0) { } @@ -234,13 +227,6 @@ class http_request content(""), content_size_limit(static_cast(-1)), underlying_connection(underlying_connection), - headers_loaded(false), - footers_loaded(false), - cookies_loaded(false), - args_loaded(false), - basic_auth_loaded(false), - digest_auth_loaded(false), - requestor_loaded(false), unescaper(unescaper) { } @@ -250,49 +236,26 @@ class http_request * @param b http_request b to copy attributes from. **/ http_request(const http_request& b): - user(b.user), - pass(b.pass), path(b.path), - digested_user(b.digested_user), method(b.method), post_path(b.post_path), - headers(b.headers), - footers(b.footers), - cookies(b.cookies), args(b.args), - querystring(b.querystring), content(b.content), content_size_limit(b.content_size_limit), version(b.version), - requestor(b.requestor), underlying_connection(b.underlying_connection), - headers_loaded(b.headers_loaded), - footers_loaded(b.footers_loaded), - cookies_loaded(b.cookies_loaded), - args_loaded(b.args_loaded), - basic_auth_loaded(b.basic_auth_loaded), - digest_auth_loaded(b.digest_auth_loaded), - requestor_loaded(b.requestor_loaded), unescaper(b.unescaper) { } http_request(http_request&& b) noexcept: - user(std::move(b.user)), - pass(std::move(b.pass)), path(std::move(b.path)), - digested_user(std::move(b.digested_user)), method(std::move(b.method)), post_path(std::move(b.post_path)), - headers(std::move(b.headers)), - footers(std::move(b.footers)), - cookies(std::move(b.cookies)), args(std::move(b.args)), - querystring(std::move(b.querystring)), content(std::move(b.content)), content_size_limit(b.content_size_limit), version(std::move(b.version)), - requestor(std::move(b.requestor)), underlying_connection(std::move(b.underlying_connection)) { } @@ -301,21 +264,13 @@ class http_request { if (this == &b) return *this; - this->user = b.user; - this->pass = b.pass; this->path = b.path; - this->digested_user = b.digested_user; this->method = b.method; this->post_path = b.post_path; - this->headers = b.headers; - this->footers = b.footers; - this->cookies = b.cookies; this->args = b.args; - this->querystring = b.querystring; this->content = b.content; this->content_size_limit = b.content_size_limit; this->version = b.version; - this->requestor = b.requestor; this->underlying_connection = b.underlying_connection; return *this; @@ -325,109 +280,42 @@ class http_request { if (this == &b) return *this; - this->user = std::move(b.user); - this->pass = std::move(b.pass); this->path = std::move(b.path); - this->digested_user = std::move(b.digested_user); this->method = std::move(b.method); this->post_path = std::move(b.post_path); - this->headers = std::move(b.headers); - this->footers = std::move(b.footers); - this->cookies = std::move(b.cookies); this->args = std::move(b.args); - this->querystring = std::move(b.querystring); this->content = std::move(b.content); this->content_size_limit = b.content_size_limit; this->version = std::move(b.version); - this->requestor = std::move(b.requestor); this->underlying_connection = std::move(b.underlying_connection); return *this; } - std::string user; - std::string pass; std::string path; - std::string digested_user; std::string method; std::vector post_path; - std::map headers; - std::map footers; - std::map cookies; std::map args; - std::string querystring; std::string content; size_t content_size_limit; std::string version; - std::string requestor; - unsigned short requestor_port; struct MHD_Connection* underlying_connection; - bool headers_loaded; - bool footers_loaded; - bool cookies_loaded; - bool args_loaded; - - bool basic_auth_loaded; - bool digest_auth_loaded; - - bool requestor_loaded; - unescaper_ptr unescaper; static int build_request_header(void *cls, enum MHD_ValueKind kind, const char *key, const char *value ); - static int build_request_footer(void *cls, enum MHD_ValueKind kind, - const char *key, const char *value - ); - - static int build_request_cookie(void *cls, enum MHD_ValueKind kind, + static int build_request_args(void *cls, enum MHD_ValueKind kind, const char *key, const char *value ); - static int build_request_args(void *cls, enum MHD_ValueKind kind, + static int build_request_querystring(void *cls, enum MHD_ValueKind kind, const char *key, const char *value ); - /** - * Method used to set an header value by key. - * @param key The name identifying the header - * @param value The value assumed by the header - **/ - void set_header(const std::string& key, const std::string& value) - { - this->headers[key] = value; - } - - void check_or_fill_headers(); - - /** - * Method used to set a footer value by key. - * @param key The name identifying the footer - * @param value The value assumed by the footer - **/ - void set_footer(const std::string& key, const std::string& value) - { - this->footers[key] = value; - } - - void check_or_fill_footers(); - - /** - * Method used to set a cookie value by key. - * @param key The name identifying the cookie - * @param value The value assumed by the cookie - **/ - void set_cookie(const std::string& key, const std::string& value) - { - this->cookies[key] = value; - } - - void check_or_fill_cookies(); - /** * Method used to set an argument value by key. * @param key The name identifying the argument @@ -513,66 +401,6 @@ class http_request this->version = version; } - /** - * Method used to set the requestor - * @param requestor The requestor to set - **/ - void set_requestor(const std::string& requestor) - { - this->requestor = requestor; - } - - /** - * Method used to set the requestor port - * @param requestor The requestor port to set - **/ - void set_requestor_port(unsigned short requestor_port) - { - this->requestor_port = requestor_port; - } - - /** - * Method used to remove an header previously inserted - * @param key The key identifying the header to remove. - **/ - void remove_header(const std::string& key) - { - this->headers.erase(key); - } - - /** - * Method used to set all headers of the request. - * @param headers The headers key-value map to set for the request. - **/ - void set_headers(const std::map& headers) - { - std::map::const_iterator it; - for(it = headers.begin(); it != headers.end(); ++it) - this->headers[it->first] = it->second; - } - - /** - * Method used to set all footers of the request. - * @param footers The footers key-value map to set for the request. - **/ - void set_footers(const std::map& footers) - { - std::map::const_iterator it; - for(it = footers.begin(); it != footers.end(); ++it) - this->footers[it->first] = it->second; - } - - /** - * Method used to set all cookies of the request. - * @param cookies The cookies key-value map to set for the request. - **/ - void set_cookies(const std::map& cookies) - { - std::map::const_iterator it; - for(it = cookies.begin(); it != cookies.end(); ++it) - this->cookies[it->first] = it->second; - } - /** * Method used to set all arguments of the request. * @param args The args key-value map to set for the request. @@ -584,37 +412,13 @@ class http_request this->args[it->first] = it->second.substr(0,content_size_limit); } - /** - * Method used to set the username of the request. - * @param user The username to set. - **/ - void set_user(const std::string& user) - { - this->user = user; - } - - void set_digested_user(const std::string& digested_user) - { - this->digested_user = digested_user; - } - - /** - * Method used to set the password of the request. - * @param pass The password to set. - **/ - void set_pass(const std::string& pass) - { - this->pass = pass; - } - - void basic_auth_parse(); - void digest_auth_parse(); - void parse_requestor_info(); + const std::string get_connection_value(const std::string& key, enum MHD_ValueKind kind) const; + const std::map get_headerlike_values(enum MHD_ValueKind kind) const; friend class webserver; }; -std::ostream &operator<< (std::ostream &os, http_request &r); +std::ostream &operator<< (std::ostream &os, const http_request &r); }; #endif diff --git a/src/httpserver/http_resource.hpp b/src/httpserver/http_resource.hpp index 182d891c..faaa3463 100644 --- a/src/httpserver/http_resource.hpp +++ b/src/httpserver/http_resource.hpp @@ -43,7 +43,7 @@ class http_request; namespace details { -std::shared_ptr empty_render(http_request& r); +std::shared_ptr empty_render(const http_request& r); }; @@ -69,7 +69,7 @@ class http_resource * @param req Request passed through http * @return A http_response object **/ - virtual const std::shared_ptr render(http_request& req) + virtual const std::shared_ptr render(const http_request& req) { return details::empty_render(req); } @@ -78,7 +78,7 @@ class http_resource * @param req Request passed through http * @return A http_response object **/ - virtual const std::shared_ptr render_GET(http_request& req) + virtual const std::shared_ptr render_GET(const http_request& req) { return render(req); } @@ -87,7 +87,7 @@ class http_resource * @param req Request passed through http * @return A http_response object **/ - virtual const std::shared_ptr render_POST(http_request& req) + virtual const std::shared_ptr render_POST(const http_request& req) { return render(req); } @@ -96,7 +96,7 @@ class http_resource * @param req Request passed through http * @return A http_response object **/ - virtual const std::shared_ptr render_PUT(http_request& req) + virtual const std::shared_ptr render_PUT(const http_request& req) { return render(req); } @@ -105,7 +105,7 @@ class http_resource * @param req Request passed through http * @return A http_response object **/ - virtual const std::shared_ptr render_HEAD(http_request& req) + virtual const std::shared_ptr render_HEAD(const http_request& req) { return render(req); } @@ -114,7 +114,7 @@ class http_resource * @param req Request passed through http * @return A http_response object **/ - virtual const std::shared_ptr render_DELETE(http_request& req) + virtual const std::shared_ptr render_DELETE(const http_request& req) { return render(req); } @@ -123,7 +123,7 @@ class http_resource * @param req Request passed through http * @return A http_response object **/ - virtual const std::shared_ptr render_TRACE( http_request& req) + virtual const std::shared_ptr render_TRACE(const http_request& req) { return render(req); } @@ -132,7 +132,7 @@ class http_resource * @param req Request passed through http * @return A http_response object **/ - virtual const std::shared_ptr render_OPTIONS(http_request& req) + virtual const std::shared_ptr render_OPTIONS(const http_request& req) { return render(req); } @@ -141,7 +141,7 @@ class http_resource * @param req Request passed through http * @return A http_response object **/ - virtual const std::shared_ptr render_CONNECT(http_request& req) + virtual const std::shared_ptr render_CONNECT(const http_request& req) { return render(req); } diff --git a/src/webserver.cpp b/src/webserver.cpp index d235d6d0..e71c3648 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -532,8 +532,7 @@ int webserver::bodyfull_requests_answer_first_step( MHD_HEADER_KIND, http_utils::http_header_content_type.c_str() ); - if(encoding != 0x0) - mr->dhr->set_header(http_utils::http_header_content_type, encoding); + if ( post_process_enabled && ( 0x0 != encoding && diff --git a/test/integ/authentication.cpp b/test/integ/authentication.cpp index 74ac7828..79193fd9 100644 --- a/test/integ/authentication.cpp +++ b/test/integ/authentication.cpp @@ -48,7 +48,7 @@ size_t writefunc(void *ptr, size_t size, size_t nmemb, std::string *s) class user_pass_resource : public httpserver::http_resource { public: - const shared_ptr render_GET(http_request& req) + const shared_ptr render_GET(const http_request& req) { if (req.get_user() != "myuser" || req.get_pass() != "mypass") { @@ -61,7 +61,7 @@ class user_pass_resource : public httpserver::http_resource class digest_resource : public httpserver::http_resource { public: - const shared_ptr render_GET(http_request& req) + const shared_ptr render_GET(const http_request& req) { if (req.get_digested_user() == "") { return shared_ptr(new digest_auth_fail_response("FAIL", "test@example.com", MY_OPAQUE, true)); diff --git a/test/integ/ban_system.cpp b/test/integ/ban_system.cpp index 73164ec5..505c7638 100644 --- a/test/integ/ban_system.cpp +++ b/test/integ/ban_system.cpp @@ -38,7 +38,7 @@ size_t writefunc(void *ptr, size_t size, size_t nmemb, std::string *s) class ok_resource : public http_resource { public: - const shared_ptr render_GET(http_request& req) + const shared_ptr render_GET(const http_request& req) { return shared_ptr(new string_response("OK", 200, "text/plain")); } diff --git a/test/integ/basic.cpp b/test/integ/basic.cpp index 714c0f1f..27b99977 100644 --- a/test/integ/basic.cpp +++ b/test/integ/basic.cpp @@ -49,11 +49,11 @@ size_t headerfunc(void *ptr, size_t size, size_t nmemb, map* ss) class simple_resource : public http_resource { public: - const shared_ptr render_GET(http_request& req) + const shared_ptr render_GET(const http_request& req) { return shared_ptr(new string_response("OK", 200, "text/plain")); } - const shared_ptr render_POST(http_request& req) + const shared_ptr render_POST(const http_request& req) { return shared_ptr(new string_response(req.get_arg("arg1")+req.get_arg("arg2"), 200, "text/plain")); } @@ -62,7 +62,7 @@ class simple_resource : public http_resource class args_resource : public http_resource { public: - const shared_ptr render_GET(http_request& req) + const shared_ptr render_GET(const http_request& req) { return shared_ptr(new string_response(req.get_arg("arg") + req.get_arg("arg2"), 200, "text/plain")); } @@ -71,7 +71,7 @@ class args_resource : public http_resource class long_content_resource : public http_resource { public: - const shared_ptr render_GET(http_request& req) + const shared_ptr render_GET(const http_request& req) { return shared_ptr(new string_response(lorem_ipsum, 200, "text/plain")); } @@ -80,7 +80,7 @@ class long_content_resource : public http_resource class header_set_test_resource : public http_resource { public: - const shared_ptr render_GET(http_request& req) + const shared_ptr render_GET(const http_request& req) { shared_ptr hrb(new string_response("OK", 200, "text/plain")); hrb->with_header("KEY", "VALUE"); @@ -91,7 +91,7 @@ class header_set_test_resource : public http_resource class cookie_set_test_resource : public http_resource { public: - const shared_ptr render_GET(http_request& req) + const shared_ptr render_GET(const http_request& req) { shared_ptr hrb(new string_response("OK", 200, "text/plain")); hrb->with_cookie("MyCookie", "CookieValue"); @@ -102,7 +102,7 @@ class cookie_set_test_resource : public http_resource class cookie_reading_resource : public http_resource { public: - const shared_ptr render_GET(http_request& req) + const shared_ptr render_GET(const http_request& req) { return shared_ptr(new string_response(req.get_cookie("name"), 200, "text/plain")); } @@ -111,7 +111,7 @@ class cookie_reading_resource : public http_resource class header_reading_resource : public http_resource { public: - const shared_ptr render_GET(http_request& req) + const shared_ptr render_GET(const http_request& req) { return shared_ptr(new string_response(req.get_header("MyHeader"), 200, "text/plain")); } @@ -120,23 +120,23 @@ class header_reading_resource : public http_resource class complete_test_resource : public http_resource { public: - const shared_ptr render_GET(http_request& req) + const shared_ptr render_GET(const http_request& req) { return shared_ptr(new string_response("OK", 200, "text/plain")); } - const shared_ptr render_POST(http_request& req) + const shared_ptr render_POST(const http_request& req) { return shared_ptr(new string_response("OK", 200, "text/plain")); } - const shared_ptr render_PUT(http_request& req) + const shared_ptr render_PUT(const http_request& req) { return shared_ptr(new string_response("OK", 200, "text/plain")); } - const shared_ptr render_DELETE(http_request& req) + const shared_ptr render_DELETE(const http_request& req) { return shared_ptr(new string_response("OK", 200, "text/plain")); } - const shared_ptr render_CONNECT(http_request& req) + const shared_ptr render_CONNECT(const http_request& req) { return shared_ptr(new string_response("OK", 200, "text/plain")); } @@ -145,7 +145,7 @@ class complete_test_resource : public http_resource class only_render_resource : public http_resource { public: - const shared_ptr render(http_request& req) + const shared_ptr render(const http_request& req) { return shared_ptr(new string_response("OK", 200, "text/plain")); } @@ -154,7 +154,7 @@ class only_render_resource : public http_resource class ok_resource : public http_resource { public: - const shared_ptr render_GET(http_request& req) + const shared_ptr render_GET(const http_request& req) { return shared_ptr(new string_response("OK", 200, "text/plain")); } @@ -163,7 +163,7 @@ class ok_resource : public http_resource class nok_resource : public http_resource { public: - const shared_ptr render_GET(http_request& req) + const shared_ptr render_GET(const http_request& req) { return shared_ptr(new string_response("NOK", 200, "text/plain")); } @@ -172,7 +172,7 @@ class nok_resource : public http_resource class no_response_resource : public http_resource { public: - const shared_ptr render_GET(http_request& req) + const shared_ptr render_GET(const http_request& req) { return shared_ptr(new http_response()); } @@ -181,7 +181,7 @@ class no_response_resource : public http_resource class file_response_resource : public http_resource { public: - const shared_ptr render_GET(http_request& req) + const shared_ptr render_GET(const http_request& req) { return shared_ptr(new file_response("test_content", 200, "text/plain")); } @@ -190,7 +190,7 @@ class file_response_resource : public http_resource class exception_resource : public http_resource { public: - const shared_ptr render_GET(http_request& req) + const shared_ptr render_GET(const http_request& req) { throw std::domain_error("invalid"); } @@ -199,7 +199,7 @@ class exception_resource : public http_resource class error_resource : public http_resource { public: - const shared_ptr render_GET(http_request& req) + const shared_ptr render_GET(const http_request& req) { throw "invalid"; } @@ -213,7 +213,7 @@ class print_request_resource : public http_resource this->ss = ss; } - const shared_ptr render_GET(http_request& req) + const shared_ptr render_GET(const http_request& req) { (*ss) << req; return shared_ptr(new string_response("OK", 200, "text/plain")); @@ -231,7 +231,7 @@ class print_response_resource : public http_resource this->ss = ss; } - const shared_ptr render_GET(http_request& req) + const shared_ptr render_GET(const http_request& req) { shared_ptr hresp(new string_response("OK", 200, "text/plain")); diff --git a/test/integ/deferred.cpp b/test/integ/deferred.cpp index fa1dadfa..c853ab06 100644 --- a/test/integ/deferred.cpp +++ b/test/integ/deferred.cpp @@ -65,7 +65,7 @@ ssize_t test_callback (char* buf, size_t max) class deferred_resource : public http_resource { public: - const shared_ptr render_GET(http_request& req) + const shared_ptr render_GET(const http_request& req) { return shared_ptr(new deferred_response(test_callback, "cycle callback response")); } diff --git a/test/integ/threaded.cpp b/test/integ/threaded.cpp index cd1793b7..14cb4c35 100644 --- a/test/integ/threaded.cpp +++ b/test/integ/threaded.cpp @@ -30,7 +30,7 @@ using namespace std; class ok_resource : public http_resource { public: - const shared_ptr render_GET(http_request& req) + const shared_ptr render_GET(const http_request& req) { return shared_ptr(new string_response("OK", 200, "text/plain")); } diff --git a/test/integ/ws_start_stop.cpp b/test/integ/ws_start_stop.cpp index 3515f4fc..ad8652dd 100644 --- a/test/integ/ws_start_stop.cpp +++ b/test/integ/ws_start_stop.cpp @@ -47,18 +47,18 @@ size_t writefunc(void *ptr, size_t size, size_t nmemb, std::string *s) class ok_resource : public http_resource { public: - const shared_ptr render_GET(http_request& req) + const shared_ptr render_GET(const http_request& req) { return shared_ptr(new string_response("OK", 200, "text/plain")); } }; -const shared_ptr not_found_custom(http_request& req) +const shared_ptr not_found_custom(const http_request& req) { return shared_ptr(new string_response("Not found custom", 404, "text/plain")); } -const shared_ptr not_allowed_custom(http_request& req) +const shared_ptr not_allowed_custom(const http_request& req) { return shared_ptr(new string_response("Not allowed custom", 405, "text/plain")); } From bae1774e757acc8f7fe754018c0c20246a348370 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Mon, 28 Jan 2019 02:32:28 +0000 Subject: [PATCH 432/623] Removed unused methods --- src/httpserver/http_request.hpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/httpserver/http_request.hpp b/src/httpserver/http_request.hpp index 9ae77ccd..21f2176f 100644 --- a/src/httpserver/http_request.hpp +++ b/src/httpserver/http_request.hpp @@ -338,8 +338,6 @@ class http_request std::min(size, content_size_limit)); } - void check_or_fill_args(); - /** * Method used to set the content of the request * @param content The content to set. From a7841146d1393f7013f516316f5cec12dcb63e85 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 27 Jan 2019 19:22:05 -0800 Subject: [PATCH 433/623] Remove redundant constructors --- src/httpserver/http_request.hpp | 44 --------------------------------- 1 file changed, 44 deletions(-) diff --git a/src/httpserver/http_request.hpp b/src/httpserver/http_request.hpp index a177e15e..21f2176f 100644 --- a/src/httpserver/http_request.hpp +++ b/src/httpserver/http_request.hpp @@ -292,50 +292,6 @@ class http_request return *this; } - http_request(http_request&& b) noexcept: - path(std::move(b.path)), - method(std::move(b.method)), - post_path(std::move(b.post_path)), - args(std::move(b.args)), - content(std::move(b.content)), - content_size_limit(b.content_size_limit), - version(std::move(b.version)), - underlying_connection(std::move(b.underlying_connection)) - { - } - - http_request& operator=(const http_request& b) - { - if (this == &b) return *this; - - this->path = b.path; - this->method = b.method; - this->post_path = b.post_path; - this->args = b.args; - this->content = b.content; - this->content_size_limit = b.content_size_limit; - this->version = b.version; - this->underlying_connection = b.underlying_connection; - - return *this; - } - - http_request& operator=(http_request&& b) - { - if (this == &b) return *this; - - this->path = std::move(b.path); - this->method = std::move(b.method); - this->post_path = std::move(b.post_path); - this->args = std::move(b.args); - this->content = std::move(b.content); - this->content_size_limit = b.content_size_limit; - this->version = std::move(b.version); - this->underlying_connection = std::move(b.underlying_connection); - - return *this; - } - std::string path; std::string method; std::vector post_path; From cf3b8d2e19e2b4d20cd40f87a4da9fd1ee56e9fa Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Mon, 28 Jan 2019 04:06:13 +0000 Subject: [PATCH 434/623] Align documentation to the interface --- README.md | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index ee50eb42..7b91eb97 100644 --- a/README.md +++ b/README.md @@ -542,23 +542,23 @@ The `http_request` class has a set of methods you will have access to when imple * _**const std::vector\&** get_path_pieces() **const**:_ Returns the components of the path requested by the HTTP client (each piece of the path split by `'/'`. * _**const std::string&** get_path_piece(int index) **const**:_ Returns one piece of the path requested by the HTTP client. The piece is selected through the `index` parameter (0-indexed). * _**const std::string&** get_method() **const**:_ Returns the method requested by the HTTP client. -* _**const std::string&** get_header(**const std::string&** key) **const**:_ Returns the header with name equal to `key` if present in the HTTP request. Returns an `empty string` otherwise. -* _**const std::string&** get_cookie(**const std::string&** key) **const**:_ Returns the cookie with name equal to `key` if present in the HTTP request. Returns an `empty string` otherwise. -* _**const std::string&** get_footer(**const std::string&** key) **const**:_ Returns the footer with name equal to `key` if present in the HTTP request (only for http 1.1 chunked encodings). Returns an `empty string` otherwise. -* _**const std::string&** get_arg(**const std::string&** key) **const**:_ Returns the argument with name equal to `key` if present in the HTTP request. Arguments can be (1) querystring parameters, (2) path argument (in case of parametric endpoint, (3) parameters parsed from the HTTP request body if the body is in `application/x-www-form-urlencoded` or `multipart/form-data` formats and the postprocessor is enabled in the webserver (enabled by default). -* _**const std::map&** get_headers() **const**:_ Returns a map containing all the headers present in the HTTP request. -* _**const std::map&** get_cookies() **const**:_ Returns a map containing all the cookies present in the HTTP request. -* _**const std::map&** get_footers() **const**:_ Returns a map containing all the footers present in the HTTP request (only for http 1.1 chunked encodings). -* _**const std::map&** get_args() **const**:_ Returns all the arguments present in the HTTP request. Arguments can be (1) querystring parameters, (2) path argument (in case of parametric endpoint, (3) parameters parsed from the HTTP request body if the body is in `application/x-www-form-urlencoded` or `multipart/form-data` formats and the postprocessor is enabled in the webserver (enabled by default). +* _**const std::string** get_header(**const std::string&** key) **const**:_ Returns the header with name equal to `key` if present in the HTTP request. Returns an `empty string` otherwise. +* _**const std::string** get_cookie(**const std::string&** key) **const**:_ Returns the cookie with name equal to `key` if present in the HTTP request. Returns an `empty string` otherwise. +* _**const std::string** get_footer(**const std::string&** key) **const**:_ Returns the footer with name equal to `key` if present in the HTTP request (only for http 1.1 chunked encodings). Returns an `empty string` otherwise. +* _**const std::string** get_arg(**const std::string&** key) **const**:_ Returns the argument with name equal to `key` if present in the HTTP request. Arguments can be (1) querystring parameters, (2) path argument (in case of parametric endpoint, (3) parameters parsed from the HTTP request body if the body is in `application/x-www-form-urlencoded` or `multipart/form-data` formats and the postprocessor is enabled in the webserver (enabled by default). +* _**const std::map** get_headers() **const**:_ Returns a map containing all the headers present in the HTTP request. +* _**const std::map** get_cookies() **const**:_ Returns a map containing all the cookies present in the HTTP request. +* _**const std::map** get_footers() **const**:_ Returns a map containing all the footers present in the HTTP request (only for http 1.1 chunked encodings). +* _**const std::map** get_args() **const**:_ Returns all the arguments present in the HTTP request. Arguments can be (1) querystring parameters, (2) path argument (in case of parametric endpoint, (3) parameters parsed from the HTTP request body if the body is in `application/x-www-form-urlencoded` or `multipart/form-data` formats and the postprocessor is enabled in the webserver (enabled by default). * _**const std::string&** get_content() **const**:_ Returns the body of the HTTP request. * _**bool** content_too_large() **const**:_ Returns `true` if the body length of the HTTP request sent by the client is longer than the max allowed on the server. -* _**const std::string&** get_querystring() **const**:_ Returns the `querystring` of the HTTP request. +* _**const std::string** get_querystring() **const**:_ Returns the `querystring` of the HTTP request. * _**const std::string&** get_version() **const**:_ Returns the HTTP version of the client request. -* _**const std::string&** get_requestor() **const**:_ Returns the IP from which the client is sending the request. +* _**const std::string** get_requestor() **const**:_ Returns the IP from which the client is sending the request. * _**unsigned short** get_requestor_port() **const**:_ Returns the port from which the client is sending the request. -* _**const std::string&** get_user() **const**:_ Returns the `user` as self-identified through basic authentication. The content of the user header will be parsed only if basic authentication is enabled on the server (enabled by default). -* _**const std::string&** get_pass() **const**:_ Returns the `password` as self-identified through basic authentication. The content of the password header will be parsed only if basic authentication is enabled on the server (enabled by default). -* _**const std::string&** get_digested_user() **const**:_ Returns the `digested user` as self-identified through digest authentication. The content of the user header will be parsed only if digest authentication is enabled on the server (enabled by default). +* _**const std::string** get_user() **const**:_ Returns the `user` as self-identified through basic authentication. The content of the user header will be parsed only if basic authentication is enabled on the server (enabled by default). +* _**const std::string** get_pass() **const**:_ Returns the `password` as self-identified through basic authentication. The content of the password header will be parsed only if basic authentication is enabled on the server (enabled by default). +* _**const std::string** get_digested_user() **const**:_ Returns the `digested user` as self-identified through digest authentication. The content of the user header will be parsed only if digest authentication is enabled on the server (enabled by default). * _**bool** check_digest_auth(**const std::string&** realm, **const std::string&** password, **int** nonce_timeout, **bool&** reload_nonce) **const**:_ Allows to check the validity of the authentication token sent through digest authentication (if the provided values in the WWW-Authenticate header are valid and sound according to RFC2716). Takes in input the `realm` of validity of the authentication, the `password` as known to the server to compare against, the `nonce_timeout` to indicate how long the nonce is valid and `reload_nonce` a boolean that will be set by the method to indicate a nonce being reloaded. The method returns `true` if the authentication is valid, `false` otherwise. #### Example of handler reading arguments from a request From cb0b1a63068e2d4ea423849e45fb271e38767468 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Mon, 28 Jan 2019 04:25:42 +0000 Subject: [PATCH 435/623] Additional tests on http_request methods --- test/integ/basic.cpp | 54 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/test/integ/basic.cpp b/test/integ/basic.cpp index 27b99977..69a08bfd 100644 --- a/test/integ/basic.cpp +++ b/test/integ/basic.cpp @@ -117,6 +117,24 @@ class header_reading_resource : public http_resource } }; +class full_args_resource : public http_resource +{ + public: + const shared_ptr render_GET(const http_request& req) + { + return shared_ptr(new string_response(req.get_args().at("arg"), 200, "text/plain")); + } +}; + +class querystring_resource : public http_resource +{ + public: + const shared_ptr render_GET(const http_request& req) + { + return shared_ptr(new string_response(req.get_querystring(), 200, "text/plain")); + } +}; + class complete_test_resource : public http_resource { public: @@ -705,6 +723,42 @@ LT_BEGIN_AUTO_TEST(basic_suite, querystring_processing) curl_easy_cleanup(curl); LT_END_AUTO_TEST(querystring_processing) +LT_BEGIN_AUTO_TEST(basic_suite, full_arguments_processing) + full_args_resource resource; + ws->register_resource("this/captures/args/passed/in/the/querystring", &resource); + curl_global_init(CURL_GLOBAL_ALL); + + std::string s; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/this/captures/args/passed/in/the/querystring?arg=argument"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "argument"); + curl_easy_cleanup(curl); +LT_END_AUTO_TEST(full_arguments_processing) + +LT_BEGIN_AUTO_TEST(basic_suite, querystring_query_processing) + querystring_resource resource; + ws->register_resource("this/captures/args/passed/in/the/querystring", &resource); + curl_global_init(CURL_GLOBAL_ALL); + + std::string s; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/this/captures/args/passed/in/the/querystring?arg1=value1&arg2=value2&arg3=value3"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "?arg1=value1&arg2=value2&arg3=value3"); + curl_easy_cleanup(curl); +LT_END_AUTO_TEST(querystring_query_processing) + LT_BEGIN_AUTO_TEST(basic_suite, register_unregister) simple_resource resource; ws->register_resource("base", &resource); From ead9ccc4ddbbfe7b15ab4cd97928b455218ab179 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Mon, 28 Jan 2019 06:02:39 +0000 Subject: [PATCH 436/623] Updated version and changelog --- ChangeLog | 9 +++++++++ configure.ac | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index e7adb3b0..6a0b5e05 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,12 @@ +Sat Jan 27 21:59:11 2018 -0800 + libhttpserver now includes set of examples to demonstrate the main capabilities of the library + "examples" are now optionally disabled. + Adds valgrind memcheck to the build system on travis + Travis now tests performance with apache benchmark + Reduced the CPU time spent in normalizing URLs (thus saving ~15% on average per request). + All classes now implement move constructor and move assignment operator + The library now avoids collecting connection properties (headers, arguments, footers, cookies, etc...) unless explicitly asked by the client code. + Sat Jan 12 00:51:00 2018 -0800 Removed the support for integrated COMET logic. Removed the support for caching logic. diff --git a/configure.ac b/configure.ac index 987bb9f0..7e53d670 100644 --- a/configure.ac +++ b/configure.ac @@ -22,7 +22,7 @@ AC_PREREQ(2.57) m4_define([libhttpserver_MAJOR_VERSION],[0])dnl m4_define([libhttpserver_MINOR_VERSION],[17])dnl -m4_define([libhttpserver_REVISION],[0])dnl +m4_define([libhttpserver_REVISION],[5])dnl m4_define([libhttpserver_PKG_VERSION],[libhttpserver_MAJOR_VERSION.libhttpserver_MINOR_VERSION.libhttpserver_REVISION])dnl m4_define([libhttpserver_LDF_VERSION],[libhttpserver_MAJOR_VERSION:libhttpserver_MINOR_VERSION:libhttpserver_REVISION])dnl AC_INIT([libhttpserver], libhttpserver_PKG_VERSION, [electrictwister2000@gmail.com]) From e52ed01bc610134864950a7e1186e82a57107a8b Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Mon, 11 Feb 2019 01:16:04 +0000 Subject: [PATCH 437/623] Added closure_data to deferred_response --- README.md | 90 +++++++++++++++++++++++----- examples/Makefile.am | 3 +- examples/minimal_deferred.cpp | 4 +- src/deferred_response.cpp | 32 +--------- src/httpserver/deferred_response.hpp | 46 +++++++++++--- test/integ/deferred.cpp | 58 +++++++++++++++++- 6 files changed, 174 insertions(+), 59 deletions(-) diff --git a/README.md b/README.md index 7b91eb97..e6781f86 100644 --- a/README.md +++ b/README.md @@ -601,10 +601,10 @@ There are 5 types of response that you can create - we will describe them here t * _file_response(**const std::string&** filename, **int** response_code = `200`, **const std::string&** content_type = `"text/plain"`):_ Uses the `filename` passed in construction as pointer to a file on disk. The body of the HTTP response will be set using the content of the file. The other two optional parameters are the `response_code` and the `content_type`. You can find constant definition for the various response codes within the [http_utils](https://github.com/etr/libhttpserver/blob/master/src/httpserver/http_utils.hpp) library file. * _basic_auth_fail_response(**const std::string&** content, **const std::string&** realm = `""`, **int** response_code = `200`, **const std::string&** content_type = `"text/plain"`):_ A response in return to a failure during basic authentication. It allows to specify a `content` string as a message to send back to the client. The `realm` parameter should contain your realm of authentication (if any). The other two optional parameters are the `response_code` and the `content_type`. You can find constant definition for the various response codes within the [http_utils](https://github.com/etr/libhttpserver/blob/master/src/httpserver/http_utils.hpp) library file. * _digest_auth_fail_response(**const std::string&** content, **const std::string&** realm = `""`, **const std::string&** opaque = `""`, **bool** reload_nonce = `false`, **int** response_code = `200`, **const std::string&** content_type = `"text/plain"`):_ A response in return to a failure during digest authentication. It allows to specify a `content` string as a message to send back to the client. The `realm` parameter should contain your realm of authentication (if any). The `opaque` represents a value that gets passed to the client and expected to be passed again to the server as-is. This value can be a hexadecimal or base64 string. The `reload_nonce` parameter tells the server to reload the nonce (you should use the value returned by the `check_digest_auth` method on the `http_request`. The other two optional parameters are the `response_code` and the `content_type`. You can find constant definition for the various response codes within the [http_utils](https://github.com/etr/libhttpserver/blob/master/src/httpserver/http_utils.hpp) library file. -* _deferred_response(**ssize_t(*cycle_callback_ptr)(char*, size_t)** cycle_callback, **const std::string&** content = `""`, **int** response_code = `200`, **const std::string&** content_type = `"text/plain"`):_ A response that obtains additional content from a callback executed in a deferred way. It leaves the client in pending state (returning a `100 CONTINUE` message) and suspends the connection. Besides the callback, optionally, you can provide a `content` parameter that sets the initial message sent immediately to the client. The other two optional parameters are the `response_code` and the `content_type`. You can find constant definition for the various response codes within the [http_utils](https://github.com/etr/libhttpserver/blob/master/src/httpserver/http_utils.hpp) library file. To use `deferred_response` you need to have the `deferred` option active on your webserver (enabled by default). +* _deferred_response(**ssize_t(*cycle_callback_ptr)(shared_ptr, char*, size_t)** cycle_callback, **const std::string&** content = `""`, **int** response_code = `200`, **const std::string&** content_type = `"text/plain"`):_ A response that obtains additional content from a callback executed in a deferred way. It leaves the client in pending state (returning a `100 CONTINUE` message) and suspends the connection. Besides the callback, optionally, you can provide a `content` parameter that sets the initial message sent immediately to the client. The other two optional parameters are the `response_code` and the `content_type`. You can find constant definition for the various response codes within the [http_utils](https://github.com/etr/libhttpserver/blob/master/src/httpserver/http_utils.hpp) library file. To use `deferred_response` you need to have the `deferred` option active on your webserver (enabled by default). * The `cycle_callback_ptr` has this shape: - _**ssize_t** cycle_callback(**char*** buf, **size_t** max_size)_. - You are supposed to implement a function in this shape and provide it to the `deferred_repsonse` method. The webserver will provide a `char*` to the function. It is responsibility of the function to allocate it and fill its content. The method is supposed to respect the `max_size` parameter passed in input. The function must return a `ssize_t` value representing the actual size you filled the `buf` with. Any value different from `-1` will keep the resume the connection, deliver the content and suspend it again (with a `100 CONTINUE`). If the method returns `-1`, the webserver will complete the communication with the client and close the connection. + _**ssize_t** cycle_callback(**shared_ptr closure_data, char*** buf, **size_t** max_size)_. + You are supposed to implement a function in this shape and provide it to the `deferred_repsonse` method. The webserver will provide a `char*` to the function. It is responsibility of the function to allocate it and fill its content. The method is supposed to respect the `max_size` parameter passed in input. The function must return a `ssize_t` value representing the actual size you filled the `buf` with. Any value different from `-1` will keep the resume the connection, deliver the content and suspend it again (with a `100 CONTINUE`). If the method returns `-1`, the webserver will complete the communication with the client and close the connection. You can also pass a `shared_ptr` pointing to a data object of your choice (this will be templetized with a class of your choice). The server will guarantee that this object is passed at each invocation of the method allowing the client code to use it as a memory buffer during computation. ### Setting additional properties of the response The `http_response` class offers an additional set of methods to "decorate" your responses. This set of methods is: @@ -825,36 +825,37 @@ You can also check this example on [github](https://github.com/etr/libhttpserver #### Example of a deferred response through callback #include - + using namespace httpserver; - + static int counter = 0; - - ssize_t test_callback (char* buf, size_t max) { + + ssize_t test_callback (std::shared_ptr closure_data, char* buf, size_t max) { if (counter == 2) { return -1; - } else { + } + else { memset(buf, 0, max); strcat(buf, " test "); counter++; return std::string(buf).size(); } } - + class deferred_resource : public http_resource { - public: - const std::shared_ptr render_GET(const http_request& req) { - return std::shared_ptr(new deferred_response(test_callback, "cycle callback response")); - } + public: + const std::shared_ptr render_GET(const http_request& req) { + return std::shared_ptr >(new deferred_response(test_callback, nullptr, "cycle callback response")); + } }; - + int main(int argc, char** argv) { webserver ws = create_webserver(8080); - + deferred_resource hwr; ws.register_resource("/hello", &hwr); ws.start(true); - + return 0; } @@ -864,6 +865,63 @@ To test the above example, you can run the following command from a terminal: You can also check this example on [github](https://github.com/etr/libhttpserver/blob/master/examples/minimal_deferred.cpp). +#### Example of a deferred response through callback (passing additional data along) + #include + #include + + using namespace httpserver; + + std::atomic counter; + + ssize_t test_callback (std::shared_ptr > closure_data, char* buf, size_t max) { + int reqid; + if (closure_data == nullptr) { + reqid = -1; + } else { + reqid = *closure_data; + } + + // only first 5 connections can be established + if (reqid >= 5) { + return -1; + } else { + // respond corresponding request IDs to the clients + std::string str = ""; + str += std::to_string(reqid) + " "; + memset(buf, 0, max); + std::copy(str.begin(), str.end(), buf); + + // keep sending reqid + sleep(1); + + return (ssize_t)max; + } + } + + class deferred_resource : public http_resource { + public: + const std::shared_ptr render_GET(const http_request& req) { + std::shared_ptr > closure_data(new std::atomic(counter++)); + return std::shared_ptr > >(new deferred_response >(test_callback, closure_data, "cycle callback response")); + } + }; + + int main(int argc, char** argv) { + webserver ws = create_webserver(8080); + + deferred_resource hwr; + ws.register_resource("/hello", &hwr); + ws.start(true); + + return 0; + } + +To test the above example, you can run the following command from a terminal: + + curl -XGET -v localhost:8080/hello + +You can also check this example on [github](https://github.com/etr/libhttpserver/blob/master/examples/deferred_with_accumulator.cpp). + [Back to TOC](#table-of-contents) ## Copying diff --git a/examples/Makefile.am b/examples/Makefile.am index ee879c70..137c071c 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -19,7 +19,7 @@ LDADD = $(top_builddir)/src/libhttpserver.la AM_CPPFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/src/httpserver/ METASOURCES = AUTO -noinst_PROGRAMS = hello_world service minimal_hello_world custom_error allowing_disallowing_methods handlers hello_with_get_arg setting_headers custom_access_log basic_authentication digest_authentication minimal_https minimal_file_response minimal_deferred url_registration minimal_ip_ban benchmark_select benchmark_threads +noinst_PROGRAMS = hello_world service minimal_hello_world custom_error allowing_disallowing_methods handlers hello_with_get_arg setting_headers custom_access_log basic_authentication digest_authentication minimal_https minimal_file_response minimal_deferred url_registration minimal_ip_ban benchmark_select benchmark_threads deferred_with_accumulator hello_world_SOURCES = hello_world.cpp service_SOURCES = service.cpp @@ -35,6 +35,7 @@ digest_authentication_SOURCES = digest_authentication.cpp minimal_https_SOURCES = minimal_https.cpp minimal_file_response_SOURCES = minimal_file_response.cpp minimal_deferred_SOURCES = minimal_deferred.cpp +deferred_with_accumulator_SOURCES = deferred_with_accumulator.cpp url_registration_SOURCES = url_registration.cpp minimal_ip_ban_SOURCES = minimal_ip_ban.cpp benchmark_select_SOURCES = benchmark_select.cpp diff --git a/examples/minimal_deferred.cpp b/examples/minimal_deferred.cpp index a08599c2..a7a3e51d 100644 --- a/examples/minimal_deferred.cpp +++ b/examples/minimal_deferred.cpp @@ -24,7 +24,7 @@ using namespace httpserver; static int counter = 0; -ssize_t test_callback (char* buf, size_t max) { +ssize_t test_callback (std::shared_ptr closure_data, char* buf, size_t max) { if (counter == 2) { return -1; } @@ -39,7 +39,7 @@ ssize_t test_callback (char* buf, size_t max) { class deferred_resource : public http_resource { public: const std::shared_ptr render_GET(const http_request& req) { - return std::shared_ptr(new deferred_response(test_callback, "cycle callback response")); + return std::shared_ptr >(new deferred_response(test_callback, nullptr, "cycle callback response")); } }; diff --git a/src/deferred_response.cpp b/src/deferred_response.cpp index fe17cb29..6c4fa087 100644 --- a/src/deferred_response.cpp +++ b/src/deferred_response.cpp @@ -28,44 +28,18 @@ namespace httpserver namespace details { -ssize_t cb(void* cls, uint64_t pos, char* buf, size_t max) -{ - ssize_t val = static_cast(cls)->cycle_callback(buf, max); - if(val == -1) - { - static_cast(cls)->completed = true; - } - - return val; -} - -} - -MHD_Response* deferred_response::get_raw_response() +MHD_Response* get_raw_response_helper(void* cls, bool completed, ssize_t (*cb)(void*, uint64_t, char*, size_t)) { if(!completed) { - return MHD_create_response_from_callback( - MHD_SIZE_UNKNOWN, - 1024, - &details::cb, - this, - NULL - ); + return MHD_create_response_from_callback(MHD_SIZE_UNKNOWN, 1024, cb, cls, NULL); } else { - return static_cast(this)->get_raw_response(); + return static_cast(cls)->get_raw_response(); } } -void deferred_response::decorate_response(MHD_Response* response) -{ - if(completed) - { - static_cast(this)->decorate_response(response); - } } } - diff --git a/src/httpserver/deferred_response.hpp b/src/httpserver/deferred_response.hpp index c5a9f1ff..7c965ac0 100644 --- a/src/httpserver/deferred_response.hpp +++ b/src/httpserver/deferred_response.hpp @@ -25,6 +25,7 @@ #ifndef _DEFERRED_RESPONSE_HPP_ #define _DEFERRED_RESPONSE_HPP_ +#include #include "httpserver/string_response.hpp" namespace httpserver @@ -32,22 +33,23 @@ namespace httpserver namespace details { - ssize_t cb(void*, uint64_t, char*, size_t); -}; - -typedef ssize_t(*cycle_callback_ptr)(char*, size_t); + MHD_Response* get_raw_response_helper(void* cls, bool completed, ssize_t (*cb)(void*, uint64_t, char*, size_t)); +} +template class deferred_response : public string_response { public: explicit deferred_response( - cycle_callback_ptr cycle_callback, + ssize_t(*cycle_callback)(std::shared_ptr, char*, size_t), + std::shared_ptr closure_data, const std::string& content = "", int response_code = http::http_utils::http_ok, const std::string& content_type = http::http_utils::text_plain ): string_response(content, response_code, content_type), cycle_callback(cycle_callback), + closure_data(closure_data), completed(false) { } @@ -55,6 +57,7 @@ class deferred_response : public string_response deferred_response(const deferred_response& other): string_response(other), cycle_callback(other.cycle_callback), + closure_data(other.closure_data), completed(other.completed) { } @@ -62,6 +65,7 @@ class deferred_response : public string_response deferred_response(deferred_response&& other) noexcept: string_response(std::move(other)), cycle_callback(std::move(other.cycle_callback)), + closure_data(std::move(other.closure_data)), completed(other.completed) { } @@ -72,6 +76,7 @@ class deferred_response : public string_response (string_response&) (*this) = b; this->cycle_callback = b.cycle_callback; + this->closure_data = b.closure_data; this->completed = b.completed; return *this; @@ -83,6 +88,7 @@ class deferred_response : public string_response (string_response&) (*this) = std::move(b); this->cycle_callback = std::move(b.cycle_callback); + this->closure_data = std::move(b.closure_data); this->completed = b.completed; return *this; @@ -92,13 +98,35 @@ class deferred_response : public string_response { } - MHD_Response* get_raw_response(); - void decorate_response(MHD_Response* response); + MHD_Response* get_raw_response() + { + return details::get_raw_response_helper((void*) this, completed, &(this->cb)); + } + + void decorate_response(MHD_Response* response) + { + if(completed) + { + static_cast(this)->decorate_response(response); + } + } + private: - cycle_callback_ptr cycle_callback; + ssize_t (*cycle_callback)(std::shared_ptr, char*, size_t); + std::shared_ptr closure_data; bool completed; - friend ssize_t details::cb(void* cls, uint64_t pos, char* buf, size_t max); + static ssize_t cb(void* cls, uint64_t pos, char* buf, size_t max) + { + deferred_response* dfr = static_cast*>(cls); + ssize_t val = dfr->cycle_callback(dfr->closure_data, buf, max); + if(val == -1) + { + dfr->completed = true; + } + + return val; + } }; } diff --git a/test/integ/deferred.cpp b/test/integ/deferred.cpp index c853ab06..b846ee0c 100644 --- a/test/integ/deferred.cpp +++ b/test/integ/deferred.cpp @@ -47,7 +47,12 @@ size_t writefunc(void *ptr, size_t size, size_t nmemb, std::string *s) static int counter = 0; -ssize_t test_callback (char* buf, size_t max) +struct test_data +{ + int value; +}; + +ssize_t test_callback(std::shared_ptr closure_data, char* buf, size_t max) { if (counter == 2) { @@ -62,12 +67,41 @@ ssize_t test_callback (char* buf, size_t max) } } +ssize_t test_callback_with_data(std::shared_ptr closure_data, char* buf, size_t max) +{ + if (counter == 2) + { + return -1; + } + else + { + memset(buf, 0, max); + strcat(buf, ("test" + std::to_string(closure_data->value)).c_str()); + + closure_data->value = 84; + + counter++; + return std::string(buf).size(); + } +} + class deferred_resource : public http_resource { public: const shared_ptr render_GET(const http_request& req) { - return shared_ptr(new deferred_response(test_callback, "cycle callback response")); + return shared_ptr>(new deferred_response(test_callback, nullptr, "cycle callback response")); + } +}; + +class deferred_resource_with_data : public http_resource +{ + public: + const shared_ptr render_GET(const http_request& req) + { + std::shared_ptr internal_info(new test_data); + internal_info->value = 42; + return shared_ptr>(new deferred_response(test_callback_with_data, internal_info, "cycle callback response")); } }; @@ -82,6 +116,8 @@ LT_BEGIN_SUITE(deferred_suite) void tear_down() { + counter = 0; + ws->stop(); delete ws; } @@ -105,6 +141,24 @@ LT_BEGIN_AUTO_TEST(deferred_suite, deferred_response) curl_easy_cleanup(curl); LT_END_AUTO_TEST(deferred_response) +LT_BEGIN_AUTO_TEST(deferred_suite, deferred_response_with_data) + deferred_resource_with_data resource; + ws->register_resource("base", &resource); + curl_global_init(CURL_GLOBAL_ALL); + + std::string s; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "test42test84"); + curl_easy_cleanup(curl); +LT_END_AUTO_TEST(deferred_response_with_data) + LT_BEGIN_AUTO_TEST_ENV() AUTORUN_TESTS() LT_END_AUTO_TEST_ENV() From cd9839e1830db5f6f221113fd3f8c1297b051899 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Mon, 11 Feb 2019 01:19:23 +0000 Subject: [PATCH 438/623] Added example file --- examples/deferred_with_accumulator.cpp | 70 ++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 examples/deferred_with_accumulator.cpp diff --git a/examples/deferred_with_accumulator.cpp b/examples/deferred_with_accumulator.cpp new file mode 100644 index 00000000..80a9cae8 --- /dev/null +++ b/examples/deferred_with_accumulator.cpp @@ -0,0 +1,70 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#include +#include + +using namespace httpserver; + +std::atomic counter; + +ssize_t test_callback (std::shared_ptr > closure_data, char* buf, size_t max) { + int reqid; + if (closure_data == nullptr) { + reqid = -1; + } else { + reqid = *closure_data; + } + + // only first 5 connections can be established + if (reqid >= 5) { + return -1; + } else { + // respond corresponding request IDs to the clients + std::string str = ""; + str += std::to_string(reqid) + " "; + memset(buf, 0, max); + std::copy(str.begin(), str.end(), buf); + + // keep sending reqid + sleep(1); + + return (ssize_t)max; + } +} + +class deferred_resource : public http_resource { + public: + const std::shared_ptr render_GET(const http_request& req) { + std::shared_ptr > closure_data(new std::atomic(counter++)); + return std::shared_ptr > >(new deferred_response >(test_callback, closure_data, "cycle callback response")); + } +}; + +int main(int argc, char** argv) { + webserver ws = create_webserver(8080); + + deferred_resource hwr; + ws.register_resource("/hello", &hwr); + ws.start(true); + + return 0; +} + From a7a52ce8fd87b7b12e3db6c16845737b48af59c2 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Mon, 11 Feb 2019 01:26:06 +0000 Subject: [PATCH 439/623] Fix documentation --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e6781f86..825850e5 100644 --- a/README.md +++ b/README.md @@ -601,9 +601,9 @@ There are 5 types of response that you can create - we will describe them here t * _file_response(**const std::string&** filename, **int** response_code = `200`, **const std::string&** content_type = `"text/plain"`):_ Uses the `filename` passed in construction as pointer to a file on disk. The body of the HTTP response will be set using the content of the file. The other two optional parameters are the `response_code` and the `content_type`. You can find constant definition for the various response codes within the [http_utils](https://github.com/etr/libhttpserver/blob/master/src/httpserver/http_utils.hpp) library file. * _basic_auth_fail_response(**const std::string&** content, **const std::string&** realm = `""`, **int** response_code = `200`, **const std::string&** content_type = `"text/plain"`):_ A response in return to a failure during basic authentication. It allows to specify a `content` string as a message to send back to the client. The `realm` parameter should contain your realm of authentication (if any). The other two optional parameters are the `response_code` and the `content_type`. You can find constant definition for the various response codes within the [http_utils](https://github.com/etr/libhttpserver/blob/master/src/httpserver/http_utils.hpp) library file. * _digest_auth_fail_response(**const std::string&** content, **const std::string&** realm = `""`, **const std::string&** opaque = `""`, **bool** reload_nonce = `false`, **int** response_code = `200`, **const std::string&** content_type = `"text/plain"`):_ A response in return to a failure during digest authentication. It allows to specify a `content` string as a message to send back to the client. The `realm` parameter should contain your realm of authentication (if any). The `opaque` represents a value that gets passed to the client and expected to be passed again to the server as-is. This value can be a hexadecimal or base64 string. The `reload_nonce` parameter tells the server to reload the nonce (you should use the value returned by the `check_digest_auth` method on the `http_request`. The other two optional parameters are the `response_code` and the `content_type`. You can find constant definition for the various response codes within the [http_utils](https://github.com/etr/libhttpserver/blob/master/src/httpserver/http_utils.hpp) library file. -* _deferred_response(**ssize_t(*cycle_callback_ptr)(shared_ptr, char*, size_t)** cycle_callback, **const std::string&** content = `""`, **int** response_code = `200`, **const std::string&** content_type = `"text/plain"`):_ A response that obtains additional content from a callback executed in a deferred way. It leaves the client in pending state (returning a `100 CONTINUE` message) and suspends the connection. Besides the callback, optionally, you can provide a `content` parameter that sets the initial message sent immediately to the client. The other two optional parameters are the `response_code` and the `content_type`. You can find constant definition for the various response codes within the [http_utils](https://github.com/etr/libhttpserver/blob/master/src/httpserver/http_utils.hpp) library file. To use `deferred_response` you need to have the `deferred` option active on your webserver (enabled by default). +* _deferred_response(**ssize_t(*cycle_callback_ptr)(shared_ptr<T>, char*, size_t)** cycle_callback, **const std::string&** content = `""`, **int** response_code = `200`, **const std::string&** content_type = `"text/plain"`):_ A response that obtains additional content from a callback executed in a deferred way. It leaves the client in pending state (returning a `100 CONTINUE` message) and suspends the connection. Besides the callback, optionally, you can provide a `content` parameter that sets the initial message sent immediately to the client. The other two optional parameters are the `response_code` and the `content_type`. You can find constant definition for the various response codes within the [http_utils](https://github.com/etr/libhttpserver/blob/master/src/httpserver/http_utils.hpp) library file. To use `deferred_response` you need to have the `deferred` option active on your webserver (enabled by default). * The `cycle_callback_ptr` has this shape: - _**ssize_t** cycle_callback(**shared_ptr closure_data, char*** buf, **size_t** max_size)_. + _**ssize_t** cycle_callback(**shared_ptr<T> closure_data, char*** buf, **size_t** max_size)_. You are supposed to implement a function in this shape and provide it to the `deferred_repsonse` method. The webserver will provide a `char*` to the function. It is responsibility of the function to allocate it and fill its content. The method is supposed to respect the `max_size` parameter passed in input. The function must return a `ssize_t` value representing the actual size you filled the `buf` with. Any value different from `-1` will keep the resume the connection, deliver the content and suspend it again (with a `100 CONTINUE`). If the method returns `-1`, the webserver will complete the communication with the client and close the connection. You can also pass a `shared_ptr` pointing to a data object of your choice (this will be templetized with a class of your choice). The server will guarantee that this object is passed at each invocation of the method allowing the client code to use it as a memory buffer during computation. ### Setting additional properties of the response From ed0841d177d88dada6e21e4dd5c36f8b670c1ee0 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Mon, 11 Feb 2019 02:52:35 +0000 Subject: [PATCH 440/623] Remove completed flag from code --- src/deferred_response.cpp | 11 ++-------- src/httpserver/deferred_response.hpp | 32 ++++++---------------------- 2 files changed, 8 insertions(+), 35 deletions(-) diff --git a/src/deferred_response.cpp b/src/deferred_response.cpp index 6c4fa087..c1547962 100644 --- a/src/deferred_response.cpp +++ b/src/deferred_response.cpp @@ -28,16 +28,9 @@ namespace httpserver namespace details { -MHD_Response* get_raw_response_helper(void* cls, bool completed, ssize_t (*cb)(void*, uint64_t, char*, size_t)) +MHD_Response* get_raw_response_helper(void* cls, ssize_t (*cb)(void*, uint64_t, char*, size_t)) { - if(!completed) - { - return MHD_create_response_from_callback(MHD_SIZE_UNKNOWN, 1024, cb, cls, NULL); - } - else - { - return static_cast(cls)->get_raw_response(); - } + return MHD_create_response_from_callback(MHD_SIZE_UNKNOWN, 1024, cb, cls, NULL); } } diff --git a/src/httpserver/deferred_response.hpp b/src/httpserver/deferred_response.hpp index 7c965ac0..02cc745d 100644 --- a/src/httpserver/deferred_response.hpp +++ b/src/httpserver/deferred_response.hpp @@ -33,7 +33,7 @@ namespace httpserver namespace details { - MHD_Response* get_raw_response_helper(void* cls, bool completed, ssize_t (*cb)(void*, uint64_t, char*, size_t)); + MHD_Response* get_raw_response_helper(void* cls, ssize_t (*cb)(void*, uint64_t, char*, size_t)); } template @@ -49,24 +49,21 @@ class deferred_response : public string_response ): string_response(content, response_code, content_type), cycle_callback(cycle_callback), - closure_data(closure_data), - completed(false) + closure_data(closure_data) { } deferred_response(const deferred_response& other): string_response(other), cycle_callback(other.cycle_callback), - closure_data(other.closure_data), - completed(other.completed) + closure_data(other.closure_data) { } deferred_response(deferred_response&& other) noexcept: string_response(std::move(other)), cycle_callback(std::move(other.cycle_callback)), - closure_data(std::move(other.closure_data)), - completed(other.completed) + closure_data(std::move(other.closure_data)) { } @@ -77,7 +74,6 @@ class deferred_response : public string_response (string_response&) (*this) = b; this->cycle_callback = b.cycle_callback; this->closure_data = b.closure_data; - this->completed = b.completed; return *this; } @@ -89,7 +85,6 @@ class deferred_response : public string_response (string_response&) (*this) = std::move(b); this->cycle_callback = std::move(b.cycle_callback); this->closure_data = std::move(b.closure_data); - this->completed = b.completed; return *this; } @@ -100,32 +95,17 @@ class deferred_response : public string_response MHD_Response* get_raw_response() { - return details::get_raw_response_helper((void*) this, completed, &(this->cb)); - } - - void decorate_response(MHD_Response* response) - { - if(completed) - { - static_cast(this)->decorate_response(response); - } + return details::get_raw_response_helper((void*) this, &(this->cb)); } private: ssize_t (*cycle_callback)(std::shared_ptr, char*, size_t); std::shared_ptr closure_data; - bool completed; static ssize_t cb(void* cls, uint64_t pos, char* buf, size_t max) { deferred_response* dfr = static_cast*>(cls); - ssize_t val = dfr->cycle_callback(dfr->closure_data, buf, max); - if(val == -1) - { - dfr->completed = true; - } - - return val; + return dfr->cycle_callback(dfr->closure_data, buf, max); } }; From 176d9323db1ec949b2bda7c94128bf7baca53b11 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Wed, 13 Feb 2019 21:51:20 -0800 Subject: [PATCH 441/623] Remove unused cleaner and select --- src/httpserver/webserver.hpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/httpserver/webserver.hpp b/src/httpserver/webserver.hpp index 2660c703..5745fe69 100644 --- a/src/httpserver/webserver.hpp +++ b/src/httpserver/webserver.hpp @@ -189,9 +189,6 @@ class webserver struct MHD_Daemon* daemon; - static void* select(void* self); - static void* cleaner(void* self); - const std::shared_ptr method_not_allowed_page(details::modded_request* mr) const; const std::shared_ptr internal_error_page(details::modded_request* mr, bool force_our = false) const; const std::shared_ptr not_found_page(details::modded_request* mr) const; From 0b1e6ac1093419710116256fc49805b4c9bfa5d5 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sat, 8 Jun 2019 15:14:45 -0700 Subject: [PATCH 442/623] Fix segfault when a port is already occupied --- src/webserver.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/webserver.cpp b/src/webserver.cpp index e71c3648..6b1ffc28 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -325,7 +325,7 @@ bool webserver::start(bool blocking) if(this->daemon == NULL) { - throw std::invalid_argument("Unable to connect daemon to port: " + this->port); + throw std::invalid_argument("Unable to connect daemon to port: " + std::to_string(this->port)); } bool value_onclose = false; From 85fc04d561ba9fb33330aea02c307a7c7a7c0fda Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sat, 8 Jun 2019 15:36:06 -0700 Subject: [PATCH 443/623] Fix lib expectations in libhttpserver.pc.in --- libhttpserver.pc.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libhttpserver.pc.in b/libhttpserver.pc.in index 36355eea..55f7c0e2 100644 --- a/libhttpserver.pc.in +++ b/libhttpserver.pc.in @@ -8,6 +8,6 @@ Description: A C++ library for creating an embedded Rest HTTP server Version: @VERSION@ Requires: libmicrohttpd >= 0.9.37 Conflicts: -Libs: -L${libdir} -lmicrohttpd +Libs: -L${libdir} -lhttpserver Libs.private: @LHT_LIBDEPS@ Cflags: -I${includedir} -I${includedir}/httpserver From 208f1887cffa8fcdc7fa9eae32226dc66b6b1742 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Wed, 7 Aug 2019 05:05:54 +0000 Subject: [PATCH 444/623] Fixed method DELETE to parse the body optionally --- src/webserver.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/webserver.cpp b/src/webserver.cpp index e71c3648..13a0642d 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -787,6 +787,7 @@ int webserver::answer_to_connection(void* cls, MHD_Connection* connection, else if (0 == strcasecmp(method,http_utils::http_method_delete.c_str())) { mr->callback = &http_resource::render_DELETE; + body = true; } else if (0 == strcasecmp(method, http_utils::http_method_head.c_str())) { From c91e2417cce02c259250e81494539f7848ac117a Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Wed, 7 Aug 2019 05:20:37 +0000 Subject: [PATCH 445/623] Add support for PATCH method --- src/http_resource.cpp | 1 + src/http_utils.cpp | 1 + src/httpserver/http_resource.hpp | 9 +++++++++ src/httpserver/http_utils.hpp | 2 ++ src/webserver.cpp | 5 +++++ test/integ/basic.cpp | 12 ++++++++++++ 6 files changed, 30 insertions(+) diff --git a/src/http_resource.cpp b/src/http_resource.cpp index a9ded860..2fe71a78 100644 --- a/src/http_resource.cpp +++ b/src/http_resource.cpp @@ -43,6 +43,7 @@ void resource_init(map& allowed_methods) allowed_methods[MHD_HTTP_METHOD_TRACE] = true; allowed_methods[MHD_HTTP_METHOD_CONNECT] = true; allowed_methods[MHD_HTTP_METHOD_OPTIONS] = true; + allowed_methods[MHD_HTTP_METHOD_PATCH] = true; } namespace details diff --git a/src/http_utils.cpp b/src/http_utils.cpp index 726b217a..0d00a05f 100644 --- a/src/http_utils.cpp +++ b/src/http_utils.cpp @@ -205,6 +205,7 @@ const std::string http_utils::http_method_options = MHD_HTTP_METHOD_OPTIONS; const std::string http_utils::http_method_post = MHD_HTTP_METHOD_POST; const std::string http_utils::http_method_put = MHD_HTTP_METHOD_PUT; const std::string http_utils::http_method_trace = MHD_HTTP_METHOD_TRACE; +const std::string http_utils::http_method_patch = MHD_HTTP_METHOD_PATCH; const std::string http_utils::http_post_encoding_form_urlencoded = MHD_HTTP_POST_ENCODING_FORM_URLENCODED; diff --git a/src/httpserver/http_resource.hpp b/src/httpserver/http_resource.hpp index faaa3463..ae5e78d4 100644 --- a/src/httpserver/http_resource.hpp +++ b/src/httpserver/http_resource.hpp @@ -136,6 +136,15 @@ class http_resource { return render(req); } + /** + * Method used to answer to a PATCH request + * @param req Request passed through http + * @return A http_response object + **/ + virtual const std::shared_ptr render_PATCH(const http_request& req) + { + return render(req); + } /** * Method used to answer to a CONNECT request * @param req Request passed through http diff --git a/src/httpserver/http_utils.hpp b/src/httpserver/http_utils.hpp index bd3df177..28f1d77f 100644 --- a/src/httpserver/http_utils.hpp +++ b/src/httpserver/http_utils.hpp @@ -102,6 +102,7 @@ class http_utils static const short http_method_post_code; static const short http_method_put_code; static const short http_method_trace_code; + static const short http_method_patch_code; static const short http_method_unknown_code; static const int http_continue; @@ -224,6 +225,7 @@ class http_utils static const std::string http_method_post; static const std::string http_method_put; static const std::string http_method_trace; + static const std::string http_method_patch; static const std::string http_post_encoding_form_urlencoded; static const std::string http_post_encoding_multipart_formdata; diff --git a/src/webserver.cpp b/src/webserver.cpp index 13a0642d..91f5fd0c 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -789,6 +789,11 @@ int webserver::answer_to_connection(void* cls, MHD_Connection* connection, mr->callback = &http_resource::render_DELETE; body = true; } + else if (0 == strcasecmp(method, http_utils::http_method_patch.c_str())) + { + mr->callback = &http_resource::render_PATCH; + body = true; + } else if (0 == strcasecmp(method, http_utils::http_method_head.c_str())) { mr->callback = &http_resource::render_HEAD; diff --git a/test/integ/basic.cpp b/test/integ/basic.cpp index 69a08bfd..c1b1156f 100644 --- a/test/integ/basic.cpp +++ b/test/integ/basic.cpp @@ -158,6 +158,10 @@ class complete_test_resource : public http_resource { return shared_ptr(new string_response("OK", 200, "text/plain")); } + const shared_ptr render_PATCH(const http_request& req) + { + return shared_ptr(new string_response("OK", 200, "text/plain")); + } }; class only_render_resource : public http_resource @@ -483,6 +487,14 @@ LT_BEGIN_AUTO_TEST(basic_suite, complete) curl_easy_cleanup(curl); } + { + CURL* curl = curl_easy_init(); + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PATCH"); + CURLcode res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + curl_easy_cleanup(curl); + } /* { CURL* curl = curl_easy_init(); From 1b48091ee3a142812f272be172ae909fadbbfd6a Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Wed, 7 Aug 2019 05:23:06 +0000 Subject: [PATCH 446/623] Update changelog --- ChangeLog | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/ChangeLog b/ChangeLog index 6a0b5e05..6b3f6929 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,4 +1,8 @@ -Sat Jan 27 21:59:11 2018 -0800 +Tue Aug 06 22:22:14 2019 -0800 + Added support for body parsing in DELETE requests. + Added support for PATCH method + +Sat Jan 27 21:59:11 2019 -0800 libhttpserver now includes set of examples to demonstrate the main capabilities of the library "examples" are now optionally disabled. Adds valgrind memcheck to the build system on travis @@ -7,7 +11,7 @@ Sat Jan 27 21:59:11 2018 -0800 All classes now implement move constructor and move assignment operator The library now avoids collecting connection properties (headers, arguments, footers, cookies, etc...) unless explicitly asked by the client code. -Sat Jan 12 00:51:00 2018 -0800 +Sat Jan 12 00:51:00 2019 -0800 Removed the support for integrated COMET logic. Removed the support for caching logic. Added integ tests. From 1f1f867e4e92748a7b9f3619551aa5e07cd53004 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Wed, 7 Aug 2019 09:14:50 -0700 Subject: [PATCH 447/623] Fixing compiler package for clang Due to travis having moved to xenial, some builds are failing (as they are using the wrong toolchain). This attempts to fix. --- .travis.yml | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/.travis.yml b/.travis.yml index 68f2d125..49180545 100644 --- a/.travis.yml +++ b/.travis.yml @@ -288,8 +288,6 @@ matrix: - os: linux addons: apt: - sources: - - llvm-toolchain-trusty-4.0 packages: - clang-4.0 env: @@ -298,8 +296,6 @@ matrix: - os: linux addons: apt: - sources: - - llvm-toolchain-trusty-5.0 packages: - clang-5.0 env: @@ -307,9 +303,6 @@ matrix: - os: linux addons: apt: - sources: - - llvm-toolchain-trusty-6.0 - - ubuntu-toolchain-r-test packages: - clang-6.0 env: @@ -318,12 +311,22 @@ matrix: addons: apt: sources: - - llvm-toolchain-trusty-7 + - llvm-toolchain-xenial-7 - ubuntu-toolchain-r-test packages: - clang-7 env: - MATRIX_EVAL="CC=clang-7 && CXX=clang++-7" + - os: linux + addons: + apt: + sources: + - llvm-toolchain-xenial-8 + - ubuntu-toolchain-r-test + packages: + - clang-8 + env: + - MATRIX_EVAL="CC=clang-8 && CXX=clang++-8" - os: linux compiler: clang addons: From cd8b3c92f9a8f62ff74138527287fc93619478ad Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Mon, 11 Feb 2019 01:16:04 +0000 Subject: [PATCH 448/623] Added closure_data to deferred_response --- README.md | 90 +++++++++++++++++++++++----- examples/Makefile.am | 3 +- examples/minimal_deferred.cpp | 4 +- src/deferred_response.cpp | 32 +--------- src/httpserver/deferred_response.hpp | 46 +++++++++++--- test/integ/deferred.cpp | 58 +++++++++++++++++- 6 files changed, 174 insertions(+), 59 deletions(-) diff --git a/README.md b/README.md index 7b91eb97..e6781f86 100644 --- a/README.md +++ b/README.md @@ -601,10 +601,10 @@ There are 5 types of response that you can create - we will describe them here t * _file_response(**const std::string&** filename, **int** response_code = `200`, **const std::string&** content_type = `"text/plain"`):_ Uses the `filename` passed in construction as pointer to a file on disk. The body of the HTTP response will be set using the content of the file. The other two optional parameters are the `response_code` and the `content_type`. You can find constant definition for the various response codes within the [http_utils](https://github.com/etr/libhttpserver/blob/master/src/httpserver/http_utils.hpp) library file. * _basic_auth_fail_response(**const std::string&** content, **const std::string&** realm = `""`, **int** response_code = `200`, **const std::string&** content_type = `"text/plain"`):_ A response in return to a failure during basic authentication. It allows to specify a `content` string as a message to send back to the client. The `realm` parameter should contain your realm of authentication (if any). The other two optional parameters are the `response_code` and the `content_type`. You can find constant definition for the various response codes within the [http_utils](https://github.com/etr/libhttpserver/blob/master/src/httpserver/http_utils.hpp) library file. * _digest_auth_fail_response(**const std::string&** content, **const std::string&** realm = `""`, **const std::string&** opaque = `""`, **bool** reload_nonce = `false`, **int** response_code = `200`, **const std::string&** content_type = `"text/plain"`):_ A response in return to a failure during digest authentication. It allows to specify a `content` string as a message to send back to the client. The `realm` parameter should contain your realm of authentication (if any). The `opaque` represents a value that gets passed to the client and expected to be passed again to the server as-is. This value can be a hexadecimal or base64 string. The `reload_nonce` parameter tells the server to reload the nonce (you should use the value returned by the `check_digest_auth` method on the `http_request`. The other two optional parameters are the `response_code` and the `content_type`. You can find constant definition for the various response codes within the [http_utils](https://github.com/etr/libhttpserver/blob/master/src/httpserver/http_utils.hpp) library file. -* _deferred_response(**ssize_t(*cycle_callback_ptr)(char*, size_t)** cycle_callback, **const std::string&** content = `""`, **int** response_code = `200`, **const std::string&** content_type = `"text/plain"`):_ A response that obtains additional content from a callback executed in a deferred way. It leaves the client in pending state (returning a `100 CONTINUE` message) and suspends the connection. Besides the callback, optionally, you can provide a `content` parameter that sets the initial message sent immediately to the client. The other two optional parameters are the `response_code` and the `content_type`. You can find constant definition for the various response codes within the [http_utils](https://github.com/etr/libhttpserver/blob/master/src/httpserver/http_utils.hpp) library file. To use `deferred_response` you need to have the `deferred` option active on your webserver (enabled by default). +* _deferred_response(**ssize_t(*cycle_callback_ptr)(shared_ptr, char*, size_t)** cycle_callback, **const std::string&** content = `""`, **int** response_code = `200`, **const std::string&** content_type = `"text/plain"`):_ A response that obtains additional content from a callback executed in a deferred way. It leaves the client in pending state (returning a `100 CONTINUE` message) and suspends the connection. Besides the callback, optionally, you can provide a `content` parameter that sets the initial message sent immediately to the client. The other two optional parameters are the `response_code` and the `content_type`. You can find constant definition for the various response codes within the [http_utils](https://github.com/etr/libhttpserver/blob/master/src/httpserver/http_utils.hpp) library file. To use `deferred_response` you need to have the `deferred` option active on your webserver (enabled by default). * The `cycle_callback_ptr` has this shape: - _**ssize_t** cycle_callback(**char*** buf, **size_t** max_size)_. - You are supposed to implement a function in this shape and provide it to the `deferred_repsonse` method. The webserver will provide a `char*` to the function. It is responsibility of the function to allocate it and fill its content. The method is supposed to respect the `max_size` parameter passed in input. The function must return a `ssize_t` value representing the actual size you filled the `buf` with. Any value different from `-1` will keep the resume the connection, deliver the content and suspend it again (with a `100 CONTINUE`). If the method returns `-1`, the webserver will complete the communication with the client and close the connection. + _**ssize_t** cycle_callback(**shared_ptr closure_data, char*** buf, **size_t** max_size)_. + You are supposed to implement a function in this shape and provide it to the `deferred_repsonse` method. The webserver will provide a `char*` to the function. It is responsibility of the function to allocate it and fill its content. The method is supposed to respect the `max_size` parameter passed in input. The function must return a `ssize_t` value representing the actual size you filled the `buf` with. Any value different from `-1` will keep the resume the connection, deliver the content and suspend it again (with a `100 CONTINUE`). If the method returns `-1`, the webserver will complete the communication with the client and close the connection. You can also pass a `shared_ptr` pointing to a data object of your choice (this will be templetized with a class of your choice). The server will guarantee that this object is passed at each invocation of the method allowing the client code to use it as a memory buffer during computation. ### Setting additional properties of the response The `http_response` class offers an additional set of methods to "decorate" your responses. This set of methods is: @@ -825,36 +825,37 @@ You can also check this example on [github](https://github.com/etr/libhttpserver #### Example of a deferred response through callback #include - + using namespace httpserver; - + static int counter = 0; - - ssize_t test_callback (char* buf, size_t max) { + + ssize_t test_callback (std::shared_ptr closure_data, char* buf, size_t max) { if (counter == 2) { return -1; - } else { + } + else { memset(buf, 0, max); strcat(buf, " test "); counter++; return std::string(buf).size(); } } - + class deferred_resource : public http_resource { - public: - const std::shared_ptr render_GET(const http_request& req) { - return std::shared_ptr(new deferred_response(test_callback, "cycle callback response")); - } + public: + const std::shared_ptr render_GET(const http_request& req) { + return std::shared_ptr >(new deferred_response(test_callback, nullptr, "cycle callback response")); + } }; - + int main(int argc, char** argv) { webserver ws = create_webserver(8080); - + deferred_resource hwr; ws.register_resource("/hello", &hwr); ws.start(true); - + return 0; } @@ -864,6 +865,63 @@ To test the above example, you can run the following command from a terminal: You can also check this example on [github](https://github.com/etr/libhttpserver/blob/master/examples/minimal_deferred.cpp). +#### Example of a deferred response through callback (passing additional data along) + #include + #include + + using namespace httpserver; + + std::atomic counter; + + ssize_t test_callback (std::shared_ptr > closure_data, char* buf, size_t max) { + int reqid; + if (closure_data == nullptr) { + reqid = -1; + } else { + reqid = *closure_data; + } + + // only first 5 connections can be established + if (reqid >= 5) { + return -1; + } else { + // respond corresponding request IDs to the clients + std::string str = ""; + str += std::to_string(reqid) + " "; + memset(buf, 0, max); + std::copy(str.begin(), str.end(), buf); + + // keep sending reqid + sleep(1); + + return (ssize_t)max; + } + } + + class deferred_resource : public http_resource { + public: + const std::shared_ptr render_GET(const http_request& req) { + std::shared_ptr > closure_data(new std::atomic(counter++)); + return std::shared_ptr > >(new deferred_response >(test_callback, closure_data, "cycle callback response")); + } + }; + + int main(int argc, char** argv) { + webserver ws = create_webserver(8080); + + deferred_resource hwr; + ws.register_resource("/hello", &hwr); + ws.start(true); + + return 0; + } + +To test the above example, you can run the following command from a terminal: + + curl -XGET -v localhost:8080/hello + +You can also check this example on [github](https://github.com/etr/libhttpserver/blob/master/examples/deferred_with_accumulator.cpp). + [Back to TOC](#table-of-contents) ## Copying diff --git a/examples/Makefile.am b/examples/Makefile.am index ee879c70..137c071c 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -19,7 +19,7 @@ LDADD = $(top_builddir)/src/libhttpserver.la AM_CPPFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/src/httpserver/ METASOURCES = AUTO -noinst_PROGRAMS = hello_world service minimal_hello_world custom_error allowing_disallowing_methods handlers hello_with_get_arg setting_headers custom_access_log basic_authentication digest_authentication minimal_https minimal_file_response minimal_deferred url_registration minimal_ip_ban benchmark_select benchmark_threads +noinst_PROGRAMS = hello_world service minimal_hello_world custom_error allowing_disallowing_methods handlers hello_with_get_arg setting_headers custom_access_log basic_authentication digest_authentication minimal_https minimal_file_response minimal_deferred url_registration minimal_ip_ban benchmark_select benchmark_threads deferred_with_accumulator hello_world_SOURCES = hello_world.cpp service_SOURCES = service.cpp @@ -35,6 +35,7 @@ digest_authentication_SOURCES = digest_authentication.cpp minimal_https_SOURCES = minimal_https.cpp minimal_file_response_SOURCES = minimal_file_response.cpp minimal_deferred_SOURCES = minimal_deferred.cpp +deferred_with_accumulator_SOURCES = deferred_with_accumulator.cpp url_registration_SOURCES = url_registration.cpp minimal_ip_ban_SOURCES = minimal_ip_ban.cpp benchmark_select_SOURCES = benchmark_select.cpp diff --git a/examples/minimal_deferred.cpp b/examples/minimal_deferred.cpp index a08599c2..a7a3e51d 100644 --- a/examples/minimal_deferred.cpp +++ b/examples/minimal_deferred.cpp @@ -24,7 +24,7 @@ using namespace httpserver; static int counter = 0; -ssize_t test_callback (char* buf, size_t max) { +ssize_t test_callback (std::shared_ptr closure_data, char* buf, size_t max) { if (counter == 2) { return -1; } @@ -39,7 +39,7 @@ ssize_t test_callback (char* buf, size_t max) { class deferred_resource : public http_resource { public: const std::shared_ptr render_GET(const http_request& req) { - return std::shared_ptr(new deferred_response(test_callback, "cycle callback response")); + return std::shared_ptr >(new deferred_response(test_callback, nullptr, "cycle callback response")); } }; diff --git a/src/deferred_response.cpp b/src/deferred_response.cpp index fe17cb29..6c4fa087 100644 --- a/src/deferred_response.cpp +++ b/src/deferred_response.cpp @@ -28,44 +28,18 @@ namespace httpserver namespace details { -ssize_t cb(void* cls, uint64_t pos, char* buf, size_t max) -{ - ssize_t val = static_cast(cls)->cycle_callback(buf, max); - if(val == -1) - { - static_cast(cls)->completed = true; - } - - return val; -} - -} - -MHD_Response* deferred_response::get_raw_response() +MHD_Response* get_raw_response_helper(void* cls, bool completed, ssize_t (*cb)(void*, uint64_t, char*, size_t)) { if(!completed) { - return MHD_create_response_from_callback( - MHD_SIZE_UNKNOWN, - 1024, - &details::cb, - this, - NULL - ); + return MHD_create_response_from_callback(MHD_SIZE_UNKNOWN, 1024, cb, cls, NULL); } else { - return static_cast(this)->get_raw_response(); + return static_cast(cls)->get_raw_response(); } } -void deferred_response::decorate_response(MHD_Response* response) -{ - if(completed) - { - static_cast(this)->decorate_response(response); - } } } - diff --git a/src/httpserver/deferred_response.hpp b/src/httpserver/deferred_response.hpp index c5a9f1ff..7c965ac0 100644 --- a/src/httpserver/deferred_response.hpp +++ b/src/httpserver/deferred_response.hpp @@ -25,6 +25,7 @@ #ifndef _DEFERRED_RESPONSE_HPP_ #define _DEFERRED_RESPONSE_HPP_ +#include #include "httpserver/string_response.hpp" namespace httpserver @@ -32,22 +33,23 @@ namespace httpserver namespace details { - ssize_t cb(void*, uint64_t, char*, size_t); -}; - -typedef ssize_t(*cycle_callback_ptr)(char*, size_t); + MHD_Response* get_raw_response_helper(void* cls, bool completed, ssize_t (*cb)(void*, uint64_t, char*, size_t)); +} +template class deferred_response : public string_response { public: explicit deferred_response( - cycle_callback_ptr cycle_callback, + ssize_t(*cycle_callback)(std::shared_ptr, char*, size_t), + std::shared_ptr closure_data, const std::string& content = "", int response_code = http::http_utils::http_ok, const std::string& content_type = http::http_utils::text_plain ): string_response(content, response_code, content_type), cycle_callback(cycle_callback), + closure_data(closure_data), completed(false) { } @@ -55,6 +57,7 @@ class deferred_response : public string_response deferred_response(const deferred_response& other): string_response(other), cycle_callback(other.cycle_callback), + closure_data(other.closure_data), completed(other.completed) { } @@ -62,6 +65,7 @@ class deferred_response : public string_response deferred_response(deferred_response&& other) noexcept: string_response(std::move(other)), cycle_callback(std::move(other.cycle_callback)), + closure_data(std::move(other.closure_data)), completed(other.completed) { } @@ -72,6 +76,7 @@ class deferred_response : public string_response (string_response&) (*this) = b; this->cycle_callback = b.cycle_callback; + this->closure_data = b.closure_data; this->completed = b.completed; return *this; @@ -83,6 +88,7 @@ class deferred_response : public string_response (string_response&) (*this) = std::move(b); this->cycle_callback = std::move(b.cycle_callback); + this->closure_data = std::move(b.closure_data); this->completed = b.completed; return *this; @@ -92,13 +98,35 @@ class deferred_response : public string_response { } - MHD_Response* get_raw_response(); - void decorate_response(MHD_Response* response); + MHD_Response* get_raw_response() + { + return details::get_raw_response_helper((void*) this, completed, &(this->cb)); + } + + void decorate_response(MHD_Response* response) + { + if(completed) + { + static_cast(this)->decorate_response(response); + } + } + private: - cycle_callback_ptr cycle_callback; + ssize_t (*cycle_callback)(std::shared_ptr, char*, size_t); + std::shared_ptr closure_data; bool completed; - friend ssize_t details::cb(void* cls, uint64_t pos, char* buf, size_t max); + static ssize_t cb(void* cls, uint64_t pos, char* buf, size_t max) + { + deferred_response* dfr = static_cast*>(cls); + ssize_t val = dfr->cycle_callback(dfr->closure_data, buf, max); + if(val == -1) + { + dfr->completed = true; + } + + return val; + } }; } diff --git a/test/integ/deferred.cpp b/test/integ/deferred.cpp index c853ab06..b846ee0c 100644 --- a/test/integ/deferred.cpp +++ b/test/integ/deferred.cpp @@ -47,7 +47,12 @@ size_t writefunc(void *ptr, size_t size, size_t nmemb, std::string *s) static int counter = 0; -ssize_t test_callback (char* buf, size_t max) +struct test_data +{ + int value; +}; + +ssize_t test_callback(std::shared_ptr closure_data, char* buf, size_t max) { if (counter == 2) { @@ -62,12 +67,41 @@ ssize_t test_callback (char* buf, size_t max) } } +ssize_t test_callback_with_data(std::shared_ptr closure_data, char* buf, size_t max) +{ + if (counter == 2) + { + return -1; + } + else + { + memset(buf, 0, max); + strcat(buf, ("test" + std::to_string(closure_data->value)).c_str()); + + closure_data->value = 84; + + counter++; + return std::string(buf).size(); + } +} + class deferred_resource : public http_resource { public: const shared_ptr render_GET(const http_request& req) { - return shared_ptr(new deferred_response(test_callback, "cycle callback response")); + return shared_ptr>(new deferred_response(test_callback, nullptr, "cycle callback response")); + } +}; + +class deferred_resource_with_data : public http_resource +{ + public: + const shared_ptr render_GET(const http_request& req) + { + std::shared_ptr internal_info(new test_data); + internal_info->value = 42; + return shared_ptr>(new deferred_response(test_callback_with_data, internal_info, "cycle callback response")); } }; @@ -82,6 +116,8 @@ LT_BEGIN_SUITE(deferred_suite) void tear_down() { + counter = 0; + ws->stop(); delete ws; } @@ -105,6 +141,24 @@ LT_BEGIN_AUTO_TEST(deferred_suite, deferred_response) curl_easy_cleanup(curl); LT_END_AUTO_TEST(deferred_response) +LT_BEGIN_AUTO_TEST(deferred_suite, deferred_response_with_data) + deferred_resource_with_data resource; + ws->register_resource("base", &resource); + curl_global_init(CURL_GLOBAL_ALL); + + std::string s; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "test42test84"); + curl_easy_cleanup(curl); +LT_END_AUTO_TEST(deferred_response_with_data) + LT_BEGIN_AUTO_TEST_ENV() AUTORUN_TESTS() LT_END_AUTO_TEST_ENV() From e50354c45de96b7e1ec3d3308a174bcdb1982645 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Mon, 11 Feb 2019 01:19:23 +0000 Subject: [PATCH 449/623] Added example file --- examples/deferred_with_accumulator.cpp | 70 ++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 examples/deferred_with_accumulator.cpp diff --git a/examples/deferred_with_accumulator.cpp b/examples/deferred_with_accumulator.cpp new file mode 100644 index 00000000..80a9cae8 --- /dev/null +++ b/examples/deferred_with_accumulator.cpp @@ -0,0 +1,70 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#include +#include + +using namespace httpserver; + +std::atomic counter; + +ssize_t test_callback (std::shared_ptr > closure_data, char* buf, size_t max) { + int reqid; + if (closure_data == nullptr) { + reqid = -1; + } else { + reqid = *closure_data; + } + + // only first 5 connections can be established + if (reqid >= 5) { + return -1; + } else { + // respond corresponding request IDs to the clients + std::string str = ""; + str += std::to_string(reqid) + " "; + memset(buf, 0, max); + std::copy(str.begin(), str.end(), buf); + + // keep sending reqid + sleep(1); + + return (ssize_t)max; + } +} + +class deferred_resource : public http_resource { + public: + const std::shared_ptr render_GET(const http_request& req) { + std::shared_ptr > closure_data(new std::atomic(counter++)); + return std::shared_ptr > >(new deferred_response >(test_callback, closure_data, "cycle callback response")); + } +}; + +int main(int argc, char** argv) { + webserver ws = create_webserver(8080); + + deferred_resource hwr; + ws.register_resource("/hello", &hwr); + ws.start(true); + + return 0; +} + From f3862def6944cd3161d0263a66ed3bc1168aac2a Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Mon, 11 Feb 2019 01:26:06 +0000 Subject: [PATCH 450/623] Fix documentation --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e6781f86..825850e5 100644 --- a/README.md +++ b/README.md @@ -601,9 +601,9 @@ There are 5 types of response that you can create - we will describe them here t * _file_response(**const std::string&** filename, **int** response_code = `200`, **const std::string&** content_type = `"text/plain"`):_ Uses the `filename` passed in construction as pointer to a file on disk. The body of the HTTP response will be set using the content of the file. The other two optional parameters are the `response_code` and the `content_type`. You can find constant definition for the various response codes within the [http_utils](https://github.com/etr/libhttpserver/blob/master/src/httpserver/http_utils.hpp) library file. * _basic_auth_fail_response(**const std::string&** content, **const std::string&** realm = `""`, **int** response_code = `200`, **const std::string&** content_type = `"text/plain"`):_ A response in return to a failure during basic authentication. It allows to specify a `content` string as a message to send back to the client. The `realm` parameter should contain your realm of authentication (if any). The other two optional parameters are the `response_code` and the `content_type`. You can find constant definition for the various response codes within the [http_utils](https://github.com/etr/libhttpserver/blob/master/src/httpserver/http_utils.hpp) library file. * _digest_auth_fail_response(**const std::string&** content, **const std::string&** realm = `""`, **const std::string&** opaque = `""`, **bool** reload_nonce = `false`, **int** response_code = `200`, **const std::string&** content_type = `"text/plain"`):_ A response in return to a failure during digest authentication. It allows to specify a `content` string as a message to send back to the client. The `realm` parameter should contain your realm of authentication (if any). The `opaque` represents a value that gets passed to the client and expected to be passed again to the server as-is. This value can be a hexadecimal or base64 string. The `reload_nonce` parameter tells the server to reload the nonce (you should use the value returned by the `check_digest_auth` method on the `http_request`. The other two optional parameters are the `response_code` and the `content_type`. You can find constant definition for the various response codes within the [http_utils](https://github.com/etr/libhttpserver/blob/master/src/httpserver/http_utils.hpp) library file. -* _deferred_response(**ssize_t(*cycle_callback_ptr)(shared_ptr, char*, size_t)** cycle_callback, **const std::string&** content = `""`, **int** response_code = `200`, **const std::string&** content_type = `"text/plain"`):_ A response that obtains additional content from a callback executed in a deferred way. It leaves the client in pending state (returning a `100 CONTINUE` message) and suspends the connection. Besides the callback, optionally, you can provide a `content` parameter that sets the initial message sent immediately to the client. The other two optional parameters are the `response_code` and the `content_type`. You can find constant definition for the various response codes within the [http_utils](https://github.com/etr/libhttpserver/blob/master/src/httpserver/http_utils.hpp) library file. To use `deferred_response` you need to have the `deferred` option active on your webserver (enabled by default). +* _deferred_response(**ssize_t(*cycle_callback_ptr)(shared_ptr<T>, char*, size_t)** cycle_callback, **const std::string&** content = `""`, **int** response_code = `200`, **const std::string&** content_type = `"text/plain"`):_ A response that obtains additional content from a callback executed in a deferred way. It leaves the client in pending state (returning a `100 CONTINUE` message) and suspends the connection. Besides the callback, optionally, you can provide a `content` parameter that sets the initial message sent immediately to the client. The other two optional parameters are the `response_code` and the `content_type`. You can find constant definition for the various response codes within the [http_utils](https://github.com/etr/libhttpserver/blob/master/src/httpserver/http_utils.hpp) library file. To use `deferred_response` you need to have the `deferred` option active on your webserver (enabled by default). * The `cycle_callback_ptr` has this shape: - _**ssize_t** cycle_callback(**shared_ptr closure_data, char*** buf, **size_t** max_size)_. + _**ssize_t** cycle_callback(**shared_ptr<T> closure_data, char*** buf, **size_t** max_size)_. You are supposed to implement a function in this shape and provide it to the `deferred_repsonse` method. The webserver will provide a `char*` to the function. It is responsibility of the function to allocate it and fill its content. The method is supposed to respect the `max_size` parameter passed in input. The function must return a `ssize_t` value representing the actual size you filled the `buf` with. Any value different from `-1` will keep the resume the connection, deliver the content and suspend it again (with a `100 CONTINUE`). If the method returns `-1`, the webserver will complete the communication with the client and close the connection. You can also pass a `shared_ptr` pointing to a data object of your choice (this will be templetized with a class of your choice). The server will guarantee that this object is passed at each invocation of the method allowing the client code to use it as a memory buffer during computation. ### Setting additional properties of the response From fb444e85c93d5f0ac451bf628a3401910f4136eb Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Mon, 11 Feb 2019 02:52:35 +0000 Subject: [PATCH 451/623] Remove completed flag from code --- src/deferred_response.cpp | 11 ++-------- src/httpserver/deferred_response.hpp | 32 ++++++---------------------- 2 files changed, 8 insertions(+), 35 deletions(-) diff --git a/src/deferred_response.cpp b/src/deferred_response.cpp index 6c4fa087..c1547962 100644 --- a/src/deferred_response.cpp +++ b/src/deferred_response.cpp @@ -28,16 +28,9 @@ namespace httpserver namespace details { -MHD_Response* get_raw_response_helper(void* cls, bool completed, ssize_t (*cb)(void*, uint64_t, char*, size_t)) +MHD_Response* get_raw_response_helper(void* cls, ssize_t (*cb)(void*, uint64_t, char*, size_t)) { - if(!completed) - { - return MHD_create_response_from_callback(MHD_SIZE_UNKNOWN, 1024, cb, cls, NULL); - } - else - { - return static_cast(cls)->get_raw_response(); - } + return MHD_create_response_from_callback(MHD_SIZE_UNKNOWN, 1024, cb, cls, NULL); } } diff --git a/src/httpserver/deferred_response.hpp b/src/httpserver/deferred_response.hpp index 7c965ac0..02cc745d 100644 --- a/src/httpserver/deferred_response.hpp +++ b/src/httpserver/deferred_response.hpp @@ -33,7 +33,7 @@ namespace httpserver namespace details { - MHD_Response* get_raw_response_helper(void* cls, bool completed, ssize_t (*cb)(void*, uint64_t, char*, size_t)); + MHD_Response* get_raw_response_helper(void* cls, ssize_t (*cb)(void*, uint64_t, char*, size_t)); } template @@ -49,24 +49,21 @@ class deferred_response : public string_response ): string_response(content, response_code, content_type), cycle_callback(cycle_callback), - closure_data(closure_data), - completed(false) + closure_data(closure_data) { } deferred_response(const deferred_response& other): string_response(other), cycle_callback(other.cycle_callback), - closure_data(other.closure_data), - completed(other.completed) + closure_data(other.closure_data) { } deferred_response(deferred_response&& other) noexcept: string_response(std::move(other)), cycle_callback(std::move(other.cycle_callback)), - closure_data(std::move(other.closure_data)), - completed(other.completed) + closure_data(std::move(other.closure_data)) { } @@ -77,7 +74,6 @@ class deferred_response : public string_response (string_response&) (*this) = b; this->cycle_callback = b.cycle_callback; this->closure_data = b.closure_data; - this->completed = b.completed; return *this; } @@ -89,7 +85,6 @@ class deferred_response : public string_response (string_response&) (*this) = std::move(b); this->cycle_callback = std::move(b.cycle_callback); this->closure_data = std::move(b.closure_data); - this->completed = b.completed; return *this; } @@ -100,32 +95,17 @@ class deferred_response : public string_response MHD_Response* get_raw_response() { - return details::get_raw_response_helper((void*) this, completed, &(this->cb)); - } - - void decorate_response(MHD_Response* response) - { - if(completed) - { - static_cast(this)->decorate_response(response); - } + return details::get_raw_response_helper((void*) this, &(this->cb)); } private: ssize_t (*cycle_callback)(std::shared_ptr, char*, size_t); std::shared_ptr closure_data; - bool completed; static ssize_t cb(void* cls, uint64_t pos, char* buf, size_t max) { deferred_response* dfr = static_cast*>(cls); - ssize_t val = dfr->cycle_callback(dfr->closure_data, buf, max); - if(val == -1) - { - dfr->completed = true; - } - - return val; + return dfr->cycle_callback(dfr->closure_data, buf, max); } }; From c4477430b0d12c6bd5ca69057be99687b4aecaba Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Wed, 13 Feb 2019 21:51:20 -0800 Subject: [PATCH 452/623] Remove unused cleaner and select --- src/httpserver/webserver.hpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/httpserver/webserver.hpp b/src/httpserver/webserver.hpp index 2660c703..5745fe69 100644 --- a/src/httpserver/webserver.hpp +++ b/src/httpserver/webserver.hpp @@ -189,9 +189,6 @@ class webserver struct MHD_Daemon* daemon; - static void* select(void* self); - static void* cleaner(void* self); - const std::shared_ptr method_not_allowed_page(details::modded_request* mr) const; const std::shared_ptr internal_error_page(details::modded_request* mr, bool force_our = false) const; const std::shared_ptr not_found_page(details::modded_request* mr) const; From 063e3d63742546a5c38f5477e1d68e745043af40 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sat, 8 Jun 2019 15:14:45 -0700 Subject: [PATCH 453/623] Fix segfault when a port is already occupied --- src/webserver.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/webserver.cpp b/src/webserver.cpp index 91f5fd0c..64d92c25 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -325,7 +325,7 @@ bool webserver::start(bool blocking) if(this->daemon == NULL) { - throw std::invalid_argument("Unable to connect daemon to port: " + this->port); + throw std::invalid_argument("Unable to connect daemon to port: " + std::to_string(this->port)); } bool value_onclose = false; From 458d16b922304006fe418897044e14b0544a127a Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sat, 8 Jun 2019 15:36:06 -0700 Subject: [PATCH 454/623] Fix lib expectations in libhttpserver.pc.in --- libhttpserver.pc.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libhttpserver.pc.in b/libhttpserver.pc.in index 36355eea..55f7c0e2 100644 --- a/libhttpserver.pc.in +++ b/libhttpserver.pc.in @@ -8,6 +8,6 @@ Description: A C++ library for creating an embedded Rest HTTP server Version: @VERSION@ Requires: libmicrohttpd >= 0.9.37 Conflicts: -Libs: -L${libdir} -lmicrohttpd +Libs: -L${libdir} -lhttpserver Libs.private: @LHT_LIBDEPS@ Cflags: -I${includedir} -I${includedir}/httpserver From 088c9d0b551069faa881c80781a683380555785c Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Wed, 7 Aug 2019 09:14:50 -0700 Subject: [PATCH 455/623] Fixing compiler package for clang Due to travis having moved to xenial, some builds are failing (as they are using the wrong toolchain). This attempts to fix. --- .travis.yml | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/.travis.yml b/.travis.yml index 68f2d125..49180545 100644 --- a/.travis.yml +++ b/.travis.yml @@ -288,8 +288,6 @@ matrix: - os: linux addons: apt: - sources: - - llvm-toolchain-trusty-4.0 packages: - clang-4.0 env: @@ -298,8 +296,6 @@ matrix: - os: linux addons: apt: - sources: - - llvm-toolchain-trusty-5.0 packages: - clang-5.0 env: @@ -307,9 +303,6 @@ matrix: - os: linux addons: apt: - sources: - - llvm-toolchain-trusty-6.0 - - ubuntu-toolchain-r-test packages: - clang-6.0 env: @@ -318,12 +311,22 @@ matrix: addons: apt: sources: - - llvm-toolchain-trusty-7 + - llvm-toolchain-xenial-7 - ubuntu-toolchain-r-test packages: - clang-7 env: - MATRIX_EVAL="CC=clang-7 && CXX=clang++-7" + - os: linux + addons: + apt: + sources: + - llvm-toolchain-xenial-8 + - ubuntu-toolchain-r-test + packages: + - clang-8 + env: + - MATRIX_EVAL="CC=clang-8 && CXX=clang++-8" - os: linux compiler: clang addons: From cb491fa5ca24dc76cd53ec699a11868a22ee79a5 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Fri, 9 Aug 2019 16:17:55 +0000 Subject: [PATCH 456/623] Removed unused parameters from webserver class --- src/httpserver/webserver.hpp | 1 - src/webserver.cpp | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/httpserver/webserver.hpp b/src/httpserver/webserver.hpp index 5745fe69..31ad5f34 100644 --- a/src/httpserver/webserver.hpp +++ b/src/httpserver/webserver.hpp @@ -183,7 +183,6 @@ class webserver std::map registered_resources; std::map registered_resources_str; - int next_to_choose; std::set bans; std::set allowances; diff --git a/src/webserver.cpp b/src/webserver.cpp index 64d92c25..cac34adb 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -151,8 +151,7 @@ webserver::webserver(const create_webserver& params): single_resource(params._single_resource), not_found_resource(params._not_found_resource), method_not_allowed_resource(params._method_not_allowed_resource), - internal_error_resource(params._internal_error_resource), - next_to_choose(0) + internal_error_resource(params._internal_error_resource) { ignore_sigpipe(); pthread_mutex_init(&mutexwait, NULL); From 5c64e1a3885527ee00fda7a3fd04c11a6d8b54b6 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Fri, 9 Aug 2019 17:42:01 +0000 Subject: [PATCH 457/623] Added support for TCP-NODELAY --- .travis.yml | 16 +++++++ examples/benchmark_nodelay.cpp | 41 ++++++++++++++++ src/httpserver/create_webserver.hpp | 11 +++++ src/httpserver/webserver.hpp | 1 + src/webserver.cpp | 14 ++++++ test/Makefile.am | 3 +- test/integ/nodelay.cpp | 74 +++++++++++++++++++++++++++++ 7 files changed, 159 insertions(+), 1 deletion(-) create mode 100755 examples/benchmark_nodelay.cpp create mode 100644 test/integ/nodelay.cpp diff --git a/.travis.yml b/.travis.yml index 49180545..9a47d329 100644 --- a/.travis.yml +++ b/.travis.yml @@ -98,6 +98,12 @@ script: ./benchmark_select 8080 $(nproc) & sleep 5 && ab -n 10000000 -c 100 localhost:8080/plaintext fi + - | + if [ "$PERFORMANCE" = "nodelay" ]; then + cd examples + ./benchmark_nodelay 8080 $(nproc) & + sleep 5 && ab -n 10000000 -c 100 localhost:8080/plaintext + fi - | if [ "$PERFORMANCE" = "threads" ]; then cd examples @@ -215,6 +221,16 @@ matrix: - apache2-utils env: - MATRIX_EVAL="CC=gcc-7 && CXX=g++-7 && PERFORMANCE=select" + - os: linux + addons: + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - g++-7 + - apache2-utils + env: + - MATRIX_EVAL="CC=gcc-7 && CXX=g++-7 && PERFORMANCE=nodelay" - os: linux addons: apt: diff --git a/examples/benchmark_nodelay.cpp b/examples/benchmark_nodelay.cpp new file mode 100755 index 00000000..a67e95bf --- /dev/null +++ b/examples/benchmark_nodelay.cpp @@ -0,0 +1,41 @@ +#include +#include +#include + +#define PATH "/plaintext" +#define BODY "Hello, World!" + +using namespace httpserver; + +class hello_world_resource : public http_resource { + public: + hello_world_resource(const std::shared_ptr& resp): + resp(resp) + { + } + + const std::shared_ptr render(const http_request&) { + return resp; + } + + private: + std::shared_ptr resp; +}; + +int main(int argc, char** argv) +{ + webserver ws = create_webserver(atoi(argv[1])) + .start_method(http::http_utils::INTERNAL_SELECT) + .tcp_nodelay() + .max_threads(atoi(argv[2])); + + std::shared_ptr hello = std::shared_ptr(new string_response(BODY, 200)); + hello->with_header("Server", "libhttpserver"); + + hello_world_resource hwr(hello); + ws.register_resource(PATH, &hwr, false); + + ws.start(true); + + return 0; +} diff --git a/src/httpserver/create_webserver.hpp b/src/httpserver/create_webserver.hpp index 6fc5d3c5..596d7b33 100644 --- a/src/httpserver/create_webserver.hpp +++ b/src/httpserver/create_webserver.hpp @@ -80,6 +80,7 @@ class create_webserver _post_process_enabled(true), _deferred_enabled(false), _single_resource(false), + _tcp_nodelay(false), _not_found_resource(0x0), _method_not_allowed_resource(0x0), _internal_error_resource(0x0) @@ -121,6 +122,7 @@ class create_webserver _post_process_enabled(b._post_process_enabled), _deferred_enabled(b._deferred_enabled), _single_resource(b._single_resource), + _tcp_nodelay(b._tcp_nodelay), _not_found_resource(b._not_found_resource), _method_not_allowed_resource(b._method_not_allowed_resource), _internal_error_resource(b._internal_error_resource) @@ -162,6 +164,7 @@ class create_webserver _post_process_enabled(b._post_process_enabled), _deferred_enabled(b._deferred_enabled), _single_resource(b._single_resource), + _tcp_nodelay(b._tcp_nodelay), _not_found_resource(std::move(b._not_found_resource)), _method_not_allowed_resource(std::move(b._method_not_allowed_resource)), _internal_error_resource(std::move(b._internal_error_resource)) @@ -206,6 +209,7 @@ class create_webserver this->_post_process_enabled = b._post_process_enabled; this->_deferred_enabled = b._deferred_enabled; this->_single_resource = b._single_resource; + this->_tcp_nodelay = b._tcp_nodelay; this->_not_found_resource = b._not_found_resource; this->_method_not_allowed_resource = b._method_not_allowed_resource; this->_internal_error_resource = b._internal_error_resource; @@ -251,6 +255,7 @@ class create_webserver this->_post_process_enabled = b._post_process_enabled; this->_deferred_enabled = b._deferred_enabled; this->_single_resource = b._single_resource; + this->_tcp_nodelay = b._tcp_nodelay; this->_not_found_resource = std::move(b._not_found_resource); this->_method_not_allowed_resource = std::move(b._method_not_allowed_resource); this->_internal_error_resource = std::move(b._internal_error_resource); @@ -293,6 +298,7 @@ class create_webserver _post_process_enabled(true), _deferred_enabled(false), _single_resource(false), + _tcp_nodelay(false), _not_found_resource(0x0), _method_not_allowed_resource(0x0), _internal_error_resource(0x0) @@ -471,6 +477,10 @@ class create_webserver { _single_resource = true; return *this; } + create_webserver& tcp_nodelay() + { + _tcp_nodelay = true; return *this; + } create_webserver& not_found_resource(render_ptr not_found_resource) { _not_found_resource = not_found_resource; return *this; @@ -524,6 +534,7 @@ class create_webserver bool _post_process_enabled; bool _deferred_enabled; bool _single_resource; + bool _tcp_nodelay; render_ptr _not_found_resource; render_ptr _method_not_allowed_resource; render_ptr _internal_error_resource; diff --git a/src/httpserver/webserver.hpp b/src/httpserver/webserver.hpp index 31ad5f34..71433965 100644 --- a/src/httpserver/webserver.hpp +++ b/src/httpserver/webserver.hpp @@ -174,6 +174,7 @@ class webserver const bool post_process_enabled; const bool deferred_enabled; bool single_resource; + bool tcp_nodelay; pthread_mutex_t mutexwait; pthread_rwlock_t runguard; pthread_cond_t mutexcond; diff --git a/src/webserver.cpp b/src/webserver.cpp index cac34adb..a0608f95 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -32,9 +32,11 @@ #if defined(__MINGW32__) || defined(__CYGWIN32__) #include +#include #define _WINDOWS #else #include +#include #endif #include @@ -149,6 +151,7 @@ webserver::webserver(const create_webserver& params): post_process_enabled(params._post_process_enabled), deferred_enabled(params._deferred_enabled), single_resource(params._single_resource), + tcp_nodelay(params._tcp_nodelay), not_found_resource(params._not_found_resource), method_not_allowed_resource(params._method_not_allowed_resource), internal_error_resource(params._internal_error_resource) @@ -757,6 +760,17 @@ int webserver::answer_to_connection(void* cls, MHD_Connection* connection, ); } + const MHD_ConnectionInfo * conninfo = MHD_get_connection_info( + connection, + MHD_CONNECTION_INFO_CONNECTION_FD + ); + + if (static_cast(cls)->tcp_nodelay) + { + int yes = 1; + setsockopt(conninfo->connect_fd, IPPROTO_TCP, TCP_NODELAY, (char *) &yes, sizeof(int)); + } + std::string t_url = url; base_unescaper(t_url, static_cast(cls)->unescaper); diff --git a/test/Makefile.am b/test/Makefile.am index a4ff3fce..e6c91b02 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -19,7 +19,7 @@ LDADD = $(top_builddir)/src/libhttpserver.la AM_CPPFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/src/httpserver/ METASOURCES = AUTO -check_PROGRAMS = basic http_utils threaded string_utilities http_endpoint ban_system ws_start_stop authentication deferred +check_PROGRAMS = basic http_utils threaded nodelay string_utilities http_endpoint ban_system ws_start_stop authentication deferred MOSTLYCLEANFILES = *.gcda *.gcno *.gcov @@ -32,6 +32,7 @@ deferred_SOURCES = integ/deferred.cpp http_utils_SOURCES = unit/http_utils_test.cpp string_utilities_SOURCES = unit/string_utilities_test.cpp http_endpoint_SOURCES = unit/http_endpoint_test.cpp +nodelay_SOURCES = integ/nodelay.cpp noinst_HEADERS = littletest.hpp AM_CXXFLAGS += -lcurl -Wall -fPIC diff --git a/test/integ/nodelay.cpp b/test/integ/nodelay.cpp new file mode 100644 index 00000000..aad199f7 --- /dev/null +++ b/test/integ/nodelay.cpp @@ -0,0 +1,74 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011-2019 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#include "littletest.hpp" +#include +#include +#include +#include "httpserver.hpp" + +using namespace httpserver; +using namespace std; + +class ok_resource : public http_resource +{ + public: + const shared_ptr render_GET(const http_request& req) + { + return shared_ptr(new string_response("OK", 200, "text/plain")); + } +}; + +LT_BEGIN_SUITE(threaded_suite) + + webserver* ws; + + void set_up() + { + ws = new webserver(create_webserver(8080).tcp_nodelay()); + ws->start(false); + } + + void tear_down() + { + ws->stop(); + delete ws; + } +LT_END_SUITE(threaded_suite) + +LT_BEGIN_AUTO_TEST(threaded_suite, base) + ok_resource resource; + ws->register_resource("base", &resource); + curl_global_init(CURL_GLOBAL_ALL); + std::string s; + CURL* curl; + CURLcode res; + + curl = curl_easy_init(); + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + curl_easy_cleanup(curl); +LT_END_AUTO_TEST(base) + +LT_BEGIN_AUTO_TEST_ENV() + AUTORUN_TESTS() +LT_END_AUTO_TEST_ENV() From f578654619181debe330ea162bbdd1e7104fcd17 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Fri, 9 Aug 2019 11:14:10 -0700 Subject: [PATCH 458/623] Add Target to compile nodelay benchmark --- examples/Makefile.am | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/Makefile.am b/examples/Makefile.am index 137c071c..318a7a8d 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -19,7 +19,7 @@ LDADD = $(top_builddir)/src/libhttpserver.la AM_CPPFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/src/httpserver/ METASOURCES = AUTO -noinst_PROGRAMS = hello_world service minimal_hello_world custom_error allowing_disallowing_methods handlers hello_with_get_arg setting_headers custom_access_log basic_authentication digest_authentication minimal_https minimal_file_response minimal_deferred url_registration minimal_ip_ban benchmark_select benchmark_threads deferred_with_accumulator +noinst_PROGRAMS = hello_world service minimal_hello_world custom_error allowing_disallowing_methods handlers hello_with_get_arg setting_headers custom_access_log basic_authentication digest_authentication minimal_https minimal_file_response minimal_deferred url_registration minimal_ip_ban benchmark_select benchmark_threads benchmark_nodelay deferred_with_accumulator hello_world_SOURCES = hello_world.cpp service_SOURCES = service.cpp @@ -40,3 +40,4 @@ url_registration_SOURCES = url_registration.cpp minimal_ip_ban_SOURCES = minimal_ip_ban.cpp benchmark_select_SOURCES = benchmark_select.cpp benchmark_threads_SOURCES = benchmark_threads.cpp +benchmark_nodelay_SOURCES = benchmark_nodelay.cpp From 7ad418cb4f1da73add5c796791b0cb1c568b3795 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 11 Aug 2019 00:57:15 +0000 Subject: [PATCH 459/623] Change setting path pieces to be lazy. --- src/httpserver/http_request.hpp | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/src/httpserver/http_request.hpp b/src/httpserver/http_request.hpp index 21f2176f..6ed97928 100644 --- a/src/httpserver/http_request.hpp +++ b/src/httpserver/http_request.hpp @@ -83,9 +83,9 @@ class http_request * Method used to get all pieces of the path requested; considering an url splitted by '/'. * @return a vector of strings containing all pieces **/ - const std::vector& get_path_pieces() const + const std::vector get_path_pieces() const { - return this->post_path; + return http::http_utils::tokenize_url(this->path); } /** @@ -95,8 +95,9 @@ class http_request **/ const std::string& get_path_piece(int index) const { - if(((int)(this->post_path.size())) > index) - return this->post_path[index]; + std::vector post_path = this->get_path_pieces(); + if(((int)(post_path.size())) > index) + return post_path[index]; return EMPTY; } @@ -238,7 +239,6 @@ class http_request http_request(const http_request& b): path(b.path), method(b.method), - post_path(b.post_path), args(b.args), content(b.content), content_size_limit(b.content_size_limit), @@ -251,7 +251,6 @@ class http_request http_request(http_request&& b) noexcept: path(std::move(b.path)), method(std::move(b.method)), - post_path(std::move(b.post_path)), args(std::move(b.args)), content(std::move(b.content)), content_size_limit(b.content_size_limit), @@ -266,7 +265,6 @@ class http_request this->path = b.path; this->method = b.method; - this->post_path = b.post_path; this->args = b.args; this->content = b.content; this->content_size_limit = b.content_size_limit; @@ -282,7 +280,6 @@ class http_request this->path = std::move(b.path); this->method = std::move(b.method); - this->post_path = std::move(b.post_path); this->args = std::move(b.args); this->content = std::move(b.content); this->content_size_limit = b.content_size_limit; @@ -294,7 +291,6 @@ class http_request std::string path; std::string method; - std::vector post_path; std::map args; std::string content; size_t content_size_limit; @@ -377,11 +373,6 @@ class http_request void set_path(const std::string& path) { this->path = path; - std::vector complete_path = http::http_utils::tokenize_url(this->path); - for(unsigned int i = 0; i < complete_path.size(); i++) - { - this->post_path.push_back(complete_path[i]); - } } /** From b4c307b7946183e0d27f300636abae99a900fb36 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 11 Aug 2019 01:34:31 +0000 Subject: [PATCH 460/623] Fix changelog --- ChangeLog | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ChangeLog b/ChangeLog index 6b3f6929..82b59fe6 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +Sat Aug 10 18:34:07 2019 -0800 + Added support for TCP-NODELAY + Changed set_path on http_request to have lazy behavior + Tue Aug 06 22:22:14 2019 -0800 Added support for body parsing in DELETE requests. Added support for PATCH method From 000d07c6e927cc0837a69c50d14a7c2437ac5cf0 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 11 Aug 2019 21:25:42 +0000 Subject: [PATCH 461/623] Simplify the cross-compilation build scripts. --- Makefile.am | 10 ++++-- configure.ac | 74 +++++++++++++++++++++++++-------------------- libhttpserver.pc.in | 2 +- src/Makefile.am | 3 ++ 4 files changed, 53 insertions(+), 36 deletions(-) diff --git a/Makefile.am b/Makefile.am index 14604f4f..a46882fd 100644 --- a/Makefile.am +++ b/Makefile.am @@ -24,14 +24,20 @@ LIBTOOL_DEPS = @LIBTOOL_DEPS@ AUTOMAKE_OPTIONS = foreign 1.4 ACLOCAL_AMFLAGS = -I m4 -SUBDIRS = src test -DIST_SUBDIRS = src test +SUBDIRS = src +DIST_SUBDIRS = src + +if !COND_CROSS_COMPILE +SUBDIRS += test +DIST_SUBDIRS += test if BUILD_EXAMPLES SUBDIRS += examples DIST_SUBDIRS += examples endif +endif + EXTRA_DIST = libhttpserver.pc.in $(DX_CONFIG) MOSTLYCLEANFILES = $(DX_CLEANFILES) *.gcda *.gcno *.gcov diff --git a/configure.ac b/configure.ac index 7e53d670..2010febe 100644 --- a/configure.ac +++ b/configure.ac @@ -37,7 +37,7 @@ AX_VALGRIND_CHECK OLD_CXXFLAGS=$CXXFLAGS LT_INIT AC_PROG_CC -AC_PROG_CXX([clang++]) +AC_PROG_CXX() AC_PROG_LN_S CXXFLAGS=$OLD_CXXFLAGS AC_LANG([C++]) @@ -93,28 +93,48 @@ AC_CHECK_HEADER([signal.h],[],[AC_MSG_ERROR("signal.h not found")]) AC_CHECK_HEADER([gnutls/gnutls.h],[have_gnutls="yes"],[AC_MSG_WARN("gnutls/gnutls.h not found. TLS will be disabled"); have_gnutls="no"]) # Checks for libmicrohttpd -AC_CHECK_HEADER([microhttpd.h], - AC_CHECK_LIB([microhttpd], [MHD_get_fdset2], - [AC_MSG_CHECKING([for libmicrohttpd >= 0.9.52]) - AC_COMPILE_IFELSE( - [AC_LANG_SOURCE([ - #include - #if (MHD_VERSION < 0x00095102) - #error needs at least version 0.9.52 - #endif - int main () { return 0; } - ])], +if test x"$host" = x"$build"; then + AC_CHECK_HEADER([microhttpd.h], + AC_CHECK_LIB([microhttpd], [MHD_get_fdset2], + [AC_MSG_CHECKING([for libmicrohttpd >= 0.9.52]) + AC_COMPILE_IFELSE( + [AC_LANG_SOURCE([ + #include + #if (MHD_VERSION < 0x00095102) + #error needs at least version 0.9.52 + #endif + int main () { return 0; } + ])], + [], + [AC_MSG_ERROR("libmicrohttpd is too old - install libmicrohttpd >= 0.9.52")] + ) + ], + [AC_MSG_ERROR(["libmicrohttpd not found"])] + ), + [AC_MSG_ERROR(["microhttpd.h not found"])] + ) + + CXXFLAGS="-std=c++11 -DHTTPSERVER_COMPILATION -D_REENTRANT $LIBMICROHTTPD_CFLAGS $CXXFLAGS" + LDFLAGS="$LIBMICROHTTPD_LIBS $REGEX_LIBS $LDFLAGS" + + cond_cross_compile="no" +else + AC_CHECK_HEADER([microhttpd.h], + AC_CHECK_LIB([microhttpd], [MHD_get_fdset2], [], - [AC_MSG_ERROR("libmicrohttpd is too old - install libmicrohttpd >= 0.9.52")] - ) - ], - [AC_MSG_ERROR(["libmicrohttpd not found"])] - ), - [AC_MSG_ERROR(["microhttpd.h not found"])] -) + [AC_MSG_ERROR(["libmicrohttpd not found"])] + ), + [AC_MSG_ERROR(["microhttpd.h not found"])] + ) + + CXXFLAGS="-std=c++11 -DHTTPSERVER_COMPILATION -D_REENTRANT $CXXFLAGS" + LDFLAGS="$REGEX_LIBS $LDFLAGS" + + cond_cross_compile="yes" +fi -CXXFLAGS="-std=c++11 -DHTTPSERVER_COMPILATION -D_REENTRANT $LIBMICROHTTPD_CFLAGS $CXXFLAGS" -LDFLAGS="$LIBMICROHTTPD_LIBS $REGEX_LIBS $LDFLAGS" +AM_CONDITIONAL([COND_CROSS_COMPILE],[test x"$cond_cross_compile" = x"yes"]) +AC_SUBST(COND_CROSS_COMPILE) AC_MSG_CHECKING([whether to build with TCP_FASTOPEN support]) AC_ARG_ENABLE([fastopen], @@ -292,18 +312,6 @@ AC_ARG_ENABLE([[examples]], test "x$enable_examples" = "xno" || enable_examples=yes AM_CONDITIONAL([BUILD_EXAMPLES], [test "x$enable_examples" = "xyes"]) -if test "$CROSS_COMPILE" == "1"; then - if test "$ARM_ARCH_DIR" == "aarch64-linux-gnu"; then - AM_CXXFLAGS="$AM_CXXFLAGS --prefix=${ARM_LD_PATH}/" - AM_CFLAGS="$AM_CFLAGS --prefix=${ARM_LD_PATH}/" - PATH="${ARM_LD_PATH}/:$PATH" - CXXLINK="${ARM_LD_PATH}/ld" - LINK="${ARM_LD_PATH}/ld" - LD="${ARM_LD_PATH}/ld" - LDFLAGS="$LDFLAGS -Wl,-rpath -Wl,/usr/lib -Wl,-rpath-link -Wl,${ARM_LD_PATH}/usr/lib -L${ARM_LD_PATH}/lib -L${ARM_LD_PATH}/usr/lib" - fi -fi - AM_CONDITIONAL([COND_GCOV],[test x"$cond_gcov" = x"yes"]) AC_SUBST(COND_GCOV) diff --git a/libhttpserver.pc.in b/libhttpserver.pc.in index 55f7c0e2..aaf116af 100644 --- a/libhttpserver.pc.in +++ b/libhttpserver.pc.in @@ -6,7 +6,7 @@ includedir=@includedir@ Name: libhttpserver Description: A C++ library for creating an embedded Rest HTTP server Version: @VERSION@ -Requires: libmicrohttpd >= 0.9.37 +Requires: libmicrohttpd >= 0.9.52 Conflicts: Libs: -L${libdir} -lhttpserver Libs.private: @LHT_LIBDEPS@ diff --git a/src/Makefile.am b/src/Makefile.am index 888abce4..5e549bbc 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -31,7 +31,10 @@ AM_CXXFLAGS += -O0 --coverage --no-inline AM_LDFLAGS += -O0 --coverage -lgcov --no-inline endif +if !COND_CROSS_COMPILE libhttpserver_la_LIBADD = -lmicrohttpd +endif + libhttpserver_la_CFLAGS = $(AM_CFLAGS) libhttpserver_la_CXXFLAGS = $(AM_CXXFLAGS) libhttpserver_la_LDFLAGS = $(AM_LDFLAGS) -no-undefined From 375a974255ae050d9c7035198e85c7e4e058d324 Mon Sep 17 00:00:00 2001 From: "Dean M. Sands, III" Date: Wed, 14 Aug 2019 09:35:15 -0500 Subject: [PATCH 462/623] Adapted sleep function in 'examples/deferred_with_accumulator.cpp' for non-*Nix --- examples/deferred_with_accumulator.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/examples/deferred_with_accumulator.cpp b/examples/deferred_with_accumulator.cpp index 80a9cae8..d114eb2a 100644 --- a/examples/deferred_with_accumulator.cpp +++ b/examples/deferred_with_accumulator.cpp @@ -18,6 +18,8 @@ USA */ +#include +#include #include #include @@ -44,7 +46,8 @@ ssize_t test_callback (std::shared_ptr > closure_data, char* bu std::copy(str.begin(), str.end(), buf); // keep sending reqid - sleep(1); + // sleep(1); ==> adapted for C++11 on non-*Nix systems + std::this_thread::sleep_for(std::chrono::seconds(1)); return (ssize_t)max; } From 70d166db5ac01d97cea2a3c18b88b42741ff6291 Mon Sep 17 00:00:00 2001 From: "Dean M. Sands, III" Date: Thu, 15 Aug 2019 10:41:52 -0500 Subject: [PATCH 463/623] Added Winsock2 library for mingw; Not tested for cygwin --- configure.ac | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index 2010febe..576728ad 100644 --- a/configure.ac +++ b/configure.ac @@ -60,10 +60,12 @@ if test x"$samedirectory" = x"no"; then fi is_windows=yes; +NETWORK_LIBS="" case "$host" in *-mingw*) NETWORK_HEADER="winsock2.h" REGEX_LIBS="-lregex -lpthread -no-undefined" + NETWORK_LIBS="-lws2_32" native_srcdir=$(cd $srcdir; pwd -W) ;; *-cygwin*) @@ -115,7 +117,7 @@ if test x"$host" = x"$build"; then ) CXXFLAGS="-std=c++11 -DHTTPSERVER_COMPILATION -D_REENTRANT $LIBMICROHTTPD_CFLAGS $CXXFLAGS" - LDFLAGS="$LIBMICROHTTPD_LIBS $REGEX_LIBS $LDFLAGS" + LDFLAGS="$LIBMICROHTTPD_LIBS $NETWORK_LIBS $REGEX_LIBS $LDFLAGS" cond_cross_compile="no" else @@ -128,7 +130,7 @@ else ) CXXFLAGS="-std=c++11 -DHTTPSERVER_COMPILATION -D_REENTRANT $CXXFLAGS" - LDFLAGS="$REGEX_LIBS $LDFLAGS" + LDFLAGS="$NETWORK_LIBS $REGEX_LIBS $LDFLAGS" cond_cross_compile="yes" fi From 4733d4e6c09dc9695874357c7af9b87ff9bd017a Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Tue, 27 Aug 2019 02:16:38 +0000 Subject: [PATCH 464/623] Add fix to get_path_pieces to return by value. The current implementation has a bug where the method returns a reference to a temporary value. --- src/httpserver/http_request.hpp | 2 +- test/integ/basic.cpp | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/src/httpserver/http_request.hpp b/src/httpserver/http_request.hpp index 6ed97928..632fe06d 100644 --- a/src/httpserver/http_request.hpp +++ b/src/httpserver/http_request.hpp @@ -93,7 +93,7 @@ class http_request * @param index the index of the piece selected * @return the selected piece in form of string **/ - const std::string& get_path_piece(int index) const + const std::string get_path_piece(int index) const { std::vector post_path = this->get_path_pieces(); if(((int)(post_path.size())) > index) diff --git a/test/integ/basic.cpp b/test/integ/basic.cpp index c1b1156f..767a8ecd 100644 --- a/test/integ/basic.cpp +++ b/test/integ/basic.cpp @@ -21,6 +21,7 @@ #include "littletest.hpp" #include #include +#include #include #include "string_utilities.hpp" #include "httpserver.hpp" @@ -135,6 +136,20 @@ class querystring_resource : public http_resource } }; +class path_pieces_resource : public http_resource +{ + public: + const shared_ptr render_GET(const http_request& req) + { + std::stringstream ss; + for (unsigned int i = 0; i < req.get_path_pieces().size(); i++) + { + ss << req.get_path_piece(i) << ","; + } + return shared_ptr(new string_response(ss.str(), 200, "text/plain")); + } +}; + class complete_test_resource : public http_resource { public: @@ -959,6 +974,24 @@ LT_BEGIN_AUTO_TEST(basic_suite, response_is_printable) curl_easy_cleanup(curl); LT_END_AUTO_TEST(response_is_printable) +LT_BEGIN_AUTO_TEST(basic_suite, long_path_pieces) + path_pieces_resource resource; + ws->register_resource("/settings", &resource, true); + curl_global_init(CURL_GLOBAL_ALL); + + std::string s; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/settings/somestringthatisreallylong/with_really_a_lot_of_content/and_underscores_and_looooooooooooooooooong_stuff"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "settings,somestringthatisreallylong,with_really_a_lot_of_content,and_underscores_and_looooooooooooooooooong_stuff,"); + curl_easy_cleanup(curl); +LT_END_AUTO_TEST(long_path_pieces) + LT_BEGIN_AUTO_TEST_ENV() AUTORUN_TESTS() LT_END_AUTO_TEST_ENV() From 2a74f9ef1f4b130d6b16c9a5e1c3f099ee5efd7b Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Mon, 2 Sep 2019 19:20:43 -0700 Subject: [PATCH 465/623] Enable build on travis windows (#163) --- .travis.yml | 71 ++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 56 insertions(+), 15 deletions(-) diff --git a/.travis.yml b/.travis.yml index 9a47d329..63d989c9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,6 +2,7 @@ language: cpp os: - linux - osx + - windows compiler: - gcc - clang @@ -10,22 +11,49 @@ env: - DEBUG="nodebug" COVERAGE="nocoverage" - LINKING="static" before_install: + - ps -ef + - if [ "$TRAVIS_OS_NAME" = "windows" ]; then ps -Wla | sort ; fi - eval "${MATRIX_EVAL}" - - export LDFLAGS="$LDFLAGS -L/usr/local/lib -L/usr/lib" - - export PATH=$PATH:/usr/local/lib - - export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib - - export DYLD_LIBRARY_PATH=$DYLD_LIBRARY_PATH:/usr/local/lib + - if [ "$TRAVIS_OS_NAME" != "windows" ]; then export LDFLAGS="$LDFLAGS -L/usr/local/lib -L/usr/lib"; fi + - if [ "$TRAVIS_OS_NAME" != "windows" ]; then export PATH=$PATH:/usr/local/lib; fi + - if [ "$TRAVIS_OS_NAME" != "windows" ]; then export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib; fi + - if [ "$TRAVIS_OS_NAME" != "windows" ]; then export DYLD_LIBRARY_PATH=$DYLD_LIBRARY_PATH:/usr/local/lib; fi - if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo apt-get install info install-info; fi - if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo pip install codecov; fi - if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo pip install gcovr; fi - if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo apt-get install cppcheck; fi - if [ "$TRAVIS_OS_NAME" = "osx" ]; then export CFLAGS='-mtune=generic'; fi + - if [ "$TRAVIS_OS_NAME" = "windows" ]; then choco install -r --no-progress -y msys2 make; fi + - if [ "$TRAVIS_OS_NAME" = "windows" ]; then PATH=$PATH:/c/tools/msys64/usr/bin/ ; fi + - if [ "$TRAVIS_OS_NAME" = "windows" ]; then powershell -executionpolicy bypass "pacman -Syu --noconfirm autoconf libtool automake make autoconf-archive pkg-config mingw-w64-x86_64-libsystre" ; fi + - if [ "$TRAVIS_OS_NAME" = "windows" ]; then + ln -s /c/tools/msys64/usr/share/autoconf* /usr/share/ ; + ln -s /c/tools/msys64/usr/share/automake* /usr/share/ ; + ln -s /c/tools/msys64/usr/share/aclocal* /usr/share/ ; + ln -s /c/tools/msys64/usr/share/libtool* /usr/share/ ; + ln -s /c/tools/msys64/usr/share/pkgconfig /usr/share/ ; + ln -s /c/tools/msys64/usr/bin/autom4te /usr/bin/ ; + ln -s /c/tools/msys64/usr/bin/autoconf /usr/bin/ ; + ln -s /c/tools/msys64/usr/bin/autoheader /usr/bin/ ; + ln -s /c/tools/msys64/usr/bin/m4 /usr/bin/ ; + + PATH=$PATH:/c/tools/msys64/usr/bin/ ; + export SHELL=/usr/bin/sh.exe ; + fi - curl https://s3.amazonaws.com/libhttpserver/libmicrohttpd_releases/libmicrohttpd-0.9.59.tar.gz -o libmicrohttpd-0.9.59.tar.gz - - tar -xvzf libmicrohttpd-0.9.59.tar.gz + - tar -xzf libmicrohttpd-0.9.59.tar.gz - cd libmicrohttpd-0.9.59 - - if [[ "$ARM_ARCH_DIR" != "" ]]; then ./configure --build `./config.guess` --host $ARM_ARCH_DIR --disable-examples; else ./configure --disable-examples; fi + - if [[ "$ARM_ARCH_DIR" != "" ]]; then + ./configure --build `./config.guess` --host $ARM_ARCH_DIR --disable-examples; + elif [[ "$TRAVIS_OS_NAME" = "windows" ]]; then + ./configure --prefix=/usr --build=x86_64-w64-mingw32 --host=x86_64-w64-mingw32 --disable-examples --disable-https --enable-shared --enable-static ; + else + ./configure --disable-examples; + fi + - if [ "$TRAVIS_OS_NAME" = "windows" ]; then find . -name Makefile -type f -exec sed -i "s/\${SHELL}/\/usr\/bin\/sh.exe/" "{}" + ; fi + - if [ "$TRAVIS_OS_NAME" = "windows" ]; then find . -name Makefile -type f -exec sed -i "s/\$(SHELL)/\/usr\/bin\/sh.exe/" "{}" + ; fi - make - - sudo make install + - if [ "$TRAVIS_OS_NAME" = "windows" ]; then make install; else sudo make install; fi - cd .. - if [ "$BUILD_TYPE" = "asan" ]; then export CFLAGS='-fsanitize=address'; export CXXLAGS='-fsanitize=address'; export LDFLAGS='-fsanitize=address'; fi - if [ "$BUILD_TYPE" = "msan" ]; then export CFLAGS='-fsanitize=memory'; export CXXLAGS='-fsanitize=memory'; export LDFLAGS='-fsanitize=memory'; fi @@ -33,6 +61,7 @@ before_install: - if [ "$BUILD_TYPE" = "tsan" ]; then export CFLAGS='-fsanitize=thread'; export CXXLAGS='-fsanitize=thread'; export LDFLAGS='-fsanitize=thread'; fi - if [ "$BUILD_TYPE" = "ubsan" ]; then export export CFLAGS='-fsanitize=undefined'; export CXXLAGS='-fsanitize=undefined'; export LDFLAGS='-fsanitize=undefined'; fi install: + - ps -ef - if [[ "$CROSS_COMPILE" == 1 ]] ; then if [[ "$ARM_ARCH_DIR" == "aarch64-linux-gnu" ]] ; then mkdir $HOME/linker_bin ; @@ -45,7 +74,12 @@ install: - ./bootstrap - mkdir build - cd build - - if [ "$LINKING" = "static" ]; then + - if [ "$TRAVIS_OS_NAME" = "windows" ]; then + ../configure --build=x86_64-w64-mingw32 --host=x86_64-w64-mingw32 --disable-fastopen --disable-examples CPPFLAGS='-I/c/tools/msys64/mingw64/include/ -I/usr/include/' LDFLAGS='-L/c/tools/msys64/mingw64/lib -L/usr/lib/' ; + cd .. ; + find . -name Makefile -type f -exec sed -i "s/\$(SHELL)/\/usr\/bin\/sh.exe/" "{}" + ; + cd build ; + elif [ "$LINKING" = "static" ]; then ../configure --enable-static --disable-fastopen; elif [ "$DEBUG" = "debug" ] && [ "$COVERAGE" = "coverage" ]; then ../configure --enable-debug --enable-coverage --disable-shared --disable-fastopen; @@ -63,8 +97,9 @@ install: - if [ "$CROSS_COMPILE" = "1" ] && [ "$ARM_ARCH_DIR" = "aarch64-linux-gnu" ]; then sed -i 's+/usr/bin/ld+$HOME/linker_bin/ld+g' libtool; fi - if [ "$CROSS_COMPILE" = "1" ] && [ "$ARM_ARCH_DIR" = "aarch64-linux-gnu" ]; then sed -i 's+/usr/bin/ld+$HOME/linker_bin/ld+g' Makefile; fi - make - - make check TESTS= + - if [ "$TRAVIS_OS_NAME" != "windows" ]; then make check TESTS= ; fi script: + - ps -ef - if [[ "$CROSS_COMPILE" == 1 ]]; then cd test ; if [[ "$ARM_ARCH_DIR" == "aarch64-linux-gnu" ]]; then @@ -85,12 +120,10 @@ script: qemu-arm -L /usr/arm-linux-gnueabi/ ./.libs/threaded ; fi fi - - make check - - cat test/test-suite.log + - if [ "$TRAVIS_OS_NAME" != "windows" ]; then make check ; fi + - if [ "$TRAVIS_OS_NAME" != "windows" ]; then cat test/test-suite.log; fi - if [ "$VALGRIND" = "valgrind" ]; then make check-valgrind; fi; - if [ "$VALGRIND" = "valgrind" ]; then cat test/test-suite-memcheck.log; fi; - - ls -l /usr/local/lib/ - - ls -l /usr/lib/ - if [ "$TRAVIS_OS_NAME" = "linux" ]; then cd ../src/; cppcheck --error-exitcode=1 .; cd ../build; fi - | if [ "$PERFORMANCE" = "select" ]; then @@ -110,15 +143,23 @@ script: ./benchmark_threads 8080 & sleep 5 && ab -n 10000000 -c 100 localhost:8080/plaintext fi +after_script: + - ps -ef + - if [ "$TRAVIS_OS_NAME" = "windows" ]; then ps -Wla | sort ; fi + - if [ "$TRAVIS_OS_NAME" = "windows" ]; then gpgconf --kill gpg-agent ; fi + - if [ "$TRAVIS_OS_NAME" = "windows" ]; then taskkill //F //PID $(ps -Wla | tr -s ' ' | grep gpg | cut -f2 -d' ') ; fi + - if [ "$TRAVIS_OS_NAME" = "windows" ]; then ps -Wla | sort ; fi + - echo $$ after_success: - if [ "$DEBUG" = "debug" ] && [ "$COVERAGE" = "coverage" ] && [ "$TRAVIS_OS_NAME" = "linux" ]; then bash <(curl -s https://codecov.io/bash); fi matrix: exclude: - compiler: clang env: DEBUG="debug" COVERAGE="coverage" - - compiler: clang - env: LINKING='static' + - os: windows include: + - os: windows + env: DEBUG="nodebug" COVERAGE="nocoverage" YARN_GPG=no - os: linux addons: apt: From c74246572077ac0903c6152f5e067246bfe3fb87 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Tue, 5 Nov 2019 23:46:53 -0800 Subject: [PATCH 466/623] Fixes get pending when body is provided. (#166) Body is ignored on get (and get-like) operations. --- src/httpserver/details/modded_request.hpp | 12 ++++-- src/httpserver/webserver.hpp | 9 +---- src/webserver.cpp | 45 ++++++++++++----------- 3 files changed, 34 insertions(+), 32 deletions(-) diff --git a/src/httpserver/details/modded_request.hpp b/src/httpserver/details/modded_request.hpp index 5948e1bd..df99ce8e 100644 --- a/src/httpserver/details/modded_request.hpp +++ b/src/httpserver/details/modded_request.hpp @@ -43,6 +43,7 @@ struct modded_request http_request* dhr; std::shared_ptr dhrs; bool second; + bool has_body; modded_request(): pp(0x0), @@ -50,7 +51,8 @@ struct modded_request standardized_url(0x0), ws(0x0), dhr(0x0), - second(false) + second(false), + has_body(false) { } @@ -60,7 +62,8 @@ struct modded_request standardized_url(b.standardized_url), ws(b.ws), dhr(b.dhr), - second(b.second) + second(b.second), + has_body(b.has_body) { } @@ -70,7 +73,8 @@ struct modded_request standardized_url(std::move(b.standardized_url)), ws(std::move(b.ws)), dhr(std::move(b.dhr)), - second(b.second) + second(b.second), + has_body(b.has_body) { } @@ -84,6 +88,7 @@ struct modded_request this->ws = b.ws; this->dhr = b.dhr; this->second = b.second; + this->has_body = b.has_body; return *this; } @@ -98,6 +103,7 @@ struct modded_request this->ws = std::move(b.ws); this->dhr = std::move(b.dhr); this->second = b.second; + this->has_body = b.has_body; return *this; } diff --git a/src/httpserver/webserver.hpp b/src/httpserver/webserver.hpp index 71433965..93b2e8b5 100644 --- a/src/httpserver/webserver.hpp +++ b/src/httpserver/webserver.hpp @@ -222,16 +222,11 @@ class webserver void **con_cls, int upgrade_socket ); - int bodyless_requests_answer(MHD_Connection* connection, - const char* method, const char* version, - struct details::modded_request* mr - ); - - int bodyfull_requests_answer_first_step(MHD_Connection* connection, + int requests_answer_first_step(MHD_Connection* connection, struct details::modded_request* mr ); - int bodyfull_requests_answer_second_step(MHD_Connection* connection, + int requests_answer_second_step(MHD_Connection* connection, const char* method, const char* version, const char* upload_data, size_t* upload_data_size, struct details::modded_request* mr ); diff --git a/src/webserver.cpp b/src/webserver.cpp index a0608f95..9b4d03a1 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -511,23 +511,19 @@ const std::shared_ptr webserver::internal_error_page(details::mod } } -int webserver::bodyless_requests_answer( - MHD_Connection* connection, const char* method, - const char* version, struct details::modded_request* mr - ) -{ - http_request req(connection, unescaper); - mr->dhr = &(req); - return complete_request(connection, mr, version, method); -} - -int webserver::bodyfull_requests_answer_first_step( +int webserver::requests_answer_first_step( MHD_Connection* connection, struct details::modded_request* mr ) { mr->second = true; mr->dhr = new http_request(connection, unescaper); + + if (!mr->has_body) + { + return MHD_YES; + } + mr->dhr->set_content_size_limit(content_size_limit); const char *encoding = MHD_lookup_connection_value ( connection, @@ -567,7 +563,7 @@ int webserver::bodyfull_requests_answer_first_step( return MHD_YES; } -int webserver::bodyfull_requests_answer_second_step( +int webserver::requests_answer_second_step( MHD_Connection* connection, const char* method, const char* version, const char* upload_data, size_t* upload_data_size, struct details::modded_request* mr @@ -575,12 +571,17 @@ int webserver::bodyfull_requests_answer_second_step( { if (0 == *upload_data_size) return complete_request(connection, mr, version, method); + if (mr->has_body) + { + #ifdef DEBUG - cout << "Writing content: " << upload_data << endl; + cout << "Writing content: " << upload_data << endl; #endif //DEBUG - mr->dhr->grow_content(upload_data, *upload_data_size); + mr->dhr->grow_content(upload_data, *upload_data_size); + + if (mr->pp != NULL) MHD_post_process(mr->pp, upload_data, *upload_data_size); + } - if (mr->pp != NULL) MHD_post_process(mr->pp, upload_data, *upload_data_size); *upload_data_size = 0; return MHD_YES; } @@ -750,7 +751,7 @@ int webserver::answer_to_connection(void* cls, MHD_Connection* connection, if(mr->second != false) { return static_cast(cls)-> - bodyfull_requests_answer_second_step( + requests_answer_second_step( connection, method, version, @@ -776,7 +777,7 @@ int webserver::answer_to_connection(void* cls, MHD_Connection* connection, base_unescaper(t_url, static_cast(cls)->unescaper); mr->standardized_url = new string(http_utils::standardize_url(t_url)); - bool body = false; + mr->has_body = false; access_log( static_cast(cls), @@ -790,22 +791,22 @@ int webserver::answer_to_connection(void* cls, MHD_Connection* connection, else if (0 == strcmp(method, http_utils::http_method_post.c_str())) { mr->callback = &http_resource::render_POST; - body = true; + mr->has_body = true; } else if (0 == strcasecmp(method, http_utils::http_method_put.c_str())) { mr->callback = &http_resource::render_PUT; - body = true; + mr->has_body = true; } else if (0 == strcasecmp(method,http_utils::http_method_delete.c_str())) { mr->callback = &http_resource::render_DELETE; - body = true; + mr->has_body = true; } else if (0 == strcasecmp(method, http_utils::http_method_patch.c_str())) { mr->callback = &http_resource::render_PATCH; - body = true; + mr->has_body = true; } else if (0 == strcasecmp(method, http_utils::http_method_head.c_str())) { @@ -824,7 +825,7 @@ int webserver::answer_to_connection(void* cls, MHD_Connection* connection, mr->callback = &http_resource::render_OPTIONS; } - return body ? static_cast(cls)->bodyfull_requests_answer_first_step(connection, mr) : static_cast(cls)->bodyless_requests_answer(connection, method, version, mr); + return static_cast(cls)->requests_answer_first_step(connection, mr); } }; From 84438c4a348f8a81de1dbcb278fa86ff49b9ce65 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 10 Nov 2019 16:08:44 -0800 Subject: [PATCH 467/623] Add test build on gcc-9 (#167) --- .travis.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.travis.yml b/.travis.yml index 63d989c9..088d6dcc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -241,6 +241,15 @@ matrix: - g++-8 env: - MATRIX_EVAL="CC=gcc-8 && CXX=g++-8" + - os: linux + addons: + apt: + sources: + - ubuntu-toolchain-r-test + packages: + - g++-9 + env: + - MATRIX_EVAL="CC=gcc-9 && CXX=g++-9" - os: linux addons: apt: From e0fd7a27568caf82fad9cbe2e4dd3ce7a7532fc0 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 10 Nov 2019 21:02:02 -0800 Subject: [PATCH 468/623] Clang 9 support (#168) * add support for clang-9 * Adding travis sourceline to support clang9 --- .travis.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/.travis.yml b/.travis.yml index 088d6dcc..0971f0e3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -393,6 +393,18 @@ matrix: - clang-8 env: - MATRIX_EVAL="CC=clang-8 && CXX=clang++-8" + - os: linux + addons: + apt: + sources: + - llvm-toolchain-xenial-9 + - ubuntu-toolchain-r-test + - sourceline: 'deb https://apt.llvm.org/xenial/ llvm-toolchain-xenial-9 main' + key_url: 'https://apt.llvm.org/llvm-snapshot.gpg.key' + packages: + - clang-9 + env: + - MATRIX_EVAL="CC=clang-9 && CXX=clang++-9" - os: linux compiler: clang addons: From 9cdc90177e12902adf4063300fd688fcfdbe8168 Mon Sep 17 00:00:00 2001 From: bcsgh <33939446+bcsgh@users.noreply.github.com> Date: Wed, 26 Feb 2020 06:06:16 -0800 Subject: [PATCH 469/623] Use =default for constructors and operator= where possible (#179) Use default constructor and operator= where possible. --- src/httpserver/basic_auth_fail_response.hpp | 47 +-- src/httpserver/create_webserver.hpp | 337 +++---------------- src/httpserver/deferred_response.hpp | 43 +-- src/httpserver/details/modded_request.hpp | 79 +---- src/httpserver/digest_auth_fail_response.hpp | 61 +--- src/httpserver/file_response.hpp | 46 +-- src/httpserver/http_request.hpp | 73 +--- src/httpserver/http_resource.hpp | 30 +- src/httpserver/http_response.hpp | 54 +-- src/httpserver/string_response.hpp | 46 +-- 10 files changed, 113 insertions(+), 703 deletions(-) diff --git a/src/httpserver/basic_auth_fail_response.hpp b/src/httpserver/basic_auth_fail_response.hpp index c9182dce..c7e2d476 100644 --- a/src/httpserver/basic_auth_fail_response.hpp +++ b/src/httpserver/basic_auth_fail_response.hpp @@ -33,11 +33,7 @@ namespace httpserver class basic_auth_fail_response : public string_response { public: - basic_auth_fail_response(): - string_response(), - realm("") - { - } + basic_auth_fail_response() = default; explicit basic_auth_fail_response( const std::string& content, @@ -50,46 +46,17 @@ class basic_auth_fail_response : public string_response { } - basic_auth_fail_response(const basic_auth_fail_response& other): - string_response(other), - realm(other.realm) - { - } - - basic_auth_fail_response(basic_auth_fail_response&& other) noexcept: - string_response(std::move(other)), - realm(std::move(other.realm)) - { - } - - basic_auth_fail_response& operator=(const basic_auth_fail_response& b) - { - if (this == &b) return *this; - - (string_response&) (*this) = b; - this->realm = b.realm; + basic_auth_fail_response(const basic_auth_fail_response& other) = default; + basic_auth_fail_response(basic_auth_fail_response&& other) noexcept = default; + basic_auth_fail_response& operator=(const basic_auth_fail_response& b) = default; + basic_auth_fail_response& operator=(basic_auth_fail_response&& b) = default; - return *this; - } - - basic_auth_fail_response& operator=(basic_auth_fail_response&& b) - { - if (this == &b) return *this; - - (string_response&) (*this) = std::move(b); - this->realm = std::move(b.realm); - - return *this; - } - - ~basic_auth_fail_response() - { - } + ~basic_auth_fail_response() = default; int enqueue_response(MHD_Connection* connection, MHD_Response* response); private: - std::string realm; + std::string realm = ""; }; } diff --git a/src/httpserver/create_webserver.hpp b/src/httpserver/create_webserver.hpp index 596d7b33..af1b93b6 100644 --- a/src/httpserver/create_webserver.hpp +++ b/src/httpserver/create_webserver.hpp @@ -45,263 +45,14 @@ typedef void(*log_error_ptr)(const std::string&); class create_webserver { public: - create_webserver(): - _port(DEFAULT_WS_PORT), - _start_method(http::http_utils::INTERNAL_SELECT), - _max_threads(0), - _max_connections(0), - _memory_limit(0), - _content_size_limit(static_cast(-1)), - _connection_timeout(DEFAULT_WS_TIMEOUT), - _per_IP_connection_limit(0), - _log_access(0x0), - _log_error(0x0), - _validator(0x0), - _unescaper(0x0), - _bind_address(0x0), - _bind_socket(0), - _max_thread_stack_size(0), - _use_ssl(false), - _use_ipv6(false), - _debug(false), - _pedantic(false), - _https_mem_key(""), - _https_mem_cert(""), - _https_mem_trust(""), - _https_priorities(""), - _cred_type(http::http_utils::NONE), - _digest_auth_random(""), - _nonce_nc_size(0), - _default_policy(http::http_utils::ACCEPT), - _basic_auth_enabled(true), - _digest_auth_enabled(true), - _regex_checking(true), - _ban_system_enabled(true), - _post_process_enabled(true), - _deferred_enabled(false), - _single_resource(false), - _tcp_nodelay(false), - _not_found_resource(0x0), - _method_not_allowed_resource(0x0), - _internal_error_resource(0x0) - { - } - - create_webserver(const create_webserver& b): - _port(b._port), - _start_method(b._start_method), - _max_threads(b._max_threads), - _max_connections(b._max_connections), - _memory_limit(b._memory_limit), - _content_size_limit(b._content_size_limit), - _connection_timeout(b._connection_timeout), - _per_IP_connection_limit(b._per_IP_connection_limit), - _log_access(b._log_access), - _log_error(b._log_error), - _validator(b._validator), - _unescaper(b._unescaper), - _bind_address(b._bind_address), - _bind_socket(b._bind_socket), - _max_thread_stack_size(b._max_thread_stack_size), - _use_ssl(b._use_ssl), - _use_ipv6(b._use_ipv6), - _debug(b._debug), - _pedantic(b._pedantic), - _https_mem_key(b._https_mem_key), - _https_mem_cert(b._https_mem_cert), - _https_mem_trust(b._https_mem_trust), - _https_priorities(b._https_priorities), - _cred_type(b._cred_type), - _digest_auth_random(b._digest_auth_random), - _nonce_nc_size(b._nonce_nc_size), - _default_policy(b._default_policy), - _basic_auth_enabled(b._basic_auth_enabled), - _digest_auth_enabled(b._digest_auth_enabled), - _regex_checking(b._regex_checking), - _ban_system_enabled(b._ban_system_enabled), - _post_process_enabled(b._post_process_enabled), - _deferred_enabled(b._deferred_enabled), - _single_resource(b._single_resource), - _tcp_nodelay(b._tcp_nodelay), - _not_found_resource(b._not_found_resource), - _method_not_allowed_resource(b._method_not_allowed_resource), - _internal_error_resource(b._internal_error_resource) - { - } - - create_webserver(create_webserver&& b): - _port(b._port), - _start_method(b._start_method), - _max_threads(b._max_threads), - _max_connections(b._max_connections), - _memory_limit(b._memory_limit), - _content_size_limit(b._content_size_limit), - _connection_timeout(b._connection_timeout), - _per_IP_connection_limit(b._per_IP_connection_limit), - _log_access(std::move(b._log_access)), - _log_error(std::move(b._log_error)), - _validator(std::move(b._validator)), - _unescaper(std::move(b._unescaper)), - _bind_address(std::move(b._bind_address)), - _bind_socket(b._bind_socket), - _max_thread_stack_size(b._max_thread_stack_size), - _use_ssl(b._use_ssl), - _use_ipv6(b._use_ipv6), - _debug(b._debug), - _pedantic(b._pedantic), - _https_mem_key(std::move(b._https_mem_key)), - _https_mem_cert(std::move(b._https_mem_cert)), - _https_mem_trust(std::move(b._https_mem_trust)), - _https_priorities(std::move(b._https_priorities)), - _cred_type(b._cred_type), - _digest_auth_random(std::move(b._digest_auth_random)), - _nonce_nc_size(b._nonce_nc_size), - _default_policy(b._default_policy), - _basic_auth_enabled(b._basic_auth_enabled), - _digest_auth_enabled(b._digest_auth_enabled), - _regex_checking(b._regex_checking), - _ban_system_enabled(b._ban_system_enabled), - _post_process_enabled(b._post_process_enabled), - _deferred_enabled(b._deferred_enabled), - _single_resource(b._single_resource), - _tcp_nodelay(b._tcp_nodelay), - _not_found_resource(std::move(b._not_found_resource)), - _method_not_allowed_resource(std::move(b._method_not_allowed_resource)), - _internal_error_resource(std::move(b._internal_error_resource)) - { - } - - create_webserver& operator=(const create_webserver& b) - { - if (this == &b) return *this; - - this->_port = b._port; - this->_start_method = b._start_method; - this->_max_threads = b._max_threads; - this->_max_connections = b._max_connections; - this->_memory_limit = b._memory_limit; - this->_content_size_limit = b._content_size_limit; - this->_connection_timeout = b._connection_timeout; - this->_per_IP_connection_limit = b._per_IP_connection_limit; - this->_log_access = b._log_access; - this->_log_error = b._log_error; - this->_validator = b._validator; - this->_unescaper = b._unescaper; - this->_bind_address = b._bind_address; - this->_bind_socket = b._bind_socket; - this->_max_thread_stack_size = b._max_thread_stack_size; - this->_use_ssl = b._use_ssl; - this->_use_ipv6 = b._use_ipv6; - this->_debug = b._debug; - this->_pedantic = b._pedantic; - this->_https_mem_key = b._https_mem_key; - this->_https_mem_cert = b._https_mem_cert; - this->_https_mem_trust = b._https_mem_trust; - this->_https_priorities = b._https_priorities; - this->_cred_type = b._cred_type; - this->_digest_auth_random = b._digest_auth_random; - this->_nonce_nc_size = b._nonce_nc_size; - this->_default_policy = b._default_policy; - this->_basic_auth_enabled = b._basic_auth_enabled; - this->_digest_auth_enabled = b._digest_auth_enabled; - this->_regex_checking = b._regex_checking; - this->_ban_system_enabled = b._ban_system_enabled; - this->_post_process_enabled = b._post_process_enabled; - this->_deferred_enabled = b._deferred_enabled; - this->_single_resource = b._single_resource; - this->_tcp_nodelay = b._tcp_nodelay; - this->_not_found_resource = b._not_found_resource; - this->_method_not_allowed_resource = b._method_not_allowed_resource; - this->_internal_error_resource = b._internal_error_resource; - - return *this; - } - - create_webserver& operator=(create_webserver&& b) - { - if (this == &b) return *this; - - this->_port = b._port; - this->_start_method = b._start_method; - this->_max_threads = b._max_threads; - this->_max_connections = b._max_connections; - this->_memory_limit = b._memory_limit; - this->_content_size_limit = b._content_size_limit; - this->_connection_timeout = b._connection_timeout; - this->_per_IP_connection_limit = b._per_IP_connection_limit; - this->_log_access = std::move(b._log_access); - this->_log_error = std::move(b._log_error); - this->_validator = std::move(b._validator); - this->_unescaper = std::move(b._unescaper); - this->_bind_address = std::move(b._bind_address); - this->_bind_socket = b._bind_socket; - this->_max_thread_stack_size = b._max_thread_stack_size; - this->_use_ssl = b._use_ssl; - this->_use_ipv6 = b._use_ipv6; - this->_debug = b._debug; - this->_pedantic = b._pedantic; - this->_https_mem_key = std::move(b._https_mem_key); - this->_https_mem_cert = std::move(b._https_mem_cert); - this->_https_mem_trust = std::move(b._https_mem_trust); - this->_https_priorities = std::move(b._https_priorities); - this->_cred_type = b._cred_type; - this->_digest_auth_random = std::move(b._digest_auth_random); - this->_nonce_nc_size = b._nonce_nc_size; - this->_default_policy = b._default_policy; - this->_basic_auth_enabled = b._basic_auth_enabled; - this->_digest_auth_enabled = b._digest_auth_enabled; - this->_regex_checking = b._regex_checking; - this->_ban_system_enabled = b._ban_system_enabled; - this->_post_process_enabled = b._post_process_enabled; - this->_deferred_enabled = b._deferred_enabled; - this->_single_resource = b._single_resource; - this->_tcp_nodelay = b._tcp_nodelay; - this->_not_found_resource = std::move(b._not_found_resource); - this->_method_not_allowed_resource = std::move(b._method_not_allowed_resource); - this->_internal_error_resource = std::move(b._internal_error_resource); - - return *this; - } + create_webserver() = default; + create_webserver(const create_webserver& b) = default; + create_webserver(create_webserver&& b) noexcept = default; + create_webserver& operator=(const create_webserver& b) = default; + create_webserver& operator=(create_webserver&& b) = default; explicit create_webserver(uint16_t port): - _port(port), - _start_method(http::http_utils::INTERNAL_SELECT), - _max_threads(0), - _max_connections(0), - _memory_limit(0), - _content_size_limit(static_cast(-1)), - _connection_timeout(DEFAULT_WS_TIMEOUT), - _per_IP_connection_limit(0), - _log_access(0x0), - _log_error(0x0), - _validator(0x0), - _unescaper(0x0), - _bind_address(0x0), - _bind_socket(0), - _max_thread_stack_size(0), - _use_ssl(false), - _use_ipv6(false), - _debug(false), - _pedantic(false), - _https_mem_key(""), - _https_mem_cert(""), - _https_mem_trust(""), - _https_priorities(""), - _cred_type(http::http_utils::NONE), - _digest_auth_random(""), - _nonce_nc_size(0), - _default_policy(http::http_utils::ACCEPT), - _basic_auth_enabled(true), - _digest_auth_enabled(true), - _regex_checking(true), - _ban_system_enabled(true), - _post_process_enabled(true), - _deferred_enabled(false), - _single_resource(false), - _tcp_nodelay(false), - _not_found_resource(0x0), - _method_not_allowed_resource(0x0), - _internal_error_resource(0x0) + _port(port) { } @@ -500,44 +251,44 @@ class create_webserver } private: - uint16_t _port; - http::http_utils::start_method_T _start_method; - int _max_threads; - int _max_connections; - int _memory_limit; - size_t _content_size_limit; - int _connection_timeout; - int _per_IP_connection_limit; - log_access_ptr _log_access; - log_error_ptr _log_error; - validator_ptr _validator; - unescaper_ptr _unescaper; - const struct sockaddr* _bind_address; - int _bind_socket; - int _max_thread_stack_size; - bool _use_ssl; - bool _use_ipv6; - bool _debug; - bool _pedantic; - std::string _https_mem_key; - std::string _https_mem_cert; - std::string _https_mem_trust; - std::string _https_priorities; - http::http_utils::cred_type_T _cred_type; - std::string _digest_auth_random; - int _nonce_nc_size; - http::http_utils::policy_T _default_policy; - bool _basic_auth_enabled; - bool _digest_auth_enabled; - bool _regex_checking; - bool _ban_system_enabled; - bool _post_process_enabled; - bool _deferred_enabled; - bool _single_resource; - bool _tcp_nodelay; - render_ptr _not_found_resource; - render_ptr _method_not_allowed_resource; - render_ptr _internal_error_resource; + uint16_t _port = DEFAULT_WS_PORT; + http::http_utils::start_method_T _start_method = http::http_utils::INTERNAL_SELECT; + int _max_threads = 0; + int _max_connections = 0; + int _memory_limit = 0; + size_t _content_size_limit = static_cast(-1); + int _connection_timeout = DEFAULT_WS_TIMEOUT; + int _per_IP_connection_limit = 0; + log_access_ptr _log_access = 0x0; + log_error_ptr _log_error = 0x0; + validator_ptr _validator = 0x0; + unescaper_ptr _unescaper = 0x0; + const struct sockaddr* _bind_address = 0x0; + int _bind_socket = 0; + int _max_thread_stack_size = 0; + bool _use_ssl = false; + bool _use_ipv6 = false; + bool _debug = false; + bool _pedantic = false; + std::string _https_mem_key = ""; + std::string _https_mem_cert = ""; + std::string _https_mem_trust = ""; + std::string _https_priorities = ""; + http::http_utils::cred_type_T _cred_type = http::http_utils::NONE; + std::string _digest_auth_random = ""; + int _nonce_nc_size = 0; + http::http_utils::policy_T _default_policy = http::http_utils::ACCEPT; + bool _basic_auth_enabled = true; + bool _digest_auth_enabled = true; + bool _regex_checking = true; + bool _ban_system_enabled = true; + bool _post_process_enabled = true; + bool _deferred_enabled = false; + bool _single_resource = false; + bool _tcp_nodelay = false; + render_ptr _not_found_resource = 0x0; + render_ptr _method_not_allowed_resource = 0x0; + render_ptr _internal_error_resource = 0x0; friend class webserver; }; diff --git a/src/httpserver/deferred_response.hpp b/src/httpserver/deferred_response.hpp index 02cc745d..32a97bfc 100644 --- a/src/httpserver/deferred_response.hpp +++ b/src/httpserver/deferred_response.hpp @@ -53,45 +53,12 @@ class deferred_response : public string_response { } - deferred_response(const deferred_response& other): - string_response(other), - cycle_callback(other.cycle_callback), - closure_data(other.closure_data) - { - } - - deferred_response(deferred_response&& other) noexcept: - string_response(std::move(other)), - cycle_callback(std::move(other.cycle_callback)), - closure_data(std::move(other.closure_data)) - { - } + deferred_response(const deferred_response& other) = default; + deferred_response(deferred_response&& other) noexcept = default; + deferred_response& operator=(const deferred_response& b) = default; + deferred_response& operator=(deferred_response&& b) = default; - deferred_response& operator=(const deferred_response& b) - { - if (this == &b) return *this; - - (string_response&) (*this) = b; - this->cycle_callback = b.cycle_callback; - this->closure_data = b.closure_data; - - return *this; - } - - deferred_response& operator=(deferred_response&& b) - { - if (this == &b) return *this; - - (string_response&) (*this) = std::move(b); - this->cycle_callback = std::move(b.cycle_callback); - this->closure_data = std::move(b.closure_data); - - return *this; - } - - ~deferred_response() - { - } + ~deferred_response() = default; MHD_Response* get_raw_response() { diff --git a/src/httpserver/details/modded_request.hpp b/src/httpserver/details/modded_request.hpp index df99ce8e..07d5f814 100644 --- a/src/httpserver/details/modded_request.hpp +++ b/src/httpserver/details/modded_request.hpp @@ -33,80 +33,25 @@ namespace details struct modded_request { - struct MHD_PostProcessor *pp; - std::string* complete_uri; - std::string* standardized_url; - webserver* ws; + struct MHD_PostProcessor *pp = 0x0; + std::string* complete_uri = 0x0; + std::string* standardized_url = 0x0; + webserver* ws = 0x0; const std::shared_ptr (httpserver::http_resource::*callback)(const httpserver::http_request&); - http_request* dhr; + http_request* dhr = 0x0; std::shared_ptr dhrs; - bool second; - bool has_body; - - modded_request(): - pp(0x0), - complete_uri(0x0), - standardized_url(0x0), - ws(0x0), - dhr(0x0), - second(false), - has_body(false) - { - } - - modded_request(const modded_request& b): - pp(b.pp), - complete_uri(b.complete_uri), - standardized_url(b.standardized_url), - ws(b.ws), - dhr(b.dhr), - second(b.second), - has_body(b.has_body) - { - } + bool second = false; + bool has_body = false; - modded_request(modded_request&& b): - pp(std::move(b.pp)), - complete_uri(std::move(b.complete_uri)), - standardized_url(std::move(b.standardized_url)), - ws(std::move(b.ws)), - dhr(std::move(b.dhr)), - second(b.second), - has_body(b.has_body) - { - } + modded_request() = default; - modded_request& operator=(const modded_request& b) - { - if (this == &b) return *this; + modded_request(const modded_request& b) = default; + modded_request(modded_request&& b) = default; - this->pp = b.pp; - this->complete_uri = b.complete_uri; - this->standardized_url = b.standardized_url; - this->ws = b.ws; - this->dhr = b.dhr; - this->second = b.second; - this->has_body = b.has_body; - - return *this; - } - - modded_request& operator=(modded_request&& b) - { - if (this == &b) return *this; - - this->pp = std::move(b.pp); - this->complete_uri = std::move(b.complete_uri); - this->standardized_url = std::move(b.standardized_url); - this->ws = std::move(b.ws); - this->dhr = std::move(b.dhr); - this->second = b.second; - this->has_body = b.has_body; - - return *this; - } + modded_request& operator=(const modded_request& b) = default; + modded_request& operator=(modded_request&& b) = default; ~modded_request() { diff --git a/src/httpserver/digest_auth_fail_response.hpp b/src/httpserver/digest_auth_fail_response.hpp index 63d02378..292f477b 100644 --- a/src/httpserver/digest_auth_fail_response.hpp +++ b/src/httpserver/digest_auth_fail_response.hpp @@ -33,13 +33,7 @@ namespace httpserver class digest_auth_fail_response : public string_response { public: - digest_auth_fail_response(): - string_response(), - realm(""), - opaque(""), - reload_nonce(false) - { - } + digest_auth_fail_response() = default; digest_auth_fail_response( const std::string& content, @@ -56,56 +50,19 @@ class digest_auth_fail_response : public string_response { } - digest_auth_fail_response(const digest_auth_fail_response& other): - string_response(other), - realm(other.realm), - opaque(other.opaque), - reload_nonce(other.reload_nonce) - { - } - - digest_auth_fail_response(digest_auth_fail_response&& other) noexcept: - string_response(std::move(other)), - realm(std::move(other.realm)), - opaque(std::move(other.opaque)), - reload_nonce(other.reload_nonce) - { - } - - digest_auth_fail_response& operator=(const digest_auth_fail_response& b) - { - if (this == &b) return *this; - - (string_response&) (*this) = b; - this->realm = b.realm; - this->opaque = b.opaque; - this->reload_nonce = b.reload_nonce; + digest_auth_fail_response(const digest_auth_fail_response& other) = default; + digest_auth_fail_response(digest_auth_fail_response&& other) noexcept = default; + digest_auth_fail_response& operator=(const digest_auth_fail_response& b) = default; + digest_auth_fail_response& operator=(digest_auth_fail_response&& b) = default; - return *this; - } - - digest_auth_fail_response& operator=(digest_auth_fail_response&& b) - { - if (this == &b) return *this; - - (string_response&) (*this) = std::move(b); - this->realm = std::move(b.realm); - this->opaque = std::move(b.opaque); - this->reload_nonce = b.reload_nonce; - - return *this; - } - - ~digest_auth_fail_response() - { - } + ~digest_auth_fail_response() = default; int enqueue_response(MHD_Connection* connection, MHD_Response* response); private: - std::string realm; - std::string opaque; - bool reload_nonce; + std::string realm = ""; + std::string opaque = ""; + bool reload_nonce = false; }; } diff --git a/src/httpserver/file_response.hpp b/src/httpserver/file_response.hpp index 1192d91a..cb80c114 100644 --- a/src/httpserver/file_response.hpp +++ b/src/httpserver/file_response.hpp @@ -33,11 +33,7 @@ namespace httpserver class file_response : public http_response { public: - file_response(): - http_response(), - filename("") - { - } + file_response() = default; explicit file_response( const std::string& filename, @@ -49,46 +45,18 @@ class file_response : public http_response { } - file_response(const file_response& other): - http_response(other), - filename(other.filename) - { - } - - file_response(file_response&& other) noexcept: - http_response(std::move(other)), - filename(std::move(other.filename)) - { - } - - file_response& operator=(const file_response& b) - { - if (this == &b) return *this; - - (http_response&) (*this) = b; - this->filename = b.filename; - - return *this; - } - - file_response& operator=(file_response&& b) - { - if (this == &b) return *this; + file_response(const file_response& other) = default; + file_response(file_response&& other) noexcept = default; - (http_response&) (*this) = std::move(b); - this->filename = std::move(b.filename); + file_response& operator=(const file_response& b) = default; + file_response& operator=(file_response&& b) = default; - return *this; - } - - ~file_response() - { - } + ~file_response() = default; MHD_Response* get_raw_response(); private: - std::string filename; + std::string filename = ""; }; } diff --git a/src/httpserver/http_request.hpp b/src/httpserver/http_request.hpp index 632fe06d..c96bebb6 100644 --- a/src/httpserver/http_request.hpp +++ b/src/httpserver/http_request.hpp @@ -216,17 +216,9 @@ class http_request /** * Default constructor of the class. It is a specific responsibility of apis to initialize this type of objects. **/ - http_request(): - content(""), - content_size_limit(static_cast(-1)), - underlying_connection(0x0), - unescaper(0x0) - { - } + http_request() = default; http_request(MHD_Connection* underlying_connection, unescaper_ptr unescaper): - content(""), - content_size_limit(static_cast(-1)), underlying_connection(underlying_connection), unescaper(unescaper) { @@ -236,69 +228,22 @@ class http_request * Copy constructor. * @param b http_request b to copy attributes from. **/ - http_request(const http_request& b): - path(b.path), - method(b.method), - args(b.args), - content(b.content), - content_size_limit(b.content_size_limit), - version(b.version), - underlying_connection(b.underlying_connection), - unescaper(b.unescaper) - { - } - - http_request(http_request&& b) noexcept: - path(std::move(b.path)), - method(std::move(b.method)), - args(std::move(b.args)), - content(std::move(b.content)), - content_size_limit(b.content_size_limit), - version(std::move(b.version)), - underlying_connection(std::move(b.underlying_connection)) - { - } - - http_request& operator=(const http_request& b) - { - if (this == &b) return *this; - - this->path = b.path; - this->method = b.method; - this->args = b.args; - this->content = b.content; - this->content_size_limit = b.content_size_limit; - this->version = b.version; - this->underlying_connection = b.underlying_connection; - - return *this; - } + http_request(const http_request& b) = default; + http_request(http_request&& b) noexcept = default; - http_request& operator=(http_request&& b) - { - if (this == &b) return *this; - - this->path = std::move(b.path); - this->method = std::move(b.method); - this->args = std::move(b.args); - this->content = std::move(b.content); - this->content_size_limit = b.content_size_limit; - this->version = std::move(b.version); - this->underlying_connection = std::move(b.underlying_connection); - - return *this; - } + http_request& operator=(const http_request& b) = default; + http_request& operator=(http_request&& b) = default; std::string path; std::string method; std::map args; - std::string content; - size_t content_size_limit; + std::string content = ""; + size_t content_size_limit = static_cast(-1); std::string version; - struct MHD_Connection* underlying_connection; + struct MHD_Connection* underlying_connection = 0x0; - unescaper_ptr unescaper; + unescaper_ptr unescaper = 0x0; static int build_request_header(void *cls, enum MHD_ValueKind kind, const char *key, const char *value diff --git a/src/httpserver/http_resource.hpp b/src/httpserver/http_resource.hpp index ae5e78d4..42f73498 100644 --- a/src/httpserver/http_resource.hpp +++ b/src/httpserver/http_resource.hpp @@ -59,10 +59,7 @@ class http_resource /** * Class destructor **/ - virtual ~http_resource() - { - allowed_methods.clear(); - } + virtual ~http_resource() = default; /** * Method used to answer to a generic request @@ -219,25 +216,10 @@ class http_resource /** * Copy constructor **/ - http_resource(const http_resource& b) : allowed_methods(b.allowed_methods) { } - - http_resource(http_resource&& b) noexcept: allowed_methods(std::move(b.allowed_methods)) { } - - http_resource& operator=(const http_resource& b) - { - if (this == &b) return *this; - - allowed_methods = b.allowed_methods; - return (*this); - } - - http_resource& operator=(http_resource&& b) - { - if (this == &b) return *this; - - allowed_methods = std::move(b.allowed_methods); - return (*this); - } + http_resource(const http_resource& b) = default; + http_resource(http_resource&& b) noexcept = default; + http_resource& operator=(const http_resource& b) = default; + http_resource& operator=(http_resource&& b) = default; private: friend class webserver; @@ -245,5 +227,5 @@ class http_resource std::map allowed_methods; }; -}; +} #endif diff --git a/src/httpserver/http_response.hpp b/src/httpserver/http_response.hpp index b7f9b9a9..755d6c7a 100644 --- a/src/httpserver/http_response.hpp +++ b/src/httpserver/http_response.hpp @@ -45,10 +45,7 @@ namespace httpserver class http_response { public: - http_response(): - response_code(-1) - { - } + http_response() = default; explicit http_response(int response_code, const std::string& content_type): response_code(response_code) @@ -60,49 +57,13 @@ class http_response * Copy constructor * @param b The http_response object to copy attributes value from. **/ - http_response(const http_response& b): - response_code(b.response_code), - headers(b.headers), - footers(b.footers), - cookies(b.cookies) - { - } - - http_response(http_response&& other) noexcept: - response_code(other.response_code), - headers(std::move(other.headers)), - footers(std::move(other.footers)), - cookies(std::move(other.cookies)) - { - } - - http_response& operator=(const http_response& b) - { - if (this == &b) return *this; + http_response(const http_response& b) = default; + http_response(http_response&& b) noexcept = default; - this->response_code = b.response_code; - this->headers = b.headers; - this->footers = b.footers; - this->cookies = b.cookies; + http_response& operator=(const http_response& b) = default; + http_response& operator=(http_response&& b) noexcept = default; - return *this; - } - - http_response& operator=(http_response&& b) - { - if (this == &b) return *this; - - this->response_code = b.response_code; - this->headers = std::move(b.headers); - this->footers = std::move(b.footers); - this->cookies = std::move(b.cookies); - - return *this; - } - - virtual ~http_response() - { - } + virtual ~http_response() = default; /** * Method used to get a specified header defined for the response @@ -183,8 +144,7 @@ class http_response virtual int enqueue_response(MHD_Connection* connection, MHD_Response* response); protected: - std::string content; - int response_code; + int response_code = -1; std::map headers; std::map footers; diff --git a/src/httpserver/string_response.hpp b/src/httpserver/string_response.hpp index 0397f38c..87f12c67 100644 --- a/src/httpserver/string_response.hpp +++ b/src/httpserver/string_response.hpp @@ -33,11 +33,7 @@ namespace httpserver class string_response : public http_response { public: - string_response(): - http_response(), - content("") - { - } + string_response() = default; explicit string_response( const std::string& content, @@ -49,46 +45,18 @@ class string_response : public http_response { } - string_response(const string_response& other): - http_response(other), - content(other.content) - { - } - - string_response(string_response&& other) noexcept: - http_response(std::move(other)), - content(std::move(other.content)) - { - } - - string_response& operator=(const string_response& b) - { - if (this == &b) return *this; - - (http_response&) (*this) = b; - this->content = b.content; - - return *this; - } - - string_response& operator=(string_response&& b) - { - if (this == &b) return *this; + string_response(const string_response& other) = default; + string_response(string_response&& other) noexcept = default; - (http_response&) (*this) = std::move(b); - this->content = std::move(b.content); + string_response& operator=(const string_response& b) = default; + string_response& operator=(string_response&& b) = default; - return *this; - } - - ~string_response() - { - } + ~string_response() = default; MHD_Response* get_raw_response(); private: - std::string content; + std::string content = ""; }; } From a7b9b6afa67ecbe27ea671d51c59ca0a42e09710 Mon Sep 17 00:00:00 2001 From: bcsgh <33939446+bcsgh@users.noreply.github.com> Date: Sat, 29 Feb 2020 17:37:57 -0800 Subject: [PATCH 470/623] Rename some variable so that all `this->` can be dropped, and do so. (#180) * Use default constructor and operator= where possible: etr/libhttpserver#177 * Remove some noexcept's from operator=(T&&) that newer versions of clang reject. * Remove more noexcept's from operator=(T&&) that newer versions of clang reject. * Remove more noexcept's from operator=(T&&) that newer versions of clang reject. * Remove more noexcept's from operator=(T&&) that newer versions of clang reject. * Revert some incedental changes for now. * Revert some incedental changes for now (http_request). * Refactor another class that got missed in the first pass. * Rename some variable so that all `this->` can be dropped, and do so. * Rename some variable so that all `this->` can be dropped, and do so. * Re-add the `this->` for setter methods. --- src/details/http_endpoint.cpp | 64 ++++++++++++------------ src/http_request.cpp | 16 +++--- src/http_response.cpp | 2 +- src/http_utils.cpp | 12 ++--- src/httpserver/deferred_response.hpp | 2 +- src/httpserver/details/http_endpoint.hpp | 14 +++--- src/httpserver/http_request.hpp | 17 +++---- src/httpserver/http_resource.hpp | 16 +++--- src/httpserver/http_response.hpp | 16 +++--- src/httpserver/webserver.hpp | 8 +-- src/webserver.cpp | 56 ++++++++++----------- 11 files changed, 111 insertions(+), 112 deletions(-) diff --git a/src/details/http_endpoint.cpp b/src/details/http_endpoint.cpp index 6c2ae1e0..6bf2a6ef 100644 --- a/src/details/http_endpoint.cpp +++ b/src/details/http_endpoint.cpp @@ -36,7 +36,7 @@ http_endpoint::~http_endpoint() { if(reg_compiled) { - regfree(&(this->re_url_normalized)); + regfree(&re_url_normalized); } } @@ -50,7 +50,7 @@ http_endpoint::http_endpoint family_url(family), reg_compiled(false) { - this->url_normalized = use_regex ? "^/" : "/"; + url_normalized = use_regex ? "^/" : "/"; vector parts; #ifdef CASE_INSENSITIVE @@ -77,10 +77,10 @@ http_endpoint::http_endpoint { if(!registration) { - this->url_normalized += (first ? "" : "/") + parts[i]; + url_normalized += (first ? "" : "/") + parts[i]; first = false; - this->url_pieces.push_back(parts[i]); + url_pieces.push_back(parts[i]); continue; } @@ -89,14 +89,14 @@ http_endpoint::http_endpoint { if(first) { - this->url_normalized = (parts[i][0] == '^' ? "" : this->url_normalized) + parts[i]; + url_normalized = (parts[i][0] == '^' ? "" : url_normalized) + parts[i]; first = false; } else { - this->url_normalized += "/" + parts[i]; + url_normalized += "/" + parts[i]; } - this->url_pieces.push_back(parts[i]); + url_pieces.push_back(parts[i]); continue; } @@ -105,23 +105,23 @@ http_endpoint::http_endpoint throw std::invalid_argument("Bad URL format"); std::string::size_type bar = parts[i].find_first_of('|'); - this->url_pars.push_back(parts[i].substr(1, bar != string::npos ? bar - 1 : parts[i].size() - 2)); - this->url_normalized += (first ? "" : "/") + (bar != string::npos ? parts[i].substr(bar + 1, parts[i].size() - bar - 2) : "([^\\/]+)"); + url_pars.push_back(parts[i].substr(1, bar != string::npos ? bar - 1 : parts[i].size() - 2)); + url_normalized += (first ? "" : "/") + (bar != string::npos ? parts[i].substr(bar + 1, parts[i].size() - bar - 2) : "([^\\/]+)"); first = false; - this->chunk_positions.push_back(i); + chunk_positions.push_back(i); - this->url_pieces.push_back(parts[i]); + url_pieces.push_back(parts[i]); } if(use_regex) { - this->url_normalized += "$"; - regcomp(&(this->re_url_normalized), url_normalized.c_str(), + url_normalized += "$"; + regcomp(&re_url_normalized, url_normalized.c_str(), REG_EXTENDED|REG_ICASE|REG_NOSUB ); - this->reg_compiled = true; + reg_compiled = true; } } @@ -134,52 +134,52 @@ http_endpoint::http_endpoint(const http_endpoint& h): family_url(h.family_url), reg_compiled(h.reg_compiled) { - if(this->reg_compiled) - regcomp(&(this->re_url_normalized), url_normalized.c_str(), + if(reg_compiled) + regcomp(&re_url_normalized, url_normalized.c_str(), REG_EXTENDED|REG_ICASE|REG_NOSUB ); } http_endpoint& http_endpoint::operator =(const http_endpoint& h) { - this->url_complete = h.url_complete; - this->url_normalized = h.url_normalized; - this->family_url = h.family_url; - this->reg_compiled = h.reg_compiled; - if(this->reg_compiled) + url_complete = h.url_complete; + url_normalized = h.url_normalized; + family_url = h.family_url; + reg_compiled = h.reg_compiled; + if(reg_compiled) { - regfree(&(this->re_url_normalized)); + regfree(&re_url_normalized); - regcomp(&(this->re_url_normalized), url_normalized.c_str(), + regcomp(&re_url_normalized, url_normalized.c_str(), REG_EXTENDED|REG_ICASE|REG_NOSUB ); } - this->url_pars = h.url_pars; - this->url_pieces = h.url_pieces; - this->chunk_positions = h.chunk_positions; + url_pars = h.url_pars; + url_pieces = h.url_pieces; + chunk_positions = h.chunk_positions; return *this; } bool http_endpoint::operator <(const http_endpoint& b) const { - COMPARATOR(this->url_normalized, b.url_normalized, std::toupper); + COMPARATOR(url_normalized, b.url_normalized, std::toupper); } bool http_endpoint::match(const http_endpoint& url) const { - if (!this->reg_compiled) throw std::invalid_argument("Cannot run match. Regex suppressed."); + if (!reg_compiled) throw std::invalid_argument("Cannot run match. Regex suppressed."); - if(!this->family_url || url.url_pieces.size() < this->url_pieces.size()) - return regexec(&(this->re_url_normalized), url.url_complete.c_str(), 0, NULL, 0) == 0; + if(!family_url || url.url_pieces.size() < url_pieces.size()) + return regexec(&re_url_normalized, url.url_complete.c_str(), 0, NULL, 0) == 0; string nn = "/"; bool first = true; - for(unsigned int i = 0; i < this->url_pieces.size(); i++) + for(unsigned int i = 0; i < url_pieces.size(); i++) { nn += (first ? "" : "/") + url.url_pieces[i]; first = false; } - return regexec(&(this->re_url_normalized), nn.c_str(), 0, NULL, 0) == 0; + return regexec(&re_url_normalized, nn.c_str(), 0, NULL, 0) == 0; } }; diff --git a/src/http_request.cpp b/src/http_request.cpp index 973ab338..b261f328 100644 --- a/src/http_request.cpp +++ b/src/http_request.cpp @@ -76,7 +76,7 @@ bool http_request::check_digest_auth( const std::string http_request::get_connection_value(const std::string& key, enum MHD_ValueKind kind) const { const char* header_c = MHD_lookup_connection_value( - this->underlying_connection, + underlying_connection, kind, key.c_str() ); @@ -103,7 +103,7 @@ const std::map http_request:: std::map headers; MHD_get_connection_values( - this->underlying_connection, + underlying_connection, kind, &build_request_header, (void*) &headers @@ -144,9 +144,9 @@ const std::map http_request:: const std::string http_request::get_arg(const std::string& key) const { - std::map::const_iterator it = this->args.find(key); + std::map::const_iterator it = args.find(key); - if(it != this->args.end()) + if(it != args.end()) { return it->second; } @@ -157,14 +157,14 @@ const std::string http_request::get_arg(const std::string& key) const const std::map http_request::get_args() const { std::map arguments; - arguments.insert(this->args.begin(), this->args.end()); + arguments.insert(args.begin(), args.end()); arguments_accumulator aa; - aa.unescaper = this->unescaper; + aa.unescaper = unescaper; aa.arguments = &arguments; MHD_get_connection_values( - this->underlying_connection, + underlying_connection, MHD_GET_ARGUMENT_KIND, &build_request_args, (void*) &aa @@ -178,7 +178,7 @@ const std::string http_request::get_querystring() const std::string querystring = ""; MHD_get_connection_values( - this->underlying_connection, + underlying_connection, MHD_GET_ARGUMENT_KIND, &build_request_querystring, (void*) &querystring diff --git a/src/http_response.cpp b/src/http_response.cpp index 35b83d50..2193d1f3 100644 --- a/src/http_response.cpp +++ b/src/http_response.cpp @@ -70,7 +70,7 @@ int http_response::enqueue_response(MHD_Connection* connection, MHD_Response* re void http_response::shoutCAST() { - this->response_code |= http::http_utils::shoutcast_response; + response_code |= http::http_utils::shoutcast_response; } std::ostream &operator<< (std::ostream &os, const http_response &r) diff --git a/src/http_utils.cpp b/src/http_utils.cpp index 0d00a05f..e549da99 100644 --- a/src/http_utils.cpp +++ b/src/http_utils.cpp @@ -517,16 +517,16 @@ bool ip_representation::operator <(const ip_representation& b) const { if (i == 10 || i == 11) continue; - if (CHECK_BIT(this->mask, i) && CHECK_BIT(b.mask, i)) + if (CHECK_BIT(mask, i) && CHECK_BIT(b.mask, i)) { - this_score += (16 - i) * this->pieces[i]; + this_score += (16 - i) * pieces[i]; b_score += (16 - i) * b.pieces[i]; } } if (this_score == b_score && - ((this->pieces[10] == 0x00 || this->pieces[10] == 0xFF) && (b.pieces[10] == 0x00 || b.pieces[10] == 0xFF)) && - ((this->pieces[11] == 0x00 || this->pieces[11] == 0xFF) && (b.pieces[11] == 0x00 || b.pieces[11] == 0xFF)) + ((pieces[10] == 0x00 || pieces[10] == 0xFF) && (b.pieces[10] == 0x00 || b.pieces[10] == 0xFF)) && + ((pieces[11] == 0x00 || pieces[11] == 0xFF) && (b.pieces[11] == 0x00 || b.pieces[11] == 0xFF)) ) { return false; @@ -534,9 +534,9 @@ bool ip_representation::operator <(const ip_representation& b) const for (int i = 10; i < 12; i++) { - if (CHECK_BIT(this->mask, i) && CHECK_BIT(b.mask, i)) + if (CHECK_BIT(mask, i) && CHECK_BIT(b.mask, i)) { - this_score += (16 - i) * this->pieces[i]; + this_score += (16 - i) * pieces[i]; b_score += (16 - i) * b.pieces[i]; } } diff --git a/src/httpserver/deferred_response.hpp b/src/httpserver/deferred_response.hpp index 32a97bfc..41398ee2 100644 --- a/src/httpserver/deferred_response.hpp +++ b/src/httpserver/deferred_response.hpp @@ -62,7 +62,7 @@ class deferred_response : public string_response MHD_Response* get_raw_response() { - return details::get_raw_response_helper((void*) this, &(this->cb)); + return details::get_raw_response_helper((void*) this, &cb); } private: diff --git a/src/httpserver/details/http_endpoint.hpp b/src/httpserver/details/http_endpoint.hpp index 2527f66c..ff0b5f26 100644 --- a/src/httpserver/details/http_endpoint.hpp +++ b/src/httpserver/details/http_endpoint.hpp @@ -85,12 +85,12 @@ class http_endpoint **/ const std::string& get_url_complete() const { - return this->url_complete; + return url_complete; } const std::string& get_url_normalized() const { - return this->url_normalized; + return url_normalized; } /** @@ -99,7 +99,7 @@ class http_endpoint **/ const std::vector& get_url_pars() const { - return this->url_pars; + return url_pars; } /** @@ -108,7 +108,7 @@ class http_endpoint **/ const std::vector& get_url_pieces() const { - return this->url_pieces; + return url_pieces; } /** @@ -117,17 +117,17 @@ class http_endpoint **/ const std::vector& get_chunk_positions() const { - return this->chunk_positions; + return chunk_positions; } const bool is_family_url() const { - return this->family_url; + return family_url; } const bool is_regex_compiled() const { - return this->reg_compiled; + return reg_compiled; } /** diff --git a/src/httpserver/http_request.hpp b/src/httpserver/http_request.hpp index c96bebb6..c056db6e 100644 --- a/src/httpserver/http_request.hpp +++ b/src/httpserver/http_request.hpp @@ -76,7 +76,7 @@ class http_request **/ const std::string& get_path() const { - return this->path; + return path; } /** @@ -85,7 +85,7 @@ class http_request **/ const std::vector get_path_pieces() const { - return http::http_utils::tokenize_url(this->path); + return http::http_utils::tokenize_url(path); } /** @@ -95,7 +95,7 @@ class http_request **/ const std::string get_path_piece(int index) const { - std::vector post_path = this->get_path_pieces(); + std::vector post_path = get_path_pieces(); if(((int)(post_path.size())) > index) return post_path[index]; return EMPTY; @@ -107,7 +107,7 @@ class http_request **/ const std::string& get_method() const { - return this->method; + return method; } /** @@ -167,7 +167,7 @@ class http_request **/ const std::string& get_content() const { - return this->content; + return content; } /** @@ -190,7 +190,7 @@ class http_request **/ const std::string& get_version() const { - return this->version; + return version; } /** @@ -264,7 +264,7 @@ class http_request **/ void set_arg(const std::string& key, const std::string& value) { - this->args[key] = value.substr(0,content_size_limit); + args[key] = value.substr(0,content_size_limit); } /** @@ -275,8 +275,7 @@ class http_request **/ void set_arg(const char* key, const char* value, size_t size) { - this->args[key] = std::string(value, - std::min(size, content_size_limit)); + args[key] = std::string(value, std::min(size, content_size_limit)); } /** diff --git a/src/httpserver/http_resource.hpp b/src/httpserver/http_resource.hpp index 42f73498..ba2ad750 100644 --- a/src/httpserver/http_resource.hpp +++ b/src/httpserver/http_resource.hpp @@ -158,9 +158,9 @@ class http_resource **/ void set_allowing(const std::string& method, bool allowed) { - if(this->allowed_methods.count(method)) + if(allowed_methods.count(method)) { - this->allowed_methods[method] = allowed; + allowed_methods[method] = allowed; } } /** @@ -169,8 +169,8 @@ class http_resource void allow_all() { std::map::iterator it; - for ( it=this->allowed_methods.begin() ; it != this->allowed_methods.end(); ++it ) - this->allowed_methods[(*it).first] = true; + for ( it=allowed_methods.begin() ; it != allowed_methods.end(); ++it ) + allowed_methods[(*it).first] = true; } /** * Method used to implicitly disallow all methods @@ -178,8 +178,8 @@ class http_resource void disallow_all() { std::map::iterator it; - for ( it=this->allowed_methods.begin() ; it != this->allowed_methods.end(); ++it ) - this->allowed_methods[(*it).first] = false; + for ( it=allowed_methods.begin() ; it != allowed_methods.end(); ++it ) + allowed_methods[(*it).first] = false; } /** * Method used to discover if an http method is allowed or not for this resource @@ -188,9 +188,9 @@ class http_resource **/ bool is_allowed(const std::string& method) { - if(this->allowed_methods.count(method)) + if(allowed_methods.count(method)) { - return this->allowed_methods[method]; + return allowed_methods[method]; } else { diff --git a/src/httpserver/http_response.hpp b/src/httpserver/http_response.hpp index 755d6c7a..9f247291 100644 --- a/src/httpserver/http_response.hpp +++ b/src/httpserver/http_response.hpp @@ -50,7 +50,7 @@ class http_response explicit http_response(int response_code, const std::string& content_type): response_code(response_code) { - this->headers[http::http_utils::http_header_content_type] = content_type; + headers[http::http_utils::http_header_content_type] = content_type; } /** @@ -72,7 +72,7 @@ class http_response **/ const std::string& get_header(const std::string& key) { - return this->headers[key]; + return headers[key]; } /** @@ -82,12 +82,12 @@ class http_response **/ const std::string& get_footer(const std::string& key) { - return this->footers[key]; + return footers[key]; } const std::string& get_cookie(const std::string& key) { - return this->cookies[key]; + return cookies[key]; } /** @@ -96,7 +96,7 @@ class http_response **/ const std::map& get_headers() const { - return this->headers; + return headers; } /** @@ -105,12 +105,12 @@ class http_response **/ const std::map& get_footers() const { - return this->footers; + return footers; } const std::map& get_cookies() const { - return this->cookies; + return cookies; } /** @@ -119,7 +119,7 @@ class http_response **/ int get_response_code() const { - return this->response_code; + return response_code; } void with_header(const std::string& key, const std::string& value) diff --git a/src/httpserver/webserver.hpp b/src/httpserver/webserver.hpp index 93b2e8b5..dff0392c 100644 --- a/src/httpserver/webserver.hpp +++ b/src/httpserver/webserver.hpp @@ -110,22 +110,22 @@ class webserver log_access_ptr get_access_logger() const { - return this->log_access; + return log_access; } log_error_ptr get_error_logger() const { - return this->log_error; + return log_error; } validator_ptr get_request_validator() const { - return this->validator; + return validator; } unescaper_ptr get_unescaper() const { - return this->unescaper; + return unescaper; } /** diff --git a/src/webserver.cpp b/src/webserver.cpp index 9b4d03a1..939ed342 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -164,7 +164,7 @@ webserver::webserver(const create_webserver& params): webserver::~webserver() { - this->stop(); + stop(); pthread_mutex_destroy(&mutexwait); pthread_rwlock_destroy(&runguard); pthread_cond_destroy(&mutexcond); @@ -172,7 +172,7 @@ webserver::~webserver() void webserver::sweet_kill() { - this->stop(); + stop(); } void webserver::request_completed ( @@ -308,16 +308,16 @@ bool webserver::start(bool blocking) start_conf |= MHD_USE_TCP_FASTOPEN; #endif - this->daemon = NULL; + daemon = NULL; if(bind_address == 0x0) { - this->daemon = MHD_start_daemon + daemon = MHD_start_daemon ( - start_conf, this->port, &policy_callback, this, + start_conf, port, &policy_callback, this, &answer_to_connection, this, MHD_OPTION_ARRAY, &iov[0], MHD_OPTION_END ); } else { - this->daemon = MHD_start_daemon + daemon = MHD_start_daemon ( start_conf, 1, &policy_callback, this, &answer_to_connection, this, MHD_OPTION_ARRAY, @@ -325,14 +325,14 @@ bool webserver::start(bool blocking) ); } - if(this->daemon == NULL) + if(daemon == NULL) { - throw std::invalid_argument("Unable to connect daemon to port: " + std::to_string(this->port)); + throw std::invalid_argument("Unable to connect daemon to port: " + std::to_string(port)); } bool value_onclose = false; - this->running = true; + running = true; if(blocking) { @@ -347,19 +347,19 @@ bool webserver::start(bool blocking) bool webserver::is_running() { - return this->running; + return running; } bool webserver::stop() { - if(!this->running) return false; + if(!running) return false; pthread_mutex_lock(&mutexwait); - this->running = false; + running = false; pthread_cond_signal(&mutexcond); pthread_mutex_unlock(&mutexwait); - MHD_stop_daemon(this->daemon); + MHD_stop_daemon(daemon); shutdown(bind_socket, 2); @@ -369,45 +369,45 @@ bool webserver::stop() void webserver::unregister_resource(const string& resource) { details::http_endpoint he(resource); - this->registered_resources.erase(he); - this->registered_resources.erase(he.get_url_complete()); - this->registered_resources_str.erase(he.get_url_complete()); + registered_resources.erase(he); + registered_resources.erase(he.get_url_complete()); + registered_resources_str.erase(he.get_url_complete()); } void webserver::ban_ip(const string& ip) { ip_representation t_ip(ip); - set::iterator it = this->bans.find(t_ip); - if(it != this->bans.end() && (t_ip.weight() < (*it).weight())) + set::iterator it = bans.find(t_ip); + if(it != bans.end() && (t_ip.weight() < (*it).weight())) { - this->bans.erase(it); - this->bans.insert(t_ip); + bans.erase(it); + bans.insert(t_ip); } else - this->bans.insert(t_ip); + bans.insert(t_ip); } void webserver::allow_ip(const string& ip) { ip_representation t_ip(ip); - set::iterator it = this->allowances.find(t_ip); - if(it != this->allowances.end() && (t_ip.weight() < (*it).weight())) + set::iterator it = allowances.find(t_ip); + if(it != allowances.end() && (t_ip.weight() < (*it).weight())) { - this->allowances.erase(it); - this->allowances.insert(t_ip); + allowances.erase(it); + allowances.insert(t_ip); } else - this->allowances.insert(t_ip); + allowances.insert(t_ip); } void webserver::unban_ip(const string& ip) { - this->bans.erase(ip); + bans.erase(ip); } void webserver::disallow_ip(const string& ip) { - this->allowances.erase(ip); + allowances.erase(ip); } int policy_callback (void *cls, const struct sockaddr* addr, socklen_t addrlen) From 04a23c871b5358e18dd76a220ac715817934eaa9 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sat, 29 Feb 2020 19:56:22 -0800 Subject: [PATCH 471/623] Move http_response class members to private. (#181) They never should have been protected in the first place, as they are accessible through class methods. --- src/httpserver/http_response.hpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/httpserver/http_response.hpp b/src/httpserver/http_response.hpp index 9f247291..6cf8c2b9 100644 --- a/src/httpserver/http_response.hpp +++ b/src/httpserver/http_response.hpp @@ -143,13 +143,14 @@ class http_response virtual void decorate_response(MHD_Response* response); virtual int enqueue_response(MHD_Connection* connection, MHD_Response* response); - protected: + private: int response_code = -1; std::map headers; std::map footers; std::map cookies; + protected: friend std::ostream &operator<< (std::ostream &os, const http_response &r); }; From dd7469fd3e4720f7c4898a1177d345b0cda47b54 Mon Sep 17 00:00:00 2001 From: bcsgh <33939446+bcsgh@users.noreply.github.com> Date: Sat, 7 Mar 2020 08:37:07 -0800 Subject: [PATCH 472/623] Make #include for internal headers always use fully qualified paths (#183) --- src/basic_auth_fail_response.cpp | 2 +- src/deferred_response.cpp | 2 +- src/details/http_endpoint.cpp | 6 +++--- src/digest_auth_fail_response.cpp | 2 +- src/file_response.cpp | 2 +- src/http_request.cpp | 6 +++--- src/http_resource.cpp | 14 +++++++------- src/http_response.cpp | 6 +++--- src/http_utils.cpp | 4 ++-- src/httpserver/webserver.hpp | 2 +- src/string_response.cpp | 2 +- src/string_utilities.cpp | 2 +- src/webserver.cpp | 20 ++++++++++---------- test/integ/ban_system.cpp | 2 +- test/integ/basic.cpp | 2 +- test/unit/http_endpoint_test.cpp | 2 +- test/unit/http_utils_test.cpp | 2 +- test/unit/string_utilities_test.cpp | 2 +- 18 files changed, 40 insertions(+), 40 deletions(-) diff --git a/src/basic_auth_fail_response.cpp b/src/basic_auth_fail_response.cpp index b49dcc16..500fceb6 100644 --- a/src/basic_auth_fail_response.cpp +++ b/src/basic_auth_fail_response.cpp @@ -18,7 +18,7 @@ USA */ -#include "basic_auth_fail_response.hpp" +#include "httpserver/basic_auth_fail_response.hpp" using namespace std; diff --git a/src/deferred_response.cpp b/src/deferred_response.cpp index c1547962..08033c80 100644 --- a/src/deferred_response.cpp +++ b/src/deferred_response.cpp @@ -18,7 +18,7 @@ USA */ -#include "deferred_response.hpp" +#include "httpserver/deferred_response.hpp" using namespace std; diff --git a/src/details/http_endpoint.cpp b/src/details/http_endpoint.cpp index 6bf2a6ef..d48f8908 100644 --- a/src/details/http_endpoint.cpp +++ b/src/details/http_endpoint.cpp @@ -18,9 +18,9 @@ USA */ -#include "details/http_endpoint.hpp" -#include "http_utils.hpp" -#include "string_utilities.hpp" +#include "httpserver/details/http_endpoint.hpp" +#include "httpserver/http_utils.hpp" +#include "httpserver/string_utilities.hpp" using namespace std; diff --git a/src/digest_auth_fail_response.cpp b/src/digest_auth_fail_response.cpp index cc2c6201..d2fd5263 100644 --- a/src/digest_auth_fail_response.cpp +++ b/src/digest_auth_fail_response.cpp @@ -18,7 +18,7 @@ USA */ -#include "digest_auth_fail_response.hpp" +#include "httpserver/digest_auth_fail_response.hpp" using namespace std; diff --git a/src/file_response.cpp b/src/file_response.cpp index 7de47b1e..3729e027 100644 --- a/src/file_response.cpp +++ b/src/file_response.cpp @@ -19,7 +19,7 @@ */ #include -#include "file_response.hpp" +#include "httpserver/file_response.hpp" using namespace std; diff --git a/src/http_request.cpp b/src/http_request.cpp index b261f328..a81ed1d3 100644 --- a/src/http_request.cpp +++ b/src/http_request.cpp @@ -19,9 +19,9 @@ */ -#include "http_utils.hpp" -#include "http_request.hpp" -#include "string_utilities.hpp" +#include "httpserver/http_utils.hpp" +#include "httpserver/http_request.hpp" +#include "httpserver/string_utilities.hpp" #include using namespace std; diff --git a/src/http_resource.cpp b/src/http_resource.cpp index 2fe71a78..00703306 100644 --- a/src/http_resource.cpp +++ b/src/http_resource.cpp @@ -20,13 +20,13 @@ #include -#include "http_resource.hpp" -#include "http_utils.hpp" -#include "http_request.hpp" -#include "http_response.hpp" -#include "webserver.hpp" -#include "string_utilities.hpp" -#include "string_response.hpp" +#include "httpserver/http_resource.hpp" +#include "httpserver/http_utils.hpp" +#include "httpserver/http_request.hpp" +#include "httpserver/http_response.hpp" +#include "httpserver/webserver.hpp" +#include "httpserver/string_utilities.hpp" +#include "httpserver/string_response.hpp" using namespace std; diff --git a/src/http_response.cpp b/src/http_response.cpp index 2193d1f3..279ed379 100644 --- a/src/http_response.cpp +++ b/src/http_response.cpp @@ -24,9 +24,9 @@ #include #include #include -#include "http_utils.hpp" -#include "webserver.hpp" -#include "http_response.hpp" +#include "httpserver/http_utils.hpp" +#include "httpserver/webserver.hpp" +#include "httpserver/http_response.hpp" using namespace std; diff --git a/src/http_utils.cpp b/src/http_utils.cpp index e549da99..10da0fc2 100644 --- a/src/http_utils.cpp +++ b/src/http_utils.cpp @@ -37,8 +37,8 @@ #include #include #include -#include "string_utilities.hpp" -#include "http_utils.hpp" +#include "httpserver/string_utilities.hpp" +#include "httpserver/http_utils.hpp" #pragma GCC diagnostic ignored "-Warray-bounds" #define CHECK_BIT(var,pos) ((var) & (1<<(pos))) diff --git a/src/httpserver/webserver.hpp b/src/httpserver/webserver.hpp index dff0392c..47ef6b10 100644 --- a/src/httpserver/webserver.hpp +++ b/src/httpserver/webserver.hpp @@ -46,7 +46,7 @@ #include "httpserver/create_webserver.hpp" #include "httpserver/http_response.hpp" -#include "details/http_endpoint.hpp" +#include "httpserver/details/http_endpoint.hpp" namespace httpserver { diff --git a/src/string_response.cpp b/src/string_response.cpp index ad0a1a6c..7720d61b 100644 --- a/src/string_response.cpp +++ b/src/string_response.cpp @@ -18,7 +18,7 @@ USA */ -#include "string_response.hpp" +#include "httpserver/string_response.hpp" using namespace std; diff --git a/src/string_utilities.cpp b/src/string_utilities.cpp index 15f3743c..69ec8801 100644 --- a/src/string_utilities.cpp +++ b/src/string_utilities.cpp @@ -27,7 +27,7 @@ #include #include #include -#include "string_utilities.hpp" +#include "httpserver/string_utilities.hpp" namespace httpserver { diff --git a/src/webserver.cpp b/src/webserver.cpp index 939ed342..f30f1ca7 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -46,16 +46,16 @@ #include #include "gettext.h" -#include "http_utils.hpp" -#include "http_resource.hpp" -#include "http_response.hpp" -#include "string_response.hpp" -#include "http_request.hpp" -#include "details/http_endpoint.hpp" -#include "string_utilities.hpp" -#include "create_webserver.hpp" -#include "webserver.hpp" -#include "details/modded_request.hpp" +#include "httpserver/http_utils.hpp" +#include "httpserver/http_resource.hpp" +#include "httpserver/http_response.hpp" +#include "httpserver/string_response.hpp" +#include "httpserver/http_request.hpp" +#include "httpserver/details/http_endpoint.hpp" +#include "httpserver/string_utilities.hpp" +#include "httpserver/create_webserver.hpp" +#include "httpserver/webserver.hpp" +#include "httpserver/details/modded_request.hpp" #define _REENTRANT 1 diff --git a/test/integ/ban_system.cpp b/test/integ/ban_system.cpp index 505c7638..e92dbb75 100644 --- a/test/integ/ban_system.cpp +++ b/test/integ/ban_system.cpp @@ -23,7 +23,7 @@ #include #include #include "httpserver.hpp" -#include "http_utils.hpp" +#include "httpserver/http_utils.hpp" using namespace httpserver; using namespace std; diff --git a/test/integ/basic.cpp b/test/integ/basic.cpp index 767a8ecd..60c30c41 100644 --- a/test/integ/basic.cpp +++ b/test/integ/basic.cpp @@ -23,7 +23,7 @@ #include #include #include -#include "string_utilities.hpp" +#include "httpserver/string_utilities.hpp" #include "httpserver.hpp" using namespace httpserver; diff --git a/test/unit/http_endpoint_test.cpp b/test/unit/http_endpoint_test.cpp index 1075710b..bf0294f6 100644 --- a/test/unit/http_endpoint_test.cpp +++ b/test/unit/http_endpoint_test.cpp @@ -19,7 +19,7 @@ */ #include "littletest.hpp" -#include "details/http_endpoint.hpp" +#include "httpserver/details/http_endpoint.hpp" using namespace httpserver; using namespace std; diff --git a/test/unit/http_utils_test.cpp b/test/unit/http_utils_test.cpp index 5f7a9c7e..9bb203b7 100644 --- a/test/unit/http_utils_test.cpp +++ b/test/unit/http_utils_test.cpp @@ -29,7 +29,7 @@ #endif #include "littletest.hpp" -#include "http_utils.hpp" +#include "httpserver/http_utils.hpp" #include diff --git a/test/unit/string_utilities_test.cpp b/test/unit/string_utilities_test.cpp index 49913e67..2d3fd83d 100644 --- a/test/unit/string_utilities_test.cpp +++ b/test/unit/string_utilities_test.cpp @@ -19,7 +19,7 @@ */ #include "littletest.hpp" -#include "string_utilities.hpp" +#include "httpserver/string_utilities.hpp" #include From cc0bf88c70154688222d210a4f04555d27552342 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sat, 7 Mar 2020 08:43:15 -0800 Subject: [PATCH 473/623] Fixed links in readme Updated location of the zencoders website. Updated link to code of conduct --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 825850e5..473b8b2c 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ libhttpserver is built upon [libmicrohttpd](https://www.gnu.org/software/libmic #### Community * [Code of Conduct (on a separate page)](https://github.com/etr/libhttpserver/blob/master/CODE_OF_CONDUCT.md) -* [Contributing (on a separate page)](https://github.com/etr/libhttpserver/blob/master/CODE_OF_CONDUCT.md) +* [Contributing (on a separate page)](https://github.com/etr/libhttpserver/blob/master/CONTRIBUTING.md) #### Appendices * [Copying statement](#copying) @@ -1888,7 +1888,7 @@ to permit their use in free software. This library has been originally developed under the zencoders flags and this community has always supported me all along this work so I am happy to put the logo on this readme. - When you see this tree, know that you've came across ZenCoders.org + When you see this tree, know that you've came across ZenCoders with open('ZenCoders. `num` in numbers synchronized @@ -1932,7 +1932,7 @@ This library has been originally developed under the zencoders flags and this co 010010010010110101001010 For further information: -visit our website www.zencoders.org +visit our website https://zencoders.github.io **Author:** Sebastiano Merlino From 872bf540b32950e4cd0c173f86e754c13f398e48 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sat, 7 Mar 2020 08:44:48 -0800 Subject: [PATCH 474/623] Fixed e-mail Moved to the maintainer personal e-mail. --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 043ad528..ee018af3 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -25,7 +25,7 @@ The following is a set of guidelines for contributing to libhttpserver. These ar ## Code of Conduct -This project and everyone participating in it is governed by the [Code of Conduct](CODE_OF_CONDUCT.md). By participating, you are expected to uphold this code. Please report unacceptable behavior to [etr@zencoders.org](mailto:etr@zencoders.org). +This project and everyone participating in it is governed by the [Code of Conduct](CODE_OF_CONDUCT.md). By participating, you are expected to uphold this code. Please report unacceptable behavior to [merlino.sebastiano@gmail.com](mailto:merlino.sebastiano@gmail.com). ## I don't want to read this whole thing I just have a question!!! From a53647ed3ae2b3ba2a3435245b389d06fe91f458 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sat, 7 Mar 2020 08:45:55 -0800 Subject: [PATCH 475/623] Updated e-mail with the maintainer's personal one. --- CODE_OF_CONDUCT.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 5eb1c639..565a76c1 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -55,7 +55,7 @@ further defined and clarified by project maintainers. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be -reported by contacting the project team at etr@zencoders.org. All +reported by contacting the project team at merlino.sebastiano@gmail.com. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. From ee2f2582b6c20d7b0335a894c1e2f788b43e03db Mon Sep 17 00:00:00 2001 From: bcsgh <33939446+bcsgh@users.noreply.github.com> Date: Sat, 7 Mar 2020 14:12:49 -0800 Subject: [PATCH 476/623] Sort include (using the Google C++ style pattern). Also add some more includes where indirection inclusions were broken. (#184) --- examples/benchmark_nodelay.cpp | 3 +- examples/benchmark_select.cpp | 3 +- examples/benchmark_threads.cpp | 3 +- examples/custom_access_log.cpp | 3 +- examples/deferred_with_accumulator.cpp | 3 +- examples/hello_world.cpp | 3 +- examples/service.cpp | 6 ++-- src/details/http_endpoint.cpp | 1 + src/file_response.cpp | 3 +- src/http_request.cpp | 6 ++-- src/http_resource.cpp | 9 +++--- src/http_response.cpp | 8 +++-- src/http_utils.cpp | 15 +++++---- src/httpserver.hpp | 12 +++---- src/httpserver/create_webserver.hpp | 3 +- src/httpserver/deferred_response.hpp | 1 + src/httpserver/details/http_endpoint.hpp | 6 ++-- src/httpserver/details/modded_request.hpp | 2 ++ src/httpserver/http_request.hpp | 6 ++-- src/httpserver/http_resource.hpp | 6 ++-- src/httpserver/http_response.hpp | 7 ++-- src/httpserver/http_utils.hpp | 14 ++++---- src/httpserver/webserver.hpp | 16 ++++------ src/string_utilities.cpp | 13 ++++---- src/webserver.cpp | 39 +++++++++++------------ test/integ/authentication.cpp | 3 +- test/integ/ban_system.cpp | 5 +-- test/integ/basic.cpp | 9 +++--- test/integ/deferred.cpp | 7 ++-- test/integ/nodelay.cpp | 5 +-- test/integ/threaded.cpp | 5 +-- test/integ/ws_start_stop.cpp | 5 +-- test/littletest.hpp | 6 ++-- test/unit/http_endpoint_test.cpp | 3 +- test/unit/http_utils_test.cpp | 7 ++-- test/unit/string_utilities_test.cpp | 3 +- 36 files changed, 140 insertions(+), 109 deletions(-) diff --git a/examples/benchmark_nodelay.cpp b/examples/benchmark_nodelay.cpp index a67e95bf..418f9f1c 100755 --- a/examples/benchmark_nodelay.cpp +++ b/examples/benchmark_nodelay.cpp @@ -1,7 +1,8 @@ -#include #include #include +#include + #define PATH "/plaintext" #define BODY "Hello, World!" diff --git a/examples/benchmark_select.cpp b/examples/benchmark_select.cpp index 079b5395..62a18140 100755 --- a/examples/benchmark_select.cpp +++ b/examples/benchmark_select.cpp @@ -1,7 +1,8 @@ -#include #include #include +#include + #define PATH "/plaintext" #define BODY "Hello, World!" diff --git a/examples/benchmark_threads.cpp b/examples/benchmark_threads.cpp index ba806c3d..827b1c35 100755 --- a/examples/benchmark_threads.cpp +++ b/examples/benchmark_threads.cpp @@ -1,7 +1,8 @@ -#include #include #include +#include + #define PATH "/plaintext" #define BODY "Hello, World!" diff --git a/examples/custom_access_log.cpp b/examples/custom_access_log.cpp index 7e5f1ef5..afbc1774 100644 --- a/examples/custom_access_log.cpp +++ b/examples/custom_access_log.cpp @@ -18,9 +18,10 @@ USA */ -#include #include +#include + using namespace httpserver; void custom_access_log(const std::string& url) { diff --git a/examples/deferred_with_accumulator.cpp b/examples/deferred_with_accumulator.cpp index d114eb2a..06578415 100644 --- a/examples/deferred_with_accumulator.cpp +++ b/examples/deferred_with_accumulator.cpp @@ -18,9 +18,10 @@ USA */ +#include #include #include -#include + #include using namespace httpserver; diff --git a/examples/hello_world.cpp b/examples/hello_world.cpp index f9675b32..6f8b4aad 100755 --- a/examples/hello_world.cpp +++ b/examples/hello_world.cpp @@ -18,9 +18,10 @@ USA */ -#include #include +#include + using namespace httpserver; class hello_world_resource : public http_resource { diff --git a/examples/service.cpp b/examples/service.cpp index b2d365fc..e20c9a11 100644 --- a/examples/service.cpp +++ b/examples/service.cpp @@ -18,10 +18,12 @@ USA */ -#include -#include #include + #include +#include + +#include using namespace httpserver; diff --git a/src/details/http_endpoint.cpp b/src/details/http_endpoint.cpp index d48f8908..490788c2 100644 --- a/src/details/http_endpoint.cpp +++ b/src/details/http_endpoint.cpp @@ -19,6 +19,7 @@ */ #include "httpserver/details/http_endpoint.hpp" + #include "httpserver/http_utils.hpp" #include "httpserver/string_utilities.hpp" diff --git a/src/file_response.cpp b/src/file_response.cpp index 3729e027..1503e4bd 100644 --- a/src/file_response.cpp +++ b/src/file_response.cpp @@ -18,9 +18,10 @@ USA */ -#include #include "httpserver/file_response.hpp" +#include + using namespace std; namespace httpserver diff --git a/src/http_request.cpp b/src/http_request.cpp index a81ed1d3..57036639 100644 --- a/src/http_request.cpp +++ b/src/http_request.cpp @@ -19,11 +19,13 @@ */ -#include "httpserver/http_utils.hpp" #include "httpserver/http_request.hpp" -#include "httpserver/string_utilities.hpp" + #include +#include "httpserver/http_utils.hpp" +#include "httpserver/string_utilities.hpp" + using namespace std; namespace httpserver diff --git a/src/http_resource.cpp b/src/http_resource.cpp index 00703306..0fc6402a 100644 --- a/src/http_resource.cpp +++ b/src/http_resource.cpp @@ -18,15 +18,16 @@ */ +#include "httpserver/http_resource.hpp" + #include -#include "httpserver/http_resource.hpp" -#include "httpserver/http_utils.hpp" #include "httpserver/http_request.hpp" #include "httpserver/http_response.hpp" -#include "httpserver/webserver.hpp" -#include "httpserver/string_utilities.hpp" +#include "httpserver/http_utils.hpp" #include "httpserver/string_response.hpp" +#include "httpserver/string_utilities.hpp" +#include "httpserver/webserver.hpp" using namespace std; diff --git a/src/http_response.cpp b/src/http_response.cpp index 279ed379..c502f2ac 100644 --- a/src/http_response.cpp +++ b/src/http_response.cpp @@ -18,15 +18,17 @@ USA */ +#include "httpserver/http_response.hpp" + +#include +#include #include #include #include #include -#include -#include + #include "httpserver/http_utils.hpp" #include "httpserver/webserver.hpp" -#include "httpserver/http_response.hpp" using namespace std; diff --git a/src/http_utils.cpp b/src/http_utils.cpp index 10da0fc2..ae577a9c 100644 --- a/src/http_utils.cpp +++ b/src/http_utils.cpp @@ -18,9 +18,8 @@ USA */ -#include -#include -#include +#include "httpserver/http_utils.hpp" + #if defined(__MINGW32__) || defined(__CYGWIN32__) #define _WINDOWS #undef _WIN32_WINNT @@ -32,13 +31,17 @@ #include #include #endif -#include -#include + +#include +#include +#include #include +#include #include +#include #include + #include "httpserver/string_utilities.hpp" -#include "httpserver/http_utils.hpp" #pragma GCC diagnostic ignored "-Warray-bounds" #define CHECK_BIT(var,pos) ((var) & (1<<(pos))) diff --git a/src/httpserver.hpp b/src/httpserver.hpp index 2d0e5b0e..772554af 100644 --- a/src/httpserver.hpp +++ b/src/httpserver.hpp @@ -23,17 +23,15 @@ #define _HTTPSERVER_HPP_INSIDE_ -#include "httpserver/http_utils.hpp" -#include "httpserver/http_resource.hpp" -#include "httpserver/http_response.hpp" - -#include "httpserver/string_response.hpp" #include "httpserver/basic_auth_fail_response.hpp" -#include "httpserver/digest_auth_fail_response.hpp" #include "httpserver/deferred_response.hpp" +#include "httpserver/digest_auth_fail_response.hpp" #include "httpserver/file_response.hpp" - #include "httpserver/http_request.hpp" +#include "httpserver/http_resource.hpp" +#include "httpserver/http_response.hpp" +#include "httpserver/http_utils.hpp" +#include "httpserver/string_response.hpp" #include "httpserver/webserver.hpp" #endif diff --git a/src/httpserver/create_webserver.hpp b/src/httpserver/create_webserver.hpp index af1b93b6..e8d1a018 100644 --- a/src/httpserver/create_webserver.hpp +++ b/src/httpserver/create_webserver.hpp @@ -26,8 +26,9 @@ #define _CREATE_WEBSERVER_HPP_ #include -#include "httpserver/http_utils.hpp" + #include "httpserver/http_response.hpp" +#include "httpserver/http_utils.hpp" #define DEFAULT_WS_TIMEOUT 180 #define DEFAULT_WS_PORT 9898 diff --git a/src/httpserver/deferred_response.hpp b/src/httpserver/deferred_response.hpp index 41398ee2..170d5e82 100644 --- a/src/httpserver/deferred_response.hpp +++ b/src/httpserver/deferred_response.hpp @@ -26,6 +26,7 @@ #define _DEFERRED_RESPONSE_HPP_ #include + #include "httpserver/string_response.hpp" namespace httpserver diff --git a/src/httpserver/details/http_endpoint.hpp b/src/httpserver/details/http_endpoint.hpp index ff0b5f26..8264d6cc 100644 --- a/src/httpserver/details/http_endpoint.hpp +++ b/src/httpserver/details/http_endpoint.hpp @@ -25,11 +25,11 @@ #ifndef _HTTP_ENDPOINT_HPP_ #define _HTTP_ENDPOINT_HPP_ -#include -#include #include -#include #include +#include +#include +#include namespace httpserver { diff --git a/src/httpserver/details/modded_request.hpp b/src/httpserver/details/modded_request.hpp index 07d5f814..1ebe5b12 100644 --- a/src/httpserver/details/modded_request.hpp +++ b/src/httpserver/details/modded_request.hpp @@ -25,6 +25,8 @@ #ifndef _MODDED_REQUEST_HPP_ #define _MODDED_REQUEST_HPP_ +#include "httpserver/http_request.hpp" + namespace httpserver { diff --git a/src/httpserver/http_request.hpp b/src/httpserver/http_request.hpp index c056db6e..139272be 100644 --- a/src/httpserver/http_request.hpp +++ b/src/httpserver/http_request.hpp @@ -25,11 +25,13 @@ #ifndef _HTTP_REQUEST_HPP_ #define _HTTP_REQUEST_HPP_ +#include #include -#include #include #include -#include +#include + +#include "httpserver/http_utils.hpp" struct MHD_Connection; diff --git a/src/httpserver/http_resource.hpp b/src/httpserver/http_resource.hpp index ba2ad750..7841b96b 100644 --- a/src/httpserver/http_resource.hpp +++ b/src/httpserver/http_resource.hpp @@ -24,13 +24,13 @@ #ifndef _http_resource_hpp_ #define _http_resource_hpp_ -#include -#include -#include #ifdef DEBUG #include #endif +#include +#include +#include #include "httpserver/http_response.hpp" diff --git a/src/httpserver/http_response.hpp b/src/httpserver/http_response.hpp index 6cf8c2b9..33c1df42 100644 --- a/src/httpserver/http_response.hpp +++ b/src/httpserver/http_response.hpp @@ -24,11 +24,12 @@ #ifndef _HTTP_RESPONSE_HPP_ #define _HTTP_RESPONSE_HPP_ -#include -#include -#include + #include +#include #include +#include +#include #include #include "httpserver/http_utils.hpp" diff --git a/src/httpserver/http_utils.hpp b/src/httpserver/http_utils.hpp index 28f1d77f..fdfe48e0 100644 --- a/src/httpserver/http_utils.hpp +++ b/src/httpserver/http_utils.hpp @@ -25,17 +25,17 @@ #ifndef _HTTPUTILS_H_ #define _HTTPUTILS_H_ +#ifdef HAVE_GNUTLS +#include +#endif #include -#include -#include -#include -#include #include +#include #include #include -#ifdef HAVE_GNUTLS -#include -#endif +#include +#include +#include #define DEFAULT_MASK_VALUE 0xFFFF diff --git a/src/httpserver/webserver.hpp b/src/httpserver/webserver.hpp index 47ef6b10..8debfe7f 100644 --- a/src/httpserver/webserver.hpp +++ b/src/httpserver/webserver.hpp @@ -30,23 +30,21 @@ #define NOT_METHOD_ERROR "Method not Acceptable" #define GENERIC_ERROR "Internal Error" +#include +#include #include +#include #include -#include +#include #include +#include #include #include -#include -#include -#include - -#include -#include +#include #include "httpserver/create_webserver.hpp" -#include "httpserver/http_response.hpp" - #include "httpserver/details/http_endpoint.hpp" +#include "httpserver/http_response.hpp" namespace httpserver { diff --git a/src/string_utilities.cpp b/src/string_utilities.cpp index 69ec8801..cbcbb40b 100644 --- a/src/string_utilities.cpp +++ b/src/string_utilities.cpp @@ -18,16 +18,17 @@ USA */ +#include "httpserver/string_utilities.hpp" + +#include #include -#include -#include -#include -#include #include #include #include -#include -#include "httpserver/string_utilities.hpp" +#include +#include +#include +#include namespace httpserver { diff --git a/src/webserver.cpp b/src/webserver.cpp index f30f1ca7..ca4da9fe 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -18,17 +18,7 @@ USA */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include "httpserver/webserver.hpp" #if defined(__MINGW32__) || defined(__CYGWIN32__) #include @@ -39,23 +29,32 @@ #include #endif -#include +#include #include -#include - +#include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include "gettext.h" -#include "httpserver/http_utils.hpp" +#include "httpserver/create_webserver.hpp" +#include "httpserver/details/http_endpoint.hpp" +#include "httpserver/details/modded_request.hpp" +#include "httpserver/http_request.hpp" #include "httpserver/http_resource.hpp" #include "httpserver/http_response.hpp" +#include "httpserver/http_utils.hpp" #include "httpserver/string_response.hpp" -#include "httpserver/http_request.hpp" -#include "httpserver/details/http_endpoint.hpp" #include "httpserver/string_utilities.hpp" -#include "httpserver/create_webserver.hpp" -#include "httpserver/webserver.hpp" -#include "httpserver/details/modded_request.hpp" #define _REENTRANT 1 diff --git a/test/integ/authentication.cpp b/test/integ/authentication.cpp index 79193fd9..29861eef 100644 --- a/test/integ/authentication.cpp +++ b/test/integ/authentication.cpp @@ -28,11 +28,12 @@ #include #endif -#include "littletest.hpp" #include #include #include + #include "httpserver.hpp" +#include "littletest.hpp" #define MY_OPAQUE "11733b200778ce33060f31c9af70a870ba96ddd4" diff --git a/test/integ/ban_system.cpp b/test/integ/ban_system.cpp index e92dbb75..07f3e8d8 100644 --- a/test/integ/ban_system.cpp +++ b/test/integ/ban_system.cpp @@ -18,12 +18,13 @@ USA */ -#include "littletest.hpp" #include -#include #include +#include + #include "httpserver.hpp" #include "httpserver/http_utils.hpp" +#include "littletest.hpp" using namespace httpserver; using namespace std; diff --git a/test/integ/basic.cpp b/test/integ/basic.cpp index 60c30c41..5dc453ce 100644 --- a/test/integ/basic.cpp +++ b/test/integ/basic.cpp @@ -18,13 +18,14 @@ USA */ -#include "littletest.hpp" #include -#include -#include #include -#include "httpserver/string_utilities.hpp" +#include +#include + #include "httpserver.hpp" +#include "httpserver/string_utilities.hpp" +#include "littletest.hpp" using namespace httpserver; using namespace std; diff --git a/test/integ/deferred.cpp b/test/integ/deferred.cpp index b846ee0c..b84739f1 100644 --- a/test/integ/deferred.cpp +++ b/test/integ/deferred.cpp @@ -28,13 +28,14 @@ #include #endif -#include "littletest.hpp" #include #include +#include #include -#include "httpserver.hpp" #include -#include + +#include "httpserver.hpp" +#include "littletest.hpp" using namespace std; using namespace httpserver; diff --git a/test/integ/nodelay.cpp b/test/integ/nodelay.cpp index aad199f7..0284a3f9 100644 --- a/test/integ/nodelay.cpp +++ b/test/integ/nodelay.cpp @@ -18,11 +18,12 @@ USA */ -#include "littletest.hpp" #include -#include #include +#include + #include "httpserver.hpp" +#include "littletest.hpp" using namespace httpserver; using namespace std; diff --git a/test/integ/threaded.cpp b/test/integ/threaded.cpp index 14cb4c35..1b29d512 100644 --- a/test/integ/threaded.cpp +++ b/test/integ/threaded.cpp @@ -18,11 +18,12 @@ USA */ -#include "littletest.hpp" #include -#include #include +#include + #include "httpserver.hpp" +#include "littletest.hpp" using namespace httpserver; using namespace std; diff --git a/test/integ/ws_start_stop.cpp b/test/integ/ws_start_stop.cpp index ad8652dd..092b9a74 100644 --- a/test/integ/ws_start_stop.cpp +++ b/test/integ/ws_start_stop.cpp @@ -28,12 +28,13 @@ #include #endif -#include "littletest.hpp" #include #include +#include #include + #include "httpserver.hpp" -#include +#include "littletest.hpp" using namespace std; using namespace httpserver; diff --git a/test/littletest.hpp b/test/littletest.hpp index 681d47f4..bf9539fd 100644 --- a/test/littletest.hpp +++ b/test/littletest.hpp @@ -23,11 +23,11 @@ #ifndef _LITTLETEST_HPP_ #define _LITTLETEST_HPP_ -#include +#include +#include #include #include -#include -#include +#include #include #define LT_VERSION 1.0 diff --git a/test/unit/http_endpoint_test.cpp b/test/unit/http_endpoint_test.cpp index bf0294f6..d174f5e6 100644 --- a/test/unit/http_endpoint_test.cpp +++ b/test/unit/http_endpoint_test.cpp @@ -18,9 +18,10 @@ USA */ -#include "littletest.hpp" #include "httpserver/details/http_endpoint.hpp" +#include "littletest.hpp" + using namespace httpserver; using namespace std; using namespace details; diff --git a/test/unit/http_utils_test.cpp b/test/unit/http_utils_test.cpp index 9bb203b7..96c0a9cd 100644 --- a/test/unit/http_utils_test.cpp +++ b/test/unit/http_utils_test.cpp @@ -18,6 +18,8 @@ USA */ +#include "httpserver/http_utils.hpp" + #if defined(__MINGW32__) || defined(__CYGWIN32__) #define _WINDOWS #undef _WIN32_WINNT @@ -28,11 +30,10 @@ #include #endif -#include "littletest.hpp" -#include "httpserver/http_utils.hpp" - #include +#include "littletest.hpp" + using namespace httpserver; using namespace std; diff --git a/test/unit/string_utilities_test.cpp b/test/unit/string_utilities_test.cpp index 2d3fd83d..8d3ac6c7 100644 --- a/test/unit/string_utilities_test.cpp +++ b/test/unit/string_utilities_test.cpp @@ -18,11 +18,12 @@ USA */ -#include "littletest.hpp" #include "httpserver/string_utilities.hpp" #include +#include "littletest.hpp" + using namespace httpserver; using namespace std; From bc700b5a9f3f84681dc71ec1353ebdd75005e806 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Mon, 9 Mar 2020 16:02:34 -0700 Subject: [PATCH 477/623] Re-enable windows build on travis (#187) --- .travis.yml | 99 ++++++++++++++++------------------- configure.ac | 16 +++--- src/http_utils.cpp | 5 +- src/httpserver/http_utils.hpp | 9 ++++ test/integ/authentication.cpp | 37 ++++++++++--- test/integ/deferred.cpp | 4 +- test/integ/ws_start_stop.cpp | 5 +- 7 files changed, 100 insertions(+), 75 deletions(-) diff --git a/.travis.yml b/.travis.yml index 0971f0e3..a060f88f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,7 +2,6 @@ language: cpp os: - linux - osx - - windows compiler: - gcc - clang @@ -10,50 +9,57 @@ env: - DEBUG="debug" COVERAGE="coverage" - DEBUG="nodebug" COVERAGE="nocoverage" - LINKING="static" +before_cache: +- |- + case $TRAVIS_OS_NAME in + windows) + # https://unix.stackexchange.com/a/137322/107554 + $msys2 pacman --sync --clean --noconfirm + ;; + esac +cache: + directories: + - $HOME/AppData/Local/Temp/chocolatey + - /C/tools/msys64 before_install: - - ps -ef - - if [ "$TRAVIS_OS_NAME" = "windows" ]; then ps -Wla | sort ; fi - eval "${MATRIX_EVAL}" - if [ "$TRAVIS_OS_NAME" != "windows" ]; then export LDFLAGS="$LDFLAGS -L/usr/local/lib -L/usr/lib"; fi - if [ "$TRAVIS_OS_NAME" != "windows" ]; then export PATH=$PATH:/usr/local/lib; fi - if [ "$TRAVIS_OS_NAME" != "windows" ]; then export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib; fi - if [ "$TRAVIS_OS_NAME" != "windows" ]; then export DYLD_LIBRARY_PATH=$DYLD_LIBRARY_PATH:/usr/local/lib; fi + - if [ "$TRAVIS_OS_NAME" != "windows" ]; then export buildshell=''; fi - if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo apt-get install info install-info; fi - if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo pip install codecov; fi - if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo pip install gcovr; fi - if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo apt-get install cppcheck; fi - if [ "$TRAVIS_OS_NAME" = "osx" ]; then export CFLAGS='-mtune=generic'; fi - - if [ "$TRAVIS_OS_NAME" = "windows" ]; then choco install -r --no-progress -y msys2 make; fi - - if [ "$TRAVIS_OS_NAME" = "windows" ]; then PATH=$PATH:/c/tools/msys64/usr/bin/ ; fi - - if [ "$TRAVIS_OS_NAME" = "windows" ]; then powershell -executionpolicy bypass "pacman -Syu --noconfirm autoconf libtool automake make autoconf-archive pkg-config mingw-w64-x86_64-libsystre" ; fi - - if [ "$TRAVIS_OS_NAME" = "windows" ]; then - ln -s /c/tools/msys64/usr/share/autoconf* /usr/share/ ; - ln -s /c/tools/msys64/usr/share/automake* /usr/share/ ; - ln -s /c/tools/msys64/usr/share/aclocal* /usr/share/ ; - ln -s /c/tools/msys64/usr/share/libtool* /usr/share/ ; - ln -s /c/tools/msys64/usr/share/pkgconfig /usr/share/ ; - ln -s /c/tools/msys64/usr/bin/autom4te /usr/bin/ ; - ln -s /c/tools/msys64/usr/bin/autoconf /usr/bin/ ; - ln -s /c/tools/msys64/usr/bin/autoheader /usr/bin/ ; - ln -s /c/tools/msys64/usr/bin/m4 /usr/bin/ ; - - PATH=$PATH:/c/tools/msys64/usr/bin/ ; - export SHELL=/usr/bin/sh.exe ; - fi + - |- + case $TRAVIS_OS_NAME in + windows) + [[ ! -f C:/tools/msys64/msys2_shell.cmd ]] && rm -rf C:/tools/msys64 + choco uninstall -y mingw + choco upgrade --no-progress -y msys2 + export msys2='cmd //C RefreshEnv.cmd ' + export msys2+='& set MSYS=winsymlinks:nativestrict ' + export msys2+='& C:\\tools\\msys64\\msys2_shell.cmd -defterm -no-start' + export buildshell="$msys2 -mingw64 -full-path -here -c "\"\$@"\" --" + export msys2+=" -msys2 -c "\"\$@"\" --" + $msys2 pacman --sync --noconfirm --disable-download-timeout --needed mingw-w64-x86_64-toolchain + $msys2 pacman -Syu --noconfirm --disable-download-timeout autoconf libtool automake make autoconf-archive pkg-config mingw-w64-x86_64-libsystre mingw-w64-x86_64-doxygen mingw-w64-x86_64-gnutls mingw-w64-x86_64-graphviz mingw-w64-x86_64-curl + export PATH=/C/tools/msys64/mingw64/bin:$PATH + export MAKE=mingw32-make # so that Autotools can find it + ;; + esac - curl https://s3.amazonaws.com/libhttpserver/libmicrohttpd_releases/libmicrohttpd-0.9.59.tar.gz -o libmicrohttpd-0.9.59.tar.gz - tar -xzf libmicrohttpd-0.9.59.tar.gz - cd libmicrohttpd-0.9.59 - if [[ "$ARM_ARCH_DIR" != "" ]]; then ./configure --build `./config.guess` --host $ARM_ARCH_DIR --disable-examples; - elif [[ "$TRAVIS_OS_NAME" = "windows" ]]; then - ./configure --prefix=/usr --build=x86_64-w64-mingw32 --host=x86_64-w64-mingw32 --disable-examples --disable-https --enable-shared --enable-static ; else - ./configure --disable-examples; + if [ "$TRAVIS_OS_NAME" != "windows" ]; then $buildshell ./configure --disable-examples; else $buildshell ./configure --disable-examples --enable-poll=no; fi; fi - - if [ "$TRAVIS_OS_NAME" = "windows" ]; then find . -name Makefile -type f -exec sed -i "s/\${SHELL}/\/usr\/bin\/sh.exe/" "{}" + ; fi - - if [ "$TRAVIS_OS_NAME" = "windows" ]; then find . -name Makefile -type f -exec sed -i "s/\$(SHELL)/\/usr\/bin\/sh.exe/" "{}" + ; fi - - make - - if [ "$TRAVIS_OS_NAME" = "windows" ]; then make install; else sudo make install; fi + - $buildshell make + - if [ "$TRAVIS_OS_NAME" != "windows" ]; then sudo make install; else $buildshell make install; fi - cd .. - if [ "$BUILD_TYPE" = "asan" ]; then export CFLAGS='-fsanitize=address'; export CXXLAGS='-fsanitize=address'; export LDFLAGS='-fsanitize=address'; fi - if [ "$BUILD_TYPE" = "msan" ]; then export CFLAGS='-fsanitize=memory'; export CXXLAGS='-fsanitize=memory'; export LDFLAGS='-fsanitize=memory'; fi @@ -61,7 +67,6 @@ before_install: - if [ "$BUILD_TYPE" = "tsan" ]; then export CFLAGS='-fsanitize=thread'; export CXXLAGS='-fsanitize=thread'; export LDFLAGS='-fsanitize=thread'; fi - if [ "$BUILD_TYPE" = "ubsan" ]; then export export CFLAGS='-fsanitize=undefined'; export CXXLAGS='-fsanitize=undefined'; export LDFLAGS='-fsanitize=undefined'; fi install: - - ps -ef - if [[ "$CROSS_COMPILE" == 1 ]] ; then if [[ "$ARM_ARCH_DIR" == "aarch64-linux-gnu" ]] ; then mkdir $HOME/linker_bin ; @@ -71,35 +76,29 @@ install: ls -al $HOME/linker_bin/ld ; fi fi - - ./bootstrap + - $buildshell ./bootstrap - mkdir build - cd build - - if [ "$TRAVIS_OS_NAME" = "windows" ]; then - ../configure --build=x86_64-w64-mingw32 --host=x86_64-w64-mingw32 --disable-fastopen --disable-examples CPPFLAGS='-I/c/tools/msys64/mingw64/include/ -I/usr/include/' LDFLAGS='-L/c/tools/msys64/mingw64/lib -L/usr/lib/' ; - cd .. ; - find . -name Makefile -type f -exec sed -i "s/\$(SHELL)/\/usr\/bin\/sh.exe/" "{}" + ; - cd build ; - elif [ "$LINKING" = "static" ]; then - ../configure --enable-static --disable-fastopen; + - if [ "$TRAVIS_OS_NAME" = "windows" ]; then export MANIFEST_TOOL='no'; fi + - if [ "$LINKING" = "static" ]; then + $buildshell ../configure --enable-static --disable-fastopen; elif [ "$DEBUG" = "debug" ] && [ "$COVERAGE" = "coverage" ]; then - ../configure --enable-debug --enable-coverage --disable-shared --disable-fastopen; + $buildshell ../configure --enable-debug --enable-coverage --disable-shared --disable-fastopen; elif [ "$DEBUG" = "debug" ]; then - ../configure --enable-debug --disable-shared --disable-fastopen; + $buildshell ../configure --enable-debug --disable-shared --disable-fastopen; elif [ "$CROSS_COMPILE" = "1" ] && [ "$ARM_ARCH_DIR" = "aarch64-linux-gnu" ]; then - ../configure --disable-fastopen --build `../config.guess` --host aarch64-linux-gnu CC="gcc -B$HOME/linker_bin" CXX="g++ -B$HOME/linker_bin"; + $buildshell ../configure --disable-fastopen --build `../config.guess` --host aarch64-linux-gnu CC="gcc -B$HOME/linker_bin" CXX="g++ -B$HOME/linker_bin"; elif [ "$CROSS_COMPILE" = "1" ] && [ "$ARM_ARCH_DIR" = "arm-linux-gnueabi" ]; then - ../configure --disable-fastopen --build `../config.guess` --host arm-linux-gnueabi; + $buildshell ../configure --disable-fastopen --build `../config.guess` --host arm-linux-gnueabi; elif [ "$VALGRIND" = "valgrind" ]; then - ../configure --enable-debug --disable-fastopen --disable-valgrind-helgrind --disable-valgrind-drd --disable-valgrind-sgcheck; + $buildshell ../configure --enable-debug --disable-fastopen --disable-valgrind-helgrind --disable-valgrind-drd --disable-valgrind-sgcheck; else - ../configure --disable-fastopen; + $buildshell ../configure --disable-fastopen; fi - if [ "$CROSS_COMPILE" = "1" ] && [ "$ARM_ARCH_DIR" = "aarch64-linux-gnu" ]; then sed -i 's+/usr/bin/ld+$HOME/linker_bin/ld+g' libtool; fi - if [ "$CROSS_COMPILE" = "1" ] && [ "$ARM_ARCH_DIR" = "aarch64-linux-gnu" ]; then sed -i 's+/usr/bin/ld+$HOME/linker_bin/ld+g' Makefile; fi - - make - - if [ "$TRAVIS_OS_NAME" != "windows" ]; then make check TESTS= ; fi + - $buildshell make script: - - ps -ef - if [[ "$CROSS_COMPILE" == 1 ]]; then cd test ; if [[ "$ARM_ARCH_DIR" == "aarch64-linux-gnu" ]]; then @@ -120,8 +119,8 @@ script: qemu-arm -L /usr/arm-linux-gnueabi/ ./.libs/threaded ; fi fi - - if [ "$TRAVIS_OS_NAME" != "windows" ]; then make check ; fi - - if [ "$TRAVIS_OS_NAME" != "windows" ]; then cat test/test-suite.log; fi + - $buildshell make check + - $buildshell cat test/test-suite.log - if [ "$VALGRIND" = "valgrind" ]; then make check-valgrind; fi; - if [ "$VALGRIND" = "valgrind" ]; then cat test/test-suite-memcheck.log; fi; - if [ "$TRAVIS_OS_NAME" = "linux" ]; then cd ../src/; cppcheck --error-exitcode=1 .; cd ../build; fi @@ -144,19 +143,13 @@ script: sleep 5 && ab -n 10000000 -c 100 localhost:8080/plaintext fi after_script: - - ps -ef - - if [ "$TRAVIS_OS_NAME" = "windows" ]; then ps -Wla | sort ; fi - - if [ "$TRAVIS_OS_NAME" = "windows" ]; then gpgconf --kill gpg-agent ; fi - if [ "$TRAVIS_OS_NAME" = "windows" ]; then taskkill //F //PID $(ps -Wla | tr -s ' ' | grep gpg | cut -f2 -d' ') ; fi - - if [ "$TRAVIS_OS_NAME" = "windows" ]; then ps -Wla | sort ; fi - - echo $$ after_success: - if [ "$DEBUG" = "debug" ] && [ "$COVERAGE" = "coverage" ] && [ "$TRAVIS_OS_NAME" = "linux" ]; then bash <(curl -s https://codecov.io/bash); fi matrix: exclude: - compiler: clang env: DEBUG="debug" COVERAGE="coverage" - - os: windows include: - os: windows env: DEBUG="nodebug" COVERAGE="nocoverage" YARN_GPG=no diff --git a/configure.ac b/configure.ac index 576728ad..d6d22bd6 100644 --- a/configure.ac +++ b/configure.ac @@ -344,14 +344,14 @@ AC_SUBST(LDFLAGS) AC_SUBST(EXT_LIB_PATH) AC_SUBST(EXT_LIBS) -AC_CONFIG_LINKS([test/test_content:test/test_content]) -AC_CONFIG_LINKS([test/cert.pem:test/cert.pem]) -AC_CONFIG_LINKS([test/key.pem:test/key.pem]) -AC_CONFIG_LINKS([test/test_root_ca.pem:test/test_root_ca.pem]) -AC_CONFIG_LINKS([test/libhttpserver.supp:test/libhttpserver.supp]) -AC_CONFIG_LINKS([examples/cert.pem:examples/cert.pem]) -AC_CONFIG_LINKS([examples/key.pem:examples/key.pem]) -AC_CONFIG_LINKS([examples/test_content:examples/test_content]) +AC_CONFIG_FILES([test/test_content:test/test_content]) +AC_CONFIG_FILES([test/cert.pem:test/cert.pem]) +AC_CONFIG_FILES([test/key.pem:test/key.pem]) +AC_CONFIG_FILES([test/test_root_ca.pem:test/test_root_ca.pem]) +AC_CONFIG_FILES([test/libhttpserver.supp:test/libhttpserver.supp]) +AC_CONFIG_FILES([examples/cert.pem:examples/cert.pem]) +AC_CONFIG_FILES([examples/key.pem:examples/key.pem]) +AC_CONFIG_FILES([examples/test_content:examples/test_content]) AC_OUTPUT( libhttpserver.pc diff --git a/src/http_utils.cpp b/src/http_utils.cpp index ae577a9c..fe9f2ab4 100644 --- a/src/http_utils.cpp +++ b/src/http_utils.cpp @@ -21,9 +21,6 @@ #include "httpserver/http_utils.hpp" #if defined(__MINGW32__) || defined(__CYGWIN32__) -#define _WINDOWS -#undef _WIN32_WINNT -#define _WIN32_WINNT 0x600 #include #include #else @@ -35,8 +32,8 @@ #include #include #include -#include #include +#include #include #include #include diff --git a/src/httpserver/http_utils.hpp b/src/httpserver/http_utils.hpp index fdfe48e0..9b22ea97 100644 --- a/src/httpserver/http_utils.hpp +++ b/src/httpserver/http_utils.hpp @@ -28,6 +28,15 @@ #ifdef HAVE_GNUTLS #include #endif + +// needed to force Vista as a bare minimum to have inet_ntop (libmicro defines +// this to include XP support as a lower version). +#if defined(__MINGW32__) || defined(__CYGWIN32__) +#define _WINDOWS +#undef _WIN32_WINNT +#define _WIN32_WINNT 0x600 +#endif + #include #include #include diff --git a/test/integ/authentication.cpp b/test/integ/authentication.cpp index 29861eef..52979527 100644 --- a/test/integ/authentication.cpp +++ b/test/integ/authentication.cpp @@ -26,11 +26,11 @@ #include #else #include +#include +#include #endif #include -#include -#include #include "httpserver.hpp" #include "littletest.hpp" @@ -53,7 +53,7 @@ class user_pass_resource : public httpserver::http_resource { if (req.get_user() != "myuser" || req.get_pass() != "mypass") { - return shared_ptr(new basic_auth_fail_response("FAIL", "test@example.com")); + return shared_ptr(new basic_auth_fail_response("FAIL", "examplerealm")); } return shared_ptr(new string_response(req.get_user() + " " + req.get_pass(), 200, "text/plain")); } @@ -65,14 +65,14 @@ class digest_resource : public httpserver::http_resource const shared_ptr render_GET(const http_request& req) { if (req.get_digested_user() == "") { - return shared_ptr(new digest_auth_fail_response("FAIL", "test@example.com", MY_OPAQUE, true)); + return shared_ptr(new digest_auth_fail_response("FAIL", "examplerealm", MY_OPAQUE, true)); } else { bool reload_nonce = false; - if(!req.check_digest_auth("test@example.com", "mypass", 300, reload_nonce)) + if(!req.check_digest_auth("examplerealm", "mypass", 300, reload_nonce)) { - return shared_ptr(new digest_auth_fail_response("FAIL", "test@example.com", MY_OPAQUE, reload_nonce)); + return shared_ptr(new digest_auth_fail_response("FAIL", "examplerealm", MY_OPAQUE, reload_nonce)); } } return shared_ptr(new string_response("SUCCESS", 200, "text/plain")); @@ -139,6 +139,11 @@ LT_BEGIN_AUTO_TEST(authentication_suite, base_auth_fail) ws.stop(); LT_END_AUTO_TEST(base_auth_fail) +// do not run the digest auth tests on windows as curl +// appears to have problems with it. +// Will fix this separately +#ifndef _WINDOWS + LT_BEGIN_AUTO_TEST(authentication_suite, digest_auth) webserver ws = create_webserver(8080) .digest_auth_random("myrandom") @@ -148,12 +153,21 @@ LT_BEGIN_AUTO_TEST(authentication_suite, digest_auth) ws.register_resource("base", &digest); ws.start(false); +#if defined(_WINDOWS) + curl_global_init(CURL_GLOBAL_WIN32 ); +#else curl_global_init(CURL_GLOBAL_ALL); +#endif + std::string s; CURL *curl = curl_easy_init(); CURLcode res; curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST); +#if defined(_WINDOWS) + curl_easy_setopt(curl, CURLOPT_USERPWD, "examplerealm/myuser:mypass"); +#else curl_easy_setopt(curl, CURLOPT_USERPWD, "myuser:mypass"); +#endif curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); @@ -179,12 +193,21 @@ LT_BEGIN_AUTO_TEST(authentication_suite, digest_auth_wrong_pass) ws.register_resource("base", &digest); ws.start(false); +#if defined(_WINDOWS) + curl_global_init(CURL_GLOBAL_WIN32 ); +#else curl_global_init(CURL_GLOBAL_ALL); +#endif + std::string s; CURL *curl = curl_easy_init(); CURLcode res; curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST); +#if defined(_WINDOWS) + curl_easy_setopt(curl, CURLOPT_USERPWD, "examplerealm/myuser:wrongpass"); +#else curl_easy_setopt(curl, CURLOPT_USERPWD, "myuser:wrongpass"); +#endif curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); @@ -201,6 +224,8 @@ LT_BEGIN_AUTO_TEST(authentication_suite, digest_auth_wrong_pass) ws.stop(); LT_END_AUTO_TEST(digest_auth_wrong_pass) +#endif + LT_BEGIN_AUTO_TEST_ENV() AUTORUN_TESTS() LT_END_AUTO_TEST_ENV() diff --git a/test/integ/deferred.cpp b/test/integ/deferred.cpp index b84739f1..0f6cdb97 100644 --- a/test/integ/deferred.cpp +++ b/test/integ/deferred.cpp @@ -26,12 +26,12 @@ #include #else #include +#include +#include #endif #include -#include #include -#include #include #include "httpserver.hpp" diff --git a/test/integ/ws_start_stop.cpp b/test/integ/ws_start_stop.cpp index 092b9a74..739e9d16 100644 --- a/test/integ/ws_start_stop.cpp +++ b/test/integ/ws_start_stop.cpp @@ -26,12 +26,13 @@ #include #else #include +#include +#include #endif #include -#include #include -#include +#include #include "httpserver.hpp" #include "littletest.hpp" From f6810d9bd62d85f31a8057bc2071f28d43696593 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Tue, 10 Mar 2020 07:04:27 -0700 Subject: [PATCH 478/623] Removed unused travis config code (#188) --- .travis.yml | 89 +---------------------------------------------------- 1 file changed, 1 insertion(+), 88 deletions(-) diff --git a/.travis.yml b/.travis.yml index a060f88f..675d6a48 100644 --- a/.travis.yml +++ b/.travis.yml @@ -53,11 +53,7 @@ before_install: - curl https://s3.amazonaws.com/libhttpserver/libmicrohttpd_releases/libmicrohttpd-0.9.59.tar.gz -o libmicrohttpd-0.9.59.tar.gz - tar -xzf libmicrohttpd-0.9.59.tar.gz - cd libmicrohttpd-0.9.59 - - if [[ "$ARM_ARCH_DIR" != "" ]]; then - ./configure --build `./config.guess` --host $ARM_ARCH_DIR --disable-examples; - else - if [ "$TRAVIS_OS_NAME" != "windows" ]; then $buildshell ./configure --disable-examples; else $buildshell ./configure --disable-examples --enable-poll=no; fi; - fi + - if [ "$TRAVIS_OS_NAME" != "windows" ]; then $buildshell ./configure --disable-examples; else $buildshell ./configure --disable-examples --enable-poll=no; fi; - $buildshell make - if [ "$TRAVIS_OS_NAME" != "windows" ]; then sudo make install; else $buildshell make install; fi - cd .. @@ -67,15 +63,6 @@ before_install: - if [ "$BUILD_TYPE" = "tsan" ]; then export CFLAGS='-fsanitize=thread'; export CXXLAGS='-fsanitize=thread'; export LDFLAGS='-fsanitize=thread'; fi - if [ "$BUILD_TYPE" = "ubsan" ]; then export export CFLAGS='-fsanitize=undefined'; export CXXLAGS='-fsanitize=undefined'; export LDFLAGS='-fsanitize=undefined'; fi install: - - if [[ "$CROSS_COMPILE" == 1 ]] ; then - if [[ "$ARM_ARCH_DIR" == "aarch64-linux-gnu" ]] ; then - mkdir $HOME/linker_bin ; - ln -s /usr/bin/aarch64-linux-gnu-ld $HOME/linker_bin/ld ; - LD=$HOME/linker_bin/ld ; - echo "SETTING GNU LINKER DIR" ; - ls -al $HOME/linker_bin/ld ; - fi - fi - $buildshell ./bootstrap - mkdir build - cd build @@ -86,39 +73,13 @@ install: $buildshell ../configure --enable-debug --enable-coverage --disable-shared --disable-fastopen; elif [ "$DEBUG" = "debug" ]; then $buildshell ../configure --enable-debug --disable-shared --disable-fastopen; - elif [ "$CROSS_COMPILE" = "1" ] && [ "$ARM_ARCH_DIR" = "aarch64-linux-gnu" ]; then - $buildshell ../configure --disable-fastopen --build `../config.guess` --host aarch64-linux-gnu CC="gcc -B$HOME/linker_bin" CXX="g++ -B$HOME/linker_bin"; - elif [ "$CROSS_COMPILE" = "1" ] && [ "$ARM_ARCH_DIR" = "arm-linux-gnueabi" ]; then - $buildshell ../configure --disable-fastopen --build `../config.guess` --host arm-linux-gnueabi; elif [ "$VALGRIND" = "valgrind" ]; then $buildshell ../configure --enable-debug --disable-fastopen --disable-valgrind-helgrind --disable-valgrind-drd --disable-valgrind-sgcheck; else $buildshell ../configure --disable-fastopen; fi - - if [ "$CROSS_COMPILE" = "1" ] && [ "$ARM_ARCH_DIR" = "aarch64-linux-gnu" ]; then sed -i 's+/usr/bin/ld+$HOME/linker_bin/ld+g' libtool; fi - - if [ "$CROSS_COMPILE" = "1" ] && [ "$ARM_ARCH_DIR" = "aarch64-linux-gnu" ]; then sed -i 's+/usr/bin/ld+$HOME/linker_bin/ld+g' Makefile; fi - $buildshell make script: - - if [[ "$CROSS_COMPILE" == 1 ]]; then - cd test ; - if [[ "$ARM_ARCH_DIR" == "aarch64-linux-gnu" ]]; then - file ./.libs/basic; - file ./.libs/http_utils; - file ./.libs/threaded; - - qemu-aarch64 -L /usr/aarch64-linux-gnu/ ./.libs/basic ; - qemu-aarch64 -L /usr/aarch64-linux-gnu/ ./.libs/http_utils ; - qemu-aarch64 -L /usr/aarch64-linux-gnu/ ./.libs/threaded ; - else - file ./.libs/basic; - file ./.libs/http_utils; - file ./.libs/threaded; - - qemu-arm -L /usr/arm-linux-gnueabi/ ./.libs/basic ; - qemu-arm -L /usr/arm-linux-gnueabi/ ./.libs/http_utils ; - qemu-arm -L /usr/arm-linux-gnueabi/ ./.libs/threaded ; - fi - fi - $buildshell make check - $buildshell cat test/test-suite.log - if [ "$VALGRIND" = "valgrind" ]; then make check-valgrind; fi; @@ -284,22 +245,6 @@ matrix: - apache2-utils env: - MATRIX_EVAL="CC=gcc-7 && CXX=g++-7 && PERFORMANCE=threads" - #- os: osx - # osx_image: xcode8 - # env: - # - MATRIX_EVAL="CC=gcc-4.9 && CXX=g++-4.9" - #- os: osx - # osx_image: xcode8 - # env: - # - MATRIX_EVAL="brew install gcc5 && CC=gcc-5 && CXX=g++-5" - #- os: osx - # osx_image: xcode8 - # env: - # - MATRIX_EVAL="brew install gcc6 && CC=gcc-6 && CXX=g++-6" - #- os: osx - # osx_image: xcode8 - # env: - # - MATRIX_EVAL="brew install gcc && CC=gcc-7 && CXX=g++-7" # works on Precise and Trusty - os: linux addons: @@ -398,35 +343,3 @@ matrix: - clang-9 env: - MATRIX_EVAL="CC=clang-9 && CXX=clang++-9" - - os: linux - compiler: clang - addons: - apt: - sources: - - ubuntu-toolchain-r-test - packages: - - clang-3.9 - - g++-arm-linux-gnueabi - - g++-multilib - - gcc-multilib - - qemu - - qemu-system-arm - env: MATRIX_EVAL="CROSS_COMPILE=1 && ARM_ARCH_DIR=arm-linux-gnueabi && CC=arm-linux-gnueabi-gcc" - - os: linux - compiler: clang - addons: - apt: - sources: - - ubuntu-toolchain-r-test - packages: - - clang-3.9 - - g++-4.8-aarch64-linux-gnu - - gcc-4.8-aarch64-linux-gnu - - g++-4.8-multilib - - gcc-4.8-multilib - - qemu - - qemu-system-arm - env: MATRIX_EVAL="CROSS_COMPILE=1 && ARM_LD_PATH=$HOME/linker_bin && ARM_ARCH_DIR=aarch64-linux-gnu" - allow_failures: - - env: MATRIX_EVAL="CROSS_COMPILE=1 && ARM_ARCH_DIR=arm-linux-gnueabi && CC=arm-linux-gnueabi-gcc" - - env: MATRIX_EVAL="CROSS_COMPILE=1 && ARM_LD_PATH=$HOME/linker_bin && ARM_ARCH_DIR=aarch64-linux-gnu" From a6c82e55abd9601b57bb18410213468e17afe8ae Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Fri, 13 Mar 2020 09:16:26 -0700 Subject: [PATCH 479/623] Added missing no_single_resource method (#190) --- src/httpserver/create_webserver.hpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/httpserver/create_webserver.hpp b/src/httpserver/create_webserver.hpp index e8d1a018..678a1616 100644 --- a/src/httpserver/create_webserver.hpp +++ b/src/httpserver/create_webserver.hpp @@ -229,6 +229,10 @@ class create_webserver { _single_resource = true; return *this; } + create_webserver& no_single_resource() + { + _single_resource = false; return *this; + } create_webserver& tcp_nodelay() { _tcp_nodelay = true; return *this; From b2d69d96b6aa14693482d7039a417411c8577925 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felipe=20Zipitr=C3=ADa?= Date: Tue, 14 Apr 2020 19:08:37 -0300 Subject: [PATCH 480/623] add missing freebsd headers (#192) --- src/http_utils.cpp | 3 +++ src/webserver.cpp | 3 +++ 2 files changed, 6 insertions(+) diff --git a/src/http_utils.cpp b/src/http_utils.cpp index fe9f2ab4..48f1abeb 100644 --- a/src/http_utils.cpp +++ b/src/http_utils.cpp @@ -24,6 +24,9 @@ #include #include #else +#if defined(__FreeBSD__) +#include +#endif #include #include #include diff --git a/src/webserver.cpp b/src/webserver.cpp index ca4da9fe..fe5dfa8a 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -25,6 +25,9 @@ #include #define _WINDOWS #else +#if defined(__FreeBSD__) +#include +#endif #include #include #endif From 6b1e19588c4bd6d23298621e5a9ea950d41b80f0 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sat, 2 May 2020 16:55:08 -0700 Subject: [PATCH 481/623] Fix cygwin build - moves to C++11 regex * Fixing cygwin builds by not mingling them with the rest of win32 * Include sys/select.h in CYGWIN to define fd_set * Add fd_set definition ahead of microhttpd.h inclusion * suppress ignore_sigpipe for cygwin * Add missing includes * Remove runguard (not used) * Remove runguard (unused) * Import definition of NI_MAXHOST in cygwin * Force in definitions needed by cygwin * Define u_char for cygwin * Remove regex_replace method that was never used. * Removed regex_replace method that is never used * Remove regex_replace method that is never used * Move to C++11 regex * Use C++11 regex * Prevent object from not being initalized * Use std::regex rather than std::basic_regex * Use std::regex instead of std::basic_regex * Fix typos * Reorder parameters to avoid warnings * Remove requirements for libregex --- configure.ac | 13 ++++---- src/details/http_endpoint.cpp | 30 +++++------------- src/http_utils.cpp | 28 +++++++++++++---- src/httpserver/details/http_endpoint.hpp | 5 +-- src/httpserver/http_utils.hpp | 7 ++++- src/httpserver/string_utilities.hpp | 3 -- src/httpserver/webserver.hpp | 1 - src/string_utilities.cpp | 39 ------------------------ src/webserver.cpp | 18 ++++++++--- test/integ/authentication.cpp | 2 +- test/integ/deferred.cpp | 2 +- test/integ/ws_start_stop.cpp | 2 +- test/unit/http_utils_test.cpp | 2 +- test/unit/string_utilities_test.cpp | 5 --- 14 files changed, 62 insertions(+), 95 deletions(-) diff --git a/configure.ac b/configure.ac index d6d22bd6..90b2ec7a 100644 --- a/configure.ac +++ b/configure.ac @@ -64,17 +64,17 @@ NETWORK_LIBS="" case "$host" in *-mingw*) NETWORK_HEADER="winsock2.h" - REGEX_LIBS="-lregex -lpthread -no-undefined" + ADDITIONAL_LIBS="-lpthread -no-undefined" NETWORK_LIBS="-lws2_32" native_srcdir=$(cd $srcdir; pwd -W) ;; *-cygwin*) - NETWORK_HEADER="winsock2.h" - REGEX_LIBS="-lregex -lpthread -no-undefined" + NETWORK_HEADER="arpa/inet.h" + ADDITIONAL_LIBS="-lpthread -no-undefined" ;; *) NETWORK_HEADER="arpa/inet.h" - REGEX_LIBS="" + ADDITIONAL_LIBS="" is_windows=no ;; esac @@ -86,7 +86,6 @@ AC_CHECK_HEADER([inttypes.h],[],[AC_MSG_ERROR("inttypes.h not found")]) AC_CHECK_HEADER([errno.h],[],[AC_MSG_ERROR("errno.h not found")]) AC_CHECK_HEADER([unistd.h],[],[AC_MSG_ERROR("unistd.h not found")]) AC_CHECK_HEADER([ctype.h],[],[AC_MSG_ERROR("cctype not found")]) -AC_CHECK_HEADER([regex.h],[],[AC_MSG_ERROR("regex.h not found")]) AC_CHECK_HEADER([sys/stat.h],[],[AC_MSG_ERROR("sys/stat.h not found")]) AC_CHECK_HEADER([sys/types.h],[],[AC_MSG_ERROR("sys/types.h not found")]) AC_CHECK_HEADER([$NETWORK_HEADER],[],[AC_MSG_ERROR("$NETWORK_HEADER not found")]) @@ -117,7 +116,7 @@ if test x"$host" = x"$build"; then ) CXXFLAGS="-std=c++11 -DHTTPSERVER_COMPILATION -D_REENTRANT $LIBMICROHTTPD_CFLAGS $CXXFLAGS" - LDFLAGS="$LIBMICROHTTPD_LIBS $NETWORK_LIBS $REGEX_LIBS $LDFLAGS" + LDFLAGS="$LIBMICROHTTPD_LIBS $NETWORK_LIBS $ADDITIONAL_LIBS $LDFLAGS" cond_cross_compile="no" else @@ -130,7 +129,7 @@ else ) CXXFLAGS="-std=c++11 -DHTTPSERVER_COMPILATION -D_REENTRANT $CXXFLAGS" - LDFLAGS="$NETWORK_LIBS $REGEX_LIBS $LDFLAGS" + LDFLAGS="$NETWORK_LIBS $ADDITIONAL_LIBS $LDFLAGS" cond_cross_compile="yes" fi diff --git a/src/details/http_endpoint.cpp b/src/details/http_endpoint.cpp index 490788c2..075448d7 100644 --- a/src/details/http_endpoint.cpp +++ b/src/details/http_endpoint.cpp @@ -35,10 +35,6 @@ using namespace http; http_endpoint::~http_endpoint() { - if(reg_compiled) - { - regfree(&re_url_normalized); - } } http_endpoint::http_endpoint @@ -119,9 +115,7 @@ http_endpoint::http_endpoint if(use_regex) { url_normalized += "$"; - regcomp(&re_url_normalized, url_normalized.c_str(), - REG_EXTENDED|REG_ICASE|REG_NOSUB - ); + re_url_normalized = std::regex(url_normalized, std::regex::extended | std::regex::icase | std::regex::nosubs); reg_compiled = true; } } @@ -133,12 +127,9 @@ http_endpoint::http_endpoint(const http_endpoint& h): url_pieces(h.url_pieces), chunk_positions(h.chunk_positions), family_url(h.family_url), - reg_compiled(h.reg_compiled) + reg_compiled(h.reg_compiled), + re_url_normalized(h.re_url_normalized) { - if(reg_compiled) - regcomp(&re_url_normalized, url_normalized.c_str(), - REG_EXTENDED|REG_ICASE|REG_NOSUB - ); } http_endpoint& http_endpoint::operator =(const http_endpoint& h) @@ -147,14 +138,7 @@ http_endpoint& http_endpoint::operator =(const http_endpoint& h) url_normalized = h.url_normalized; family_url = h.family_url; reg_compiled = h.reg_compiled; - if(reg_compiled) - { - regfree(&re_url_normalized); - - regcomp(&re_url_normalized, url_normalized.c_str(), - REG_EXTENDED|REG_ICASE|REG_NOSUB - ); - } + re_url_normalized = h.re_url_normalized; url_pars = h.url_pars; url_pieces = h.url_pieces; chunk_positions = h.chunk_positions; @@ -171,7 +155,9 @@ bool http_endpoint::match(const http_endpoint& url) const if (!reg_compiled) throw std::invalid_argument("Cannot run match. Regex suppressed."); if(!family_url || url.url_pieces.size() < url_pieces.size()) - return regexec(&re_url_normalized, url.url_complete.c_str(), 0, NULL, 0) == 0; + { + return regex_match(url.url_complete, re_url_normalized); + } string nn = "/"; bool first = true; @@ -180,7 +166,7 @@ bool http_endpoint::match(const http_endpoint& url) const nn += (first ? "" : "/") + url.url_pieces[i]; first = false; } - return regexec(&re_url_normalized, nn.c_str(), 0, NULL, 0) == 0; + return regex_match(nn, re_url_normalized); } }; diff --git a/src/http_utils.cpp b/src/http_utils.cpp index 48f1abeb..c9d3aa66 100644 --- a/src/http_utils.cpp +++ b/src/http_utils.cpp @@ -20,17 +20,19 @@ #include "httpserver/http_utils.hpp" -#if defined(__MINGW32__) || defined(__CYGWIN32__) +#if defined(_WIN32) && ! defined(__CYGWIN__) #include #include -#else +#else // WIN32 check #if defined(__FreeBSD__) #include -#endif -#include -#include +#endif // FreeBSD #include -#endif +#include +#include +#include +#include +#endif // WIN32 check #include #include @@ -40,6 +42,7 @@ #include #include #include +#include #include "httpserver/string_utilities.hpp" @@ -48,6 +51,19 @@ #define SET_BIT(var,pos) ((var) |= 1 << (pos)) #define CLEAR_BIT(var,pos) ((var) &= ~(1<<(pos))) +#if defined (__CYGWIN__) + +#if ! defined (NI_MAXHOST) +#define NI_MAXHOST 1025 +#endif // NI_MAXHOST + +#ifndef __u_char_defined +typedef unsigned char u_char; +#define __u_char_defined +#endif // __u_char_defined + +#endif // CYGWIN + using namespace std; namespace httpserver { diff --git a/src/httpserver/details/http_endpoint.hpp b/src/httpserver/details/http_endpoint.hpp index 8264d6cc..281685da 100644 --- a/src/httpserver/details/http_endpoint.hpp +++ b/src/httpserver/details/http_endpoint.hpp @@ -25,7 +25,7 @@ #ifndef _HTTP_ENDPOINT_HPP_ #define _HTTP_ENDPOINT_HPP_ -#include +#include #include #include #include @@ -136,6 +136,7 @@ class http_endpoint http_endpoint(): url_complete("/"), url_normalized("/"), + re_url_normalized(std::regex("")), // initialize empty family_url(false), reg_compiled(false) { @@ -186,7 +187,7 @@ class http_endpoint /** * Regex used in comparisons **/ - regex_t re_url_normalized; + std::regex re_url_normalized; /** * Boolean indicating wheter the endpoint represents a family diff --git a/src/httpserver/http_utils.hpp b/src/httpserver/http_utils.hpp index 9b22ea97..9ad89b48 100644 --- a/src/httpserver/http_utils.hpp +++ b/src/httpserver/http_utils.hpp @@ -37,6 +37,11 @@ #define _WIN32_WINNT 0x600 #endif +// needed to have the fd_set definition ahead of microhttpd.h import +#if defined(__CYGWIN__) +#include +#endif + #include #include #include @@ -72,7 +77,7 @@ class http_utils enum start_method_T { -#if defined(__MINGW32__) || defined(__CYGWIN32__) +#if defined(__MINGW32__) || defined(__CYGWIN__) #ifdef ENABLE_POLL INTERNAL_SELECT = MHD_USE_SELECT_INTERNALLY | MHD_USE_POLL, #else diff --git a/src/httpserver/string_utilities.hpp b/src/httpserver/string_utilities.hpp index adc248c2..e61762ab 100644 --- a/src/httpserver/string_utilities.hpp +++ b/src/httpserver/string_utilities.hpp @@ -44,9 +44,6 @@ const std::string to_lower_copy(const std::string& str); const std::vector string_split(const std::string& s, char sep = ' ', bool collapse = true ); -const std::string regex_replace(const std::string& str, const std::string& pattern, - const std::string& replace_str -); void to_upper(std::string& str); }; }; diff --git a/src/httpserver/webserver.hpp b/src/httpserver/webserver.hpp index 8debfe7f..1ff472b2 100644 --- a/src/httpserver/webserver.hpp +++ b/src/httpserver/webserver.hpp @@ -174,7 +174,6 @@ class webserver bool single_resource; bool tcp_nodelay; pthread_mutex_t mutexwait; - pthread_rwlock_t runguard; pthread_cond_t mutexcond; render_ptr not_found_resource; render_ptr method_not_allowed_resource; diff --git a/src/string_utilities.cpp b/src/string_utilities.cpp index cbcbb40b..c557fe06 100644 --- a/src/string_utilities.cpp +++ b/src/string_utilities.cpp @@ -20,7 +20,6 @@ #include "httpserver/string_utilities.hpp" -#include #include #include #include @@ -85,43 +84,5 @@ const std::vector string_split( return result; } -const std::string regex_replace(const std::string& str, - const std::string& pattern, - const std::string& replace_str -) -{ - regex_t preg; - regmatch_t substmatch[1]; - regcomp(&preg, pattern.c_str(), REG_EXTENDED|REG_ICASE); - std::string result; - if ( regexec(&preg, str.c_str(), 1, substmatch, 0) == 0 ) - { - char ns[substmatch[0].rm_so + 1 + - replace_str.size() + (str.size() - substmatch[0].rm_eo) + 2 - ]; - - memcpy(ns, str.c_str(), substmatch[0].rm_so+1); - - memcpy(&ns[substmatch[0].rm_so], - replace_str.c_str(), - replace_str.size() - ); - - memcpy(&ns[substmatch[0].rm_so+replace_str.size()], - &str[substmatch[0].rm_eo], str.substr(substmatch[0].rm_eo).size() - ); - - ns[substmatch[0].rm_so + - replace_str.size() + - str.substr(substmatch[0].rm_eo).size() - ] = 0; - - result = std::string((char*)ns); - } - regfree(&preg); - - return result; -} - }; }; diff --git a/src/webserver.cpp b/src/webserver.cpp index fe5dfa8a..45fd158c 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -20,7 +20,7 @@ #include "httpserver/webserver.hpp" -#if defined(__MINGW32__) || defined(__CYGWIN32__) +#if defined(_WIN32) && ! defined(__CYGWIN__) #include #include #define _WINDOWS @@ -28,6 +28,9 @@ #if defined(__FreeBSD__) #include #endif +#if defined(__CYGWIN__) +#include +#endif #include #include #endif @@ -47,6 +50,13 @@ #include #include #include +#include +#include +#include +#include +#include +#include +#include #include "gettext.h" #include "httpserver/create_webserver.hpp" @@ -88,7 +98,7 @@ struct compare_value } }; -#ifndef __MINGW32__ +#if !defined(_WIN32) && !defined(__MINGW32__) && !defined(__CYGWIN__) static void catcher (int sig) { } @@ -97,7 +107,7 @@ static void catcher (int sig) static void ignore_sigpipe () { //Mingw doesn't implement SIGPIPE -#ifndef __MINGW32__ +#if !defined(_WIN32) && !defined(__MINGW32__) && !defined(__CYGWIN__) struct sigaction oldsig; struct sigaction sig; @@ -160,7 +170,6 @@ webserver::webserver(const create_webserver& params): { ignore_sigpipe(); pthread_mutex_init(&mutexwait, NULL); - pthread_rwlock_init(&runguard, NULL); pthread_cond_init(&mutexcond, NULL); } @@ -168,7 +177,6 @@ webserver::~webserver() { stop(); pthread_mutex_destroy(&mutexwait); - pthread_rwlock_destroy(&runguard); pthread_cond_destroy(&mutexcond); } diff --git a/test/integ/authentication.cpp b/test/integ/authentication.cpp index 52979527..da89f0a2 100644 --- a/test/integ/authentication.cpp +++ b/test/integ/authentication.cpp @@ -18,7 +18,7 @@ USA */ -#if defined(__MINGW32__) || defined(__CYGWIN32__) +#if defined(_WIN32) && ! defined(__CYGWIN__) #define _WINDOWS #undef _WIN32_WINNT #define _WIN32_WINNT 0x600 diff --git a/test/integ/deferred.cpp b/test/integ/deferred.cpp index 0f6cdb97..571c36a5 100644 --- a/test/integ/deferred.cpp +++ b/test/integ/deferred.cpp @@ -18,7 +18,7 @@ USA */ -#if defined(__MINGW32__) || defined(__CYGWIN32__) +#if defined(_WIN32) && ! defined(__CYGWIN__) #define _WINDOWS #undef _WIN32_WINNT #define _WIN32_WINNT 0x600 diff --git a/test/integ/ws_start_stop.cpp b/test/integ/ws_start_stop.cpp index 739e9d16..57d1acdb 100644 --- a/test/integ/ws_start_stop.cpp +++ b/test/integ/ws_start_stop.cpp @@ -18,7 +18,7 @@ USA */ -#if defined(__MINGW32__) || defined(__CYGWIN32__) +#if defined(_WIN32) && ! defined(__CYGWIN__) #define _WINDOWS #undef _WIN32_WINNT #define _WIN32_WINNT 0x600 diff --git a/test/unit/http_utils_test.cpp b/test/unit/http_utils_test.cpp index 96c0a9cd..6ef9cb99 100644 --- a/test/unit/http_utils_test.cpp +++ b/test/unit/http_utils_test.cpp @@ -20,7 +20,7 @@ #include "httpserver/http_utils.hpp" -#if defined(__MINGW32__) || defined(__CYGWIN32__) +#if defined(_WIN32) && ! defined(__CYGWIN__) #define _WINDOWS #undef _WIN32_WINNT #define _WIN32_WINNT 0x600 diff --git a/test/unit/string_utilities_test.cpp b/test/unit/string_utilities_test.cpp index 8d3ac6c7..2539ac08 100644 --- a/test/unit/string_utilities_test.cpp +++ b/test/unit/string_utilities_test.cpp @@ -93,11 +93,6 @@ LT_BEGIN_AUTO_TEST(string_utilities_suite, split_string_end_space) LT_CHECK_COLLECTIONS_EQ(expected.begin(), expected.end(), actual.begin()); LT_END_AUTO_TEST(split_string_end_space) -LT_BEGIN_AUTO_TEST(string_utilities_suite, regex_replace) - LT_CHECK_EQ(string_utilities::regex_replace("test///message", "(\\/)+", "/"), "test/message"); - LT_CHECK_EQ(string_utilities::regex_replace("test 1234 message", "([0-9])+", "bob"), "test bob message"); -LT_END_AUTO_TEST(regex_replace) - LT_BEGIN_AUTO_TEST_ENV() AUTORUN_TESTS() LT_END_AUTO_TEST_ENV() From 90fba0995559262eed99519efd26287c925307d5 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sat, 16 May 2020 14:18:35 +0000 Subject: [PATCH 482/623] Updated authors file --- AUTHORS | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/AUTHORS b/AUTHORS index a3d752c2..8f314bd1 100644 --- a/AUTHORS +++ b/AUTHORS @@ -9,9 +9,14 @@ Craig Minihan Guo Xiao Philipp Claßen Vitaut Bajaryn +Felipe Zipitría +Steven 'Steve' Kendall +G. Mercat +Thomas Schätzlein - Support for building on MinGW/Cygwin systems Shane Peelar +Dean M. Sands, III - Support for building on MaxOsX Jan Klimke @@ -26,4 +31,14 @@ Marcel Pursche Julian Picht - Fix string termination when loading files in memory -martamoreton +martamoreton (Github: https://github.com/martamoreton) + +- Memory leaks +rdiazmartin + +- Cleanup of multiple parts of the code +bcsgh (Github: https://github.com/bcsgh) + +- Management of large uploads +Walter Landry +Jagat From 992809566951d17ef6142b4d417ecc8a80c8ca33 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sat, 16 May 2020 14:23:28 +0000 Subject: [PATCH 483/623] Updated changelog --- ChangeLog | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/ChangeLog b/ChangeLog index 82b59fe6..a0fa5bd1 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,12 @@ +Sat May 16 07:20:00 2020 -0800 + General performance improvements (reduced use of regex, lazy-building of + post-processor) + General code cleanup + General fixes to the documentation + Fixed support on FreeBSD (added missing headers) + Fixed support for Cygwin + Removed depedency on C regex - now using C++11 regex + Sat Aug 10 18:34:07 2019 -0800 Added support for TCP-NODELAY Changed set_path on http_request to have lazy behavior From 543e49b912cc90ec7b4eac9db3db713d2aad42ee Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sat, 16 May 2020 14:30:58 +0000 Subject: [PATCH 484/623] Updated major version to 0.18.0 --- configure.ac | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index 90b2ec7a..bd5da2d8 100644 --- a/configure.ac +++ b/configure.ac @@ -21,8 +21,8 @@ AC_PREREQ(2.57) m4_define([libhttpserver_MAJOR_VERSION],[0])dnl -m4_define([libhttpserver_MINOR_VERSION],[17])dnl -m4_define([libhttpserver_REVISION],[5])dnl +m4_define([libhttpserver_MINOR_VERSION],[18])dnl +m4_define([libhttpserver_REVISION],[0])dnl m4_define([libhttpserver_PKG_VERSION],[libhttpserver_MAJOR_VERSION.libhttpserver_MINOR_VERSION.libhttpserver_REVISION])dnl m4_define([libhttpserver_LDF_VERSION],[libhttpserver_MAJOR_VERSION:libhttpserver_MINOR_VERSION:libhttpserver_REVISION])dnl AC_INIT([libhttpserver], libhttpserver_PKG_VERSION, [electrictwister2000@gmail.com]) From bd6b202119df5e8f5f40cf53ee4060f25da2ddc6 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sat, 16 May 2020 07:53:55 -0700 Subject: [PATCH 485/623] Updated dependencies section in documentation --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 473b8b2c..dcd0024a 100644 --- a/README.md +++ b/README.md @@ -85,6 +85,8 @@ The minimum versions required are: Additionally, for MinGW on windows you will need: * libwinpthread (For MinGW-w64, if you use thread model posix then you have this) + +For versions before 0.18.0, on MinGW, you will need: * libgnurx >= 2.5.1 Furthermore, the testcases use [libcurl](http://curl.haxx.se/libcurl/) but you don't need it to compile the library. From 387b3c629056b729a9c753a9e624b75d79ebffe8 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sat, 6 Jun 2020 00:18:56 -0700 Subject: [PATCH 486/623] Prevent use of regex in http_endpoint outside of registration * Prevent use of regex in http_endpoint outside of registration. This avoids an abort whenever an invalid regex is passed as a URL in an http request. * No need to throw new --- src/details/http_endpoint.cpp | 20 +++++++++-- src/httpserver/details/http_endpoint.hpp | 2 +- src/webserver.cpp | 5 +-- test/integ/basic.cpp | 18 ++++++++++ test/unit/http_endpoint_test.cpp | 42 ++++++++++++++++++------ 5 files changed, 71 insertions(+), 16 deletions(-) diff --git a/src/details/http_endpoint.cpp b/src/details/http_endpoint.cpp index 075448d7..be76ddde 100644 --- a/src/details/http_endpoint.cpp +++ b/src/details/http_endpoint.cpp @@ -18,6 +18,8 @@ USA */ +#include + #include "httpserver/details/http_endpoint.hpp" #include "httpserver/http_utils.hpp" @@ -47,6 +49,11 @@ http_endpoint::http_endpoint family_url(family), reg_compiled(false) { + if (use_regex && !registration) + { + throw std::invalid_argument("Cannot use regex if not during registration"); + } + url_normalized = use_regex ? "^/" : "/"; vector parts; @@ -115,7 +122,14 @@ http_endpoint::http_endpoint if(use_regex) { url_normalized += "$"; - re_url_normalized = std::regex(url_normalized, std::regex::extended | std::regex::icase | std::regex::nosubs); + try + { + re_url_normalized = std::regex(url_normalized, std::regex::extended | std::regex::icase | std::regex::nosubs); + } + catch (std::regex_error& e) + { + throw std::invalid_argument("Not a valid regex in URL: " + url_normalized); + } reg_compiled = true; } } @@ -126,9 +140,9 @@ http_endpoint::http_endpoint(const http_endpoint& h): url_pars(h.url_pars), url_pieces(h.url_pieces), chunk_positions(h.chunk_positions), + re_url_normalized(h.re_url_normalized), family_url(h.family_url), - reg_compiled(h.reg_compiled), - re_url_normalized(h.re_url_normalized) + reg_compiled(h.reg_compiled) { } diff --git a/src/httpserver/details/http_endpoint.hpp b/src/httpserver/details/http_endpoint.hpp index 281685da..147956a7 100644 --- a/src/httpserver/details/http_endpoint.hpp +++ b/src/httpserver/details/http_endpoint.hpp @@ -156,7 +156,7 @@ class http_endpoint http_endpoint(const std::string& url, bool family = false, bool registration = false, - bool use_regex = true + bool use_regex = false ); private: /** diff --git a/src/webserver.cpp b/src/webserver.cpp index 45fd158c..a3104e98 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -378,7 +378,8 @@ bool webserver::stop() void webserver::unregister_resource(const string& resource) { - details::http_endpoint he(resource); + // family does not matter - it just checks the url_normalized anyhow + details::http_endpoint he(resource, false, true, regex_checking); registered_resources.erase(he); registered_resources.erase(he.get_url_complete()); registered_resources_str.erase(he.get_url_complete()); @@ -621,7 +622,7 @@ int webserver::finalize_answer( map::iterator found_endpoint; - details::http_endpoint endpoint(st_url, false, false, regex_checking); + details::http_endpoint endpoint(st_url, false, false, false); map::iterator it; diff --git a/test/integ/basic.cpp b/test/integ/basic.cpp index 5dc453ce..fdb503de 100644 --- a/test/integ/basic.cpp +++ b/test/integ/basic.cpp @@ -993,6 +993,24 @@ LT_BEGIN_AUTO_TEST(basic_suite, long_path_pieces) curl_easy_cleanup(curl); LT_END_AUTO_TEST(long_path_pieces) +LT_BEGIN_AUTO_TEST(basic_suite, url_with_regex_like_pieces) + path_pieces_resource resource; + ws->register_resource("/settings", &resource, true); + curl_global_init(CURL_GLOBAL_ALL); + + std::string s; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/settings/{}"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "settings,{},"); + curl_easy_cleanup(curl); +LT_END_AUTO_TEST(url_with_regex_like_pieces) + LT_BEGIN_AUTO_TEST_ENV() AUTORUN_TESTS() LT_END_AUTO_TEST_ENV() diff --git a/test/unit/http_endpoint_test.cpp b/test/unit/http_endpoint_test.cpp index d174f5e6..677b0fa4 100644 --- a/test/unit/http_endpoint_test.cpp +++ b/test/unit/http_endpoint_test.cpp @@ -50,8 +50,8 @@ LT_BEGIN_AUTO_TEST(http_endpoint_suite, http_endpoint_default) LT_CHECK_EQ(test_endpoint.is_regex_compiled(), false); LT_END_AUTO_TEST(http_endpoint_default) -LT_BEGIN_AUTO_TEST(http_endpoint_suite, http_endpoint_from_string_default) - http_endpoint test_endpoint("/path/to/resource"); +LT_BEGIN_AUTO_TEST(http_endpoint_suite, http_endpoint_from_string_registration) + http_endpoint test_endpoint("/path/to/resource", false, true, true); LT_CHECK_EQ(test_endpoint.get_url_complete(), "/path/to/resource"); LT_CHECK_EQ(test_endpoint.get_url_normalized(), "^/path/to/resource$"); @@ -66,10 +66,10 @@ LT_BEGIN_AUTO_TEST(http_endpoint_suite, http_endpoint_from_string_default) LT_CHECK_EQ(test_endpoint.is_family_url(), false); LT_CHECK_EQ(test_endpoint.is_regex_compiled(), true); -LT_END_AUTO_TEST(http_endpoint_from_string_default) +LT_END_AUTO_TEST(http_endpoint_from_string_registration) LT_BEGIN_AUTO_TEST(http_endpoint_suite, http_endpoint_from_string_not_beginning_with_slash) - http_endpoint test_endpoint("path/to/resource"); + http_endpoint test_endpoint("path/to/resource", false, true, true); LT_CHECK_EQ(test_endpoint.get_url_complete(), "/path/to/resource"); LT_CHECK_EQ(test_endpoint.get_url_normalized(), "^/path/to/resource$"); @@ -87,7 +87,7 @@ LT_BEGIN_AUTO_TEST(http_endpoint_suite, http_endpoint_from_string_not_beginning_ LT_END_AUTO_TEST(http_endpoint_from_string_not_beginning_with_slash) LT_BEGIN_AUTO_TEST(http_endpoint_suite, http_endpoint_from_string_ending_with_slash) - http_endpoint test_endpoint("path/to/resource/"); + http_endpoint test_endpoint("path/to/resource/", false, true, true); LT_CHECK_EQ(test_endpoint.get_url_complete(), "/path/to/resource"); LT_CHECK_EQ(test_endpoint.get_url_normalized(), "^/path/to/resource$"); @@ -105,7 +105,7 @@ LT_BEGIN_AUTO_TEST(http_endpoint_suite, http_endpoint_from_string_ending_with_sl LT_END_AUTO_TEST(http_endpoint_from_string_ending_with_slash) LT_BEGIN_AUTO_TEST(http_endpoint_suite, http_endpoint_from_string_family) - http_endpoint test_endpoint("/path/to/resource", true); + http_endpoint test_endpoint("/path/to/resource", true, true, true); LT_CHECK_EQ(test_endpoint.get_url_complete(), "/path/to/resource"); LT_CHECK_EQ(test_endpoint.get_url_normalized(), "^/path/to/resource$"); @@ -122,6 +122,24 @@ LT_BEGIN_AUTO_TEST(http_endpoint_suite, http_endpoint_from_string_family) LT_CHECK_EQ(test_endpoint.is_regex_compiled(), true); LT_END_AUTO_TEST(http_endpoint_from_string_family) +LT_BEGIN_AUTO_TEST(http_endpoint_suite, http_endpoint_default_no_regex) + http_endpoint test_endpoint("/path/to/resource"); + + LT_CHECK_EQ(test_endpoint.get_url_complete(), "/path/to/resource"); + LT_CHECK_EQ(test_endpoint.get_url_normalized(), "/path/to/resource"); + + LT_CHECK_EQ(test_endpoint.get_url_pars().size(), 0); + + string expected_arr[] = { "path", "to", "resource" }; + vector expected_pieces(expected_arr, expected_arr + sizeof(expected_arr) / sizeof(expected_arr[0])); + LT_CHECK_COLLECTIONS_EQ(test_endpoint.get_url_pieces().begin(), test_endpoint.get_url_pieces().end(), expected_pieces.begin()); + + LT_CHECK_EQ(test_endpoint.get_chunk_positions().size(), 0); + + LT_CHECK_EQ(test_endpoint.is_family_url(), false); + LT_CHECK_EQ(test_endpoint.is_regex_compiled(), false); +LT_END_AUTO_TEST(http_endpoint_default_no_regex) + LT_BEGIN_AUTO_TEST(http_endpoint_suite, http_endpoint_from_string_no_regex) http_endpoint test_endpoint("/path/to/resource", false, false, false); @@ -141,7 +159,7 @@ LT_BEGIN_AUTO_TEST(http_endpoint_suite, http_endpoint_from_string_no_regex) LT_END_AUTO_TEST(http_endpoint_from_string_no_regex) LT_BEGIN_AUTO_TEST(http_endpoint_suite, http_endpoint_registration) - http_endpoint test_endpoint("/path/to/resource", false, true); + http_endpoint test_endpoint("/path/to/resource", false, true, true); LT_CHECK_EQ(test_endpoint.get_url_complete(), "/path/to/resource"); LT_CHECK_EQ(test_endpoint.get_url_normalized(), "^/path/to/resource$"); @@ -159,7 +177,7 @@ LT_BEGIN_AUTO_TEST(http_endpoint_suite, http_endpoint_registration) LT_END_AUTO_TEST(http_endpoint_registration) LT_BEGIN_AUTO_TEST(http_endpoint_suite, http_endpoint_registration_nested_regex) - http_endpoint test_endpoint("/path/to/resource/with/[0-9]+/to/fetch", false, true); + http_endpoint test_endpoint("/path/to/resource/with/[0-9]+/to/fetch", false, true, true); LT_CHECK_EQ(test_endpoint.get_url_complete(), "/path/to/resource/with/[0-9]+/to/fetch"); LT_CHECK_EQ(test_endpoint.get_url_normalized(), "^/path/to/resource/with/[0-9]+/to/fetch$"); @@ -177,7 +195,7 @@ LT_BEGIN_AUTO_TEST(http_endpoint_suite, http_endpoint_registration_nested_regex) LT_END_AUTO_TEST(http_endpoint_registration_nested_regex) LT_BEGIN_AUTO_TEST(http_endpoint_suite, http_endpoint_registration_arg) - http_endpoint test_endpoint("/path/to/resource/with/{arg}/to/fetch", false, true); + http_endpoint test_endpoint("/path/to/resource/with/{arg}/to/fetch", false, true, true); LT_CHECK_EQ(test_endpoint.get_url_complete(), "/path/to/resource/with/{arg}/to/fetch"); LT_CHECK_EQ(test_endpoint.get_url_normalized(), "^/path/to/resource/with/([^\\/]+)/to/fetch$"); @@ -199,7 +217,7 @@ LT_BEGIN_AUTO_TEST(http_endpoint_suite, http_endpoint_registration_arg) LT_END_AUTO_TEST(http_endpoint_registration_arg) LT_BEGIN_AUTO_TEST(http_endpoint_suite, http_endpoint_registration_arg_custom_regex) - http_endpoint test_endpoint("/path/to/resource/with/{arg|([0-9]+)}/to/fetch", false, true); + http_endpoint test_endpoint("/path/to/resource/with/{arg|([0-9]+)}/to/fetch", false, true, true); LT_CHECK_EQ(test_endpoint.get_url_complete(), "/path/to/resource/with/{arg|([0-9]+)}/to/fetch"); LT_CHECK_EQ(test_endpoint.get_url_normalized(), "^/path/to/resource/with/([0-9]+)/to/fetch$"); @@ -318,6 +336,10 @@ LT_BEGIN_AUTO_TEST(http_endpoint_suite, http_endpoint_match_regex_disabled) LT_CHECK_THROW(test_endpoint.match(http_endpoint("/path/to/resource"))); LT_END_AUTO_TEST(http_endpoint_match_regex_disabled) +LT_BEGIN_AUTO_TEST(http_endpoint_suite, http_endpoint_cannot_use_regex_if_not_registering) + LT_CHECK_THROW(http_endpoint("/path/to/resource", false, false, true)); +LT_END_AUTO_TEST(http_endpoint_cannot_use_regex_if_not_registering) + LT_BEGIN_AUTO_TEST(http_endpoint_suite, comparator) LT_CHECK_EQ(http_endpoint("/a/b") < http_endpoint("/a/c"), true); LT_CHECK_EQ(http_endpoint("/a/c") < http_endpoint("/a/b"), false); From 91467eed0bd533d07648974bce46b04512447f36 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sat, 6 Jun 2020 17:24:46 +0000 Subject: [PATCH 487/623] Updated changelog and version --- ChangeLog | 4 ++++ configure.ac | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/ChangeLog b/ChangeLog index a0fa5bd1..72bbe973 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +Sat Jun 6 10:21:05 2020 -0800 + Prevent use of regex in http_endpoint outside of registration which could + allow DOS attacks. + Sat May 16 07:20:00 2020 -0800 General performance improvements (reduced use of regex, lazy-building of post-processor) diff --git a/configure.ac b/configure.ac index bd5da2d8..4328f078 100644 --- a/configure.ac +++ b/configure.ac @@ -22,7 +22,7 @@ AC_PREREQ(2.57) m4_define([libhttpserver_MAJOR_VERSION],[0])dnl m4_define([libhttpserver_MINOR_VERSION],[18])dnl -m4_define([libhttpserver_REVISION],[0])dnl +m4_define([libhttpserver_REVISION],[1])dnl m4_define([libhttpserver_PKG_VERSION],[libhttpserver_MAJOR_VERSION.libhttpserver_MINOR_VERSION.libhttpserver_REVISION])dnl m4_define([libhttpserver_LDF_VERSION],[libhttpserver_MAJOR_VERSION:libhttpserver_MINOR_VERSION:libhttpserver_REVISION])dnl AC_INIT([libhttpserver], libhttpserver_PKG_VERSION, [electrictwister2000@gmail.com]) From 51b343c6b05dd13cbde0db04984fbf392e9f6df6 Mon Sep 17 00:00:00 2001 From: bkuhls Date: Mon, 6 Jul 2020 04:23:21 +0200 Subject: [PATCH 488/623] Compatibility with libmicrohttpd 0.9.71 (#199) From the libmicrohttpd 0.9.71 release notes: The release introduces an 'enum MHD_Result' instead of for certain API misuse bugs by providing better types (not everything is an 'int'). While this does NOT change the binary API, this change _will_ cause compiler warnings for all legacy code -- until 'int' is replaced with 'enum MHD_Result'. --- src/http_request.cpp | 6 +++--- src/httpserver/http_request.hpp | 6 +++--- src/httpserver/http_utils.hpp | 4 ++++ src/httpserver/webserver.hpp | 14 +++++++------- src/webserver.cpp | 22 +++++++++++++--------- 5 files changed, 30 insertions(+), 22 deletions(-) diff --git a/src/http_request.cpp b/src/http_request.cpp index 57036639..be342c7c 100644 --- a/src/http_request.cpp +++ b/src/http_request.cpp @@ -88,7 +88,7 @@ const std::string http_request::get_connection_value(const std::string& key, enu return header_c; } -int http_request::build_request_header( +MHD_Result http_request::build_request_header( void *cls, enum MHD_ValueKind kind, const char *key, @@ -189,7 +189,7 @@ const std::string http_request::get_querystring() const return querystring; } -int http_request::build_request_args( +MHD_Result http_request::build_request_args( void *cls, enum MHD_ValueKind kind, const char *key, @@ -204,7 +204,7 @@ int http_request::build_request_args( return MHD_YES; } -int http_request::build_request_querystring( +MHD_Result http_request::build_request_querystring( void *cls, enum MHD_ValueKind kind, const char *key, diff --git a/src/httpserver/http_request.hpp b/src/httpserver/http_request.hpp index 139272be..62e52751 100644 --- a/src/httpserver/http_request.hpp +++ b/src/httpserver/http_request.hpp @@ -247,15 +247,15 @@ class http_request unescaper_ptr unescaper = 0x0; - static int build_request_header(void *cls, enum MHD_ValueKind kind, + static MHD_Result build_request_header(void *cls, enum MHD_ValueKind kind, const char *key, const char *value ); - static int build_request_args(void *cls, enum MHD_ValueKind kind, + static MHD_Result build_request_args(void *cls, enum MHD_ValueKind kind, const char *key, const char *value ); - static int build_request_querystring(void *cls, enum MHD_ValueKind kind, + static MHD_Result build_request_querystring(void *cls, enum MHD_ValueKind kind, const char *key, const char *value ); diff --git a/src/httpserver/http_utils.hpp b/src/httpserver/http_utils.hpp index 9ad89b48..a8121977 100644 --- a/src/httpserver/http_utils.hpp +++ b/src/httpserver/http_utils.hpp @@ -53,6 +53,10 @@ #define DEFAULT_MASK_VALUE 0xFFFF +#if MHD_VERSION < 0x00097002 +typedef int MHD_Result; +#endif + namespace httpserver { typedef void(*unescaper_ptr)(std::string&); diff --git a/src/httpserver/webserver.hpp b/src/httpserver/webserver.hpp index 1ff472b2..661b6eef 100644 --- a/src/httpserver/webserver.hpp +++ b/src/httpserver/webserver.hpp @@ -195,14 +195,14 @@ class webserver enum MHD_RequestTerminationCode toe ); - static int answer_to_connection + static MHD_Result answer_to_connection ( void* cls, MHD_Connection* connection, const char* url, const char* method, const char* version, const char* upload_data, size_t* upload_data_size, void** con_cls ); - static int post_iterator + static MHD_Result post_iterator ( void *cls, enum MHD_ValueKind kind, @@ -219,25 +219,25 @@ class webserver void **con_cls, int upgrade_socket ); - int requests_answer_first_step(MHD_Connection* connection, + MHD_Result requests_answer_first_step(MHD_Connection* connection, struct details::modded_request* mr ); - int requests_answer_second_step(MHD_Connection* connection, + MHD_Result requests_answer_second_step(MHD_Connection* connection, const char* method, const char* version, const char* upload_data, size_t* upload_data_size, struct details::modded_request* mr ); - int finalize_answer(MHD_Connection* connection, + MHD_Result finalize_answer(MHD_Connection* connection, struct details::modded_request* mr, const char* method ); - int complete_request(MHD_Connection* connection, + MHD_Result complete_request(MHD_Connection* connection, struct details::modded_request* mr, const char* version, const char* method ); - friend int policy_callback (void *cls, + friend MHD_Result policy_callback (void *cls, const struct sockaddr* addr, socklen_t addrlen ); friend void error_log(void* cls, const char* fmt, va_list ap); diff --git a/src/webserver.cpp b/src/webserver.cpp index a3104e98..3340eb01 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -75,6 +75,10 @@ #define SOCK_CLOEXEC 02000000 #endif +#if MHD_VERSION < 0x00097002 +typedef int MHD_Result; +#endif + using namespace std; namespace httpserver @@ -82,7 +86,7 @@ namespace httpserver using namespace http; -int policy_callback (void *, const struct sockaddr*, socklen_t); +MHD_Result policy_callback (void *, const struct sockaddr*, socklen_t); void error_log(void*, const char*, va_list); void* uri_log(void*, const char*); void access_log(webserver*, string); @@ -421,7 +425,7 @@ void webserver::disallow_ip(const string& ip) allowances.erase(ip); } -int policy_callback (void *cls, const struct sockaddr* addr, socklen_t addrlen) +MHD_Result policy_callback (void *cls, const struct sockaddr* addr, socklen_t addrlen) { if(!(static_cast(cls))->ban_system_enabled) return MHD_YES; @@ -468,7 +472,7 @@ size_t unescaper_func(void * cls, struct MHD_Connection *c, char *s) return std::string(s).size(); } -int webserver::post_iterator (void *cls, enum MHD_ValueKind kind, +MHD_Result webserver::post_iterator (void *cls, enum MHD_ValueKind kind, const char *key, const char *filename, const char *content_type, @@ -522,7 +526,7 @@ const std::shared_ptr webserver::internal_error_page(details::mod } } -int webserver::requests_answer_first_step( +MHD_Result webserver::requests_answer_first_step( MHD_Connection* connection, struct details::modded_request* mr ) @@ -574,7 +578,7 @@ int webserver::requests_answer_first_step( return MHD_YES; } -int webserver::requests_answer_second_step( +MHD_Result webserver::requests_answer_second_step( MHD_Connection* connection, const char* method, const char* version, const char* upload_data, size_t* upload_data_size, struct details::modded_request* mr @@ -597,7 +601,7 @@ int webserver::requests_answer_second_step( return MHD_YES; } -int webserver::finalize_answer( +MHD_Result webserver::finalize_answer( MHD_Connection* connection, struct details::modded_request* mr, const char* method @@ -731,10 +735,10 @@ int webserver::finalize_answer( mr->dhrs->decorate_response(raw_response); to_ret = mr->dhrs->enqueue_response(connection, raw_response); MHD_destroy_response(raw_response); - return to_ret; + return (MHD_Result) to_ret; } -int webserver::complete_request( +MHD_Result webserver::complete_request( MHD_Connection* connection, struct details::modded_request* mr, const char* version, @@ -750,7 +754,7 @@ int webserver::complete_request( return finalize_answer(connection, mr, method); } -int webserver::answer_to_connection(void* cls, MHD_Connection* connection, +MHD_Result webserver::answer_to_connection(void* cls, MHD_Connection* connection, const char* url, const char* method, const char* version, const char* upload_data, size_t* upload_data_size, void** con_cls From a35281a42960a7f6b9428541cf08cdb419b0e0c0 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sat, 11 Jul 2020 22:30:36 -0700 Subject: [PATCH 489/623] Add configuration to build on Appveyor (msys2) (#200) --- appveyor.yml | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 appveyor.yml diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 00000000..4fc56074 --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,32 @@ +platform: x64 + +branches: + except: + - /.*travis.*/ +skip_commits: + message: /travis/ + files: + - .travis.yml + +environment: + matrix: + - compiler: msys2 + MINGW_CHOST: x86_64-w64-mingw32 + MSYS2_ARCH: x86_64 +init: + - 'echo Building libhttpserver %version% for Windows' + - 'echo System architecture: %PLATFORM%' + - 'echo Repo build branch is: %APPVEYOR_REPO_BRANCH%' + - 'echo Build folder is: %APPVEYOR_BUILD_FOLDER%' + - 'echo Repo build commit is: %APPVEYOR_REPO_COMMIT%' + - 'echo Cygwin root is: %CYG_ROOT%' +install: + - 'if "%compiler%"=="msys2" C:\msys64\msys2_shell.cmd -defterm -no-start -msys2 -c "pacman --noconfirm --force -S --needed mingw-w64-$MSYS2_ARCH-{libtool,make,pkg-config,libsystre,doxygen,gnutls,graphviz,curl}"' + - 'if "%compiler%"=="msys2" C:\msys64\msys2_shell.cmd -defterm -no-start -mingw64 -full-path -here -c "cd $APPVEYOR_BUILD_FOLDER && curl https://s3.amazonaws.com/libhttpserver/libmicrohttpd_releases/libmicrohttpd-0.9.59.tar.gz -o libmicrohttpd-0.9.59.tar.gz"' + - 'if "%compiler%"=="msys2" C:\msys64\msys2_shell.cmd -defterm -no-start -mingw64 -full-path -here -c "cd $APPVEYOR_BUILD_FOLDER && tar -xzf libmicrohttpd-0.9.59.tar.gz"' + - 'if "%compiler%"=="msys2" C:\msys64\msys2_shell.cmd -defterm -no-start -mingw64 -full-path -here -c "cd $APPVEYOR_BUILD_FOLDER/libmicrohttpd-0.9.59 && ./configure --disable-examples --enable-poll=no --prefix /C/msys64 && make && make install"' + - 'if "%compiler%"=="msys2" C:\msys64\msys2_shell.cmd -defterm -no-start -mingw64 -full-path -here -c "cd $APPVEYOR_BUILD_FOLDER && ./bootstrap"' + - 'if "%compiler%"=="msys2" C:\msys64\msys2_shell.cmd -defterm -no-start -mingw64 -full-path -here -c "cd $APPVEYOR_BUILD_FOLDER && mkdir build && cd build && MANIFEST_TOOL=no; ../configure --disable-fastopen --prefix /C/msys64 CXXFLAGS=-I/C/msys64/include LDFLAGS=-L/C/msys64/lib; make"' +build_script: + - 'if "%compiler%"=="msys2" C:\msys64\msys2_shell.cmd -defterm -no-start -mingw64 -full-path -here -c "cd $APPVEYOR_BUILD_FOLDER/build && make check"' + - 'if "%compiler%"=="msys2" C:\msys64\msys2_shell.cmd -defterm -no-start -mingw64 -full-path -here -c "cd $APPVEYOR_BUILD_FOLDER/build && cat test/test-suite.log"' From 1e2b7d99e761cae72ee97afb7a3b813aabcf82d9 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sat, 11 Jul 2020 22:33:41 -0700 Subject: [PATCH 490/623] Removing windows as now building on appveyor Travis support for windows is in beta and tends to fail way too often due to travis itself. This makes the whole build system unreliable. Appveyor solves this. --- .travis.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 675d6a48..69412488 100644 --- a/.travis.yml +++ b/.travis.yml @@ -112,8 +112,6 @@ matrix: - compiler: clang env: DEBUG="debug" COVERAGE="coverage" include: - - os: windows - env: DEBUG="nodebug" COVERAGE="nocoverage" YARN_GPG=no - os: linux addons: apt: From cbaa3f34436a048f96cd1b50480207f31c0662bf Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sat, 11 Jul 2020 22:40:11 -0700 Subject: [PATCH 491/623] Add AppVeyor badge --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index dcd0024a..fb7fedad 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,7 @@ Copyright (C) 2011-2019 Sebastiano Merlino. # The libhttpserver reference manual [![Build Status](https://travis-ci.org/etr/libhttpserver.png?branch=master)](https://travis-ci.org/etr/libhttpserver) +[![Build status](https://ci.appveyor.com/api/projects/status/ktoy6ewkrf0q1hw6/branch/master?svg=true)](https://ci.appveyor.com/project/etr/libhttpserver/branch/master) [![codecov](https://codecov.io/gh/etr/libhttpserver/branch/master/graph/badge.svg)](https://codecov.io/gh/etr/libhttpserver) [![Codacy Badge](https://api.codacy.com/project/badge/Grade/5fa4bdc3815b4c10977f3badefedecd6)](https://www.codacy.com/app/etr/libhttpserver?utm_source=github.com&utm_medium=referral&utm_content=etr/libhttpserver&utm_campaign=Badge_Grade) [![Gitter chat](https://badges.gitter.im/etr/libhttpserver.png)](https://gitter.im/libhttpserver/community) From e9da6f84514275d3f925faae24dcadc2ff99cfe2 Mon Sep 17 00:00:00 2001 From: fechantillac-antidot <44236947+fechantillac-antidot@users.noreply.github.com> Date: Wed, 22 Jul 2020 18:15:39 +0200 Subject: [PATCH 492/623] Allow to avoid a string copy in string_response constructor (#201) --- src/httpserver/string_response.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/httpserver/string_response.hpp b/src/httpserver/string_response.hpp index 87f12c67..1c2183d0 100644 --- a/src/httpserver/string_response.hpp +++ b/src/httpserver/string_response.hpp @@ -36,12 +36,12 @@ class string_response : public http_response string_response() = default; explicit string_response( - const std::string& content, + std::string content, int response_code = http::http_utils::http_ok, const std::string& content_type = http::http_utils::text_plain ): http_response(response_code, content_type), - content(content) + content(std::move(content)) { } From 3fa034c3f823d1ac10f1a20062fa7393f9e6b79e Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Wed, 5 Aug 2020 08:38:34 -0700 Subject: [PATCH 493/623] Updated version of GCC recommended in the readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index fb7fedad..14ec4b7a 100644 --- a/README.md +++ b/README.md @@ -79,7 +79,7 @@ Additionally, clients can specify resource limits on the overall number of conne libhttpserver can be used without any dependencies aside for libmicrohttpd. The minimum versions required are: -* g++ >= 4.8.4 or clang-3.6 +* g++ >= 5.5.0 or clang-3.6 * libmicrohttpd >= 0.9.52 * [Optionally]: for TLS (HTTPS) support, you'll need [libgnutls](http://www.gnutls.org/). * [Optionally]: to compile the code-reference, you'll need [doxygen](http://www.doxygen.nl/). From 6ede3c9b688a7b2c30825ed2fc7bee28c5b7e8c2 Mon Sep 17 00:00:00 2001 From: Chris Benesch <34348427+beneschtech@users.noreply.github.com> Date: Fri, 7 Aug 2020 23:46:12 -0600 Subject: [PATCH 494/623] Update configure and bootstrap, fix warnings in http_endpoint (#205) * Update configure and bootstrap, fix compiler warnings in http_endpoint and deferred_response, add OS specific readme for commonly seen issues * Move const to end to satisfy both clang and g++ --- README.CentOS-7 | 7 +++++++ README.FreeBSD | 9 +++++++++ README.md | 4 +++- bootstrap | 2 +- configure.ac | 3 +++ src/httpserver/deferred_response.hpp | 2 +- src/httpserver/details/http_endpoint.hpp | 4 ++-- 7 files changed, 26 insertions(+), 5 deletions(-) create mode 100644 README.CentOS-7 create mode 100644 README.FreeBSD diff --git a/README.CentOS-7 b/README.CentOS-7 new file mode 100644 index 00000000..1dfaaa70 --- /dev/null +++ b/README.CentOS-7 @@ -0,0 +1,7 @@ +## Cent OS 7 / RHEL 7 + +CentOS 7 has a lower version of gcc (4.8.7) that is barely C++11 capable and this library +needs a better compiler. We recommend at least gcc 5+ + +We recommend installing devtoolset-8 +https://www.softwarecollections.org/en/scls/rhscl/devtoolset-8/ diff --git a/README.FreeBSD b/README.FreeBSD new file mode 100644 index 00000000..853c5589 --- /dev/null +++ b/README.FreeBSD @@ -0,0 +1,9 @@ +## Building on FreeBSD (Tested on 12.0) + +# Due to the differences in the directory structures on BSD systems some minor tweaks need to occur +# Also, FreeBSD and AIX "make" command is not compatible with gmake, like Linux and Mingw are + +export MAKE=gmake +bootstrap +configure CPPFLAGS=-I/usr/local/include LDFLAGS=-L/usr/local/lib +gmake diff --git a/README.md b/README.md index 14ec4b7a..ba3ea6b0 100644 --- a/README.md +++ b/README.md @@ -76,7 +76,7 @@ Additionally, clients can specify resource limits on the overall number of conne [Back to TOC](#table-of-contents) ## Requirements -libhttpserver can be used without any dependencies aside for libmicrohttpd. +libhttpserver can be used without any dependencies aside from libmicrohttpd. The minimum versions required are: * g++ >= 5.5.0 or clang-3.6 @@ -92,6 +92,8 @@ For versions before 0.18.0, on MinGW, you will need: Furthermore, the testcases use [libcurl](http://curl.haxx.se/libcurl/) but you don't need it to compile the library. +Please refer to the readme file for your particular distribution if there is one for important notes. + [Back to TOC](#table-of-contents) ## Building diff --git a/bootstrap b/bootstrap index 67425d32..bc1ad731 100755 --- a/bootstrap +++ b/bootstrap @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # This file is part of libhttpserver # Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino diff --git a/configure.ac b/configure.ac index 4328f078..8fc0e314 100644 --- a/configure.ac +++ b/configure.ac @@ -284,6 +284,9 @@ case $host_os in AM_CXXFLAGS="$AM_CXXFLAGS -DDARWIN" AM_CFLAGS="$AM_CFLAGS -DDARWIN" ;; + freebsd* ) + AM_LDFLAGS="" + ;; esac AC_MSG_CHECKING([whether to build with coverage information]) diff --git a/src/httpserver/deferred_response.hpp b/src/httpserver/deferred_response.hpp index 170d5e82..2193f960 100644 --- a/src/httpserver/deferred_response.hpp +++ b/src/httpserver/deferred_response.hpp @@ -70,7 +70,7 @@ class deferred_response : public string_response ssize_t (*cycle_callback)(std::shared_ptr, char*, size_t); std::shared_ptr closure_data; - static ssize_t cb(void* cls, uint64_t pos, char* buf, size_t max) + static ssize_t cb(void* cls, uint64_t, char* buf, size_t max) { deferred_response* dfr = static_cast*>(cls); return dfr->cycle_callback(dfr->closure_data, buf, max); diff --git a/src/httpserver/details/http_endpoint.hpp b/src/httpserver/details/http_endpoint.hpp index 147956a7..37fd0d8c 100644 --- a/src/httpserver/details/http_endpoint.hpp +++ b/src/httpserver/details/http_endpoint.hpp @@ -120,12 +120,12 @@ class http_endpoint return chunk_positions; } - const bool is_family_url() const + bool is_family_url() const { return family_url; } - const bool is_regex_compiled() const + bool is_regex_compiled() const { return reg_compiled; } From 56c68faf4d82009f7eb0a1030b593b59fbd88278 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Mon, 24 Aug 2020 20:38:08 -0700 Subject: [PATCH 495/623] Introduce use_dual_stack to allow support for both ipv4 and ipv6 * Introduce use_dual_stack to allow support for both ipv4 and ipv6. See: https://github.com/etr/libhttpserver/issues/202 * Remove explicit DNS resolve for IPV6 * IPV6 tests run if IPV6_TESTS_ENABLED specified * Enable IPV6 tests only on osx Travis linux doesn't currently support IPV6 --- .travis.yml | 1 + README.md | 1 + src/httpserver/create_webserver.hpp | 3 ++ src/httpserver/webserver.hpp | 1 + src/webserver.cpp | 3 ++ test/integ/ws_start_stop.cpp | 53 +++++++++++++++++++++++++++++ 6 files changed, 62 insertions(+) diff --git a/.travis.yml b/.travis.yml index 69412488..4b525dba 100644 --- a/.travis.yml +++ b/.travis.yml @@ -33,6 +33,7 @@ before_install: - if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo pip install gcovr; fi - if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo apt-get install cppcheck; fi - if [ "$TRAVIS_OS_NAME" = "osx" ]; then export CFLAGS='-mtune=generic'; fi + - if [ "$TRAVIS_OS_NAME" = "osx" ]; then export IPV6_TESTS_ENABLED="true"; fi - |- case $TRAVIS_OS_NAME in windows) diff --git a/README.md b/README.md index ba3ea6b0..344c1ae6 100644 --- a/README.md +++ b/README.md @@ -188,6 +188,7 @@ For example, if your connection limit is “1”, a browser may open a first con * _.bind_socket(**int** socket_fd):_ Listen socket to use. Pass a listen socket for the daemon to use (systemd-style). If this option is used, the daemon will not open its own listen socket(s). The argument passed must be of type "int" and refer to an existing socket that has been bound to a port and is listening. * _.max_thread_stack_size(**int** stack_size):_ Maximum stack size for threads created by the library. Not specifying this option or using a value of zero means using the system default (which is likely to differ based on your platform). Default is `0 (system default)`. * _.use_ipv6() and .no_ipv6():_ Enable or disable the IPv6 protocol support (by default, libhttpserver will just support IPv4). If you specify this and the local platform does not support it, starting up the server will throw an exception. `off` by default. +* _.use_dual_stack() and .no_dual_stack():_ Enable or disable the support for both IPv6 and IPv4 protocols at the same time (by default, libhttpserver will just support IPv4). If you specify this and the local platform does not support it, starting up the server will throw an exception. Note that this will mean that IPv4 addresses are returned in the IPv6-mapped format (the ’structsockaddrin6’ format will be used for IPv4 and IPv6). `off` by default. * _.pedantic() and .no_pedantic():_ Enables pedantic checks about the protocol (as opposed to as tolerant as possible). Specifically, at the moment, this flag causes the library to reject HTTP 1.1 connections without a `Host` header. This is required by the standard, but of course in violation of the “be as liberal as possible in what you accept” norm. It is recommended to turn this **off** if you are testing clients against the library, and **on** in production. `off` by default. * _.debug() and .no_debug():_ Enables debug messages from the library. `off` by default. * _.regex_checking() and .no_regex_checking():_ Enables pattern matching for endpoints. Read more [here](#registering-resources). `on` by default. diff --git a/src/httpserver/create_webserver.hpp b/src/httpserver/create_webserver.hpp index 678a1616..39cb3ecb 100644 --- a/src/httpserver/create_webserver.hpp +++ b/src/httpserver/create_webserver.hpp @@ -120,6 +120,8 @@ class create_webserver create_webserver& no_ssl() { _use_ssl = false; return *this; } create_webserver& use_ipv6() { _use_ipv6 = true; return *this; } create_webserver& no_ipv6() { _use_ipv6 = false; return *this; } + create_webserver& use_dual_stack() { _use_dual_stack = true; return *this; } + create_webserver& no_dual_stack() { _use_dual_stack = false; return *this; } create_webserver& debug() { _debug = true; return *this; } create_webserver& no_debug() { _debug = false; return *this; } create_webserver& pedantic() { _pedantic = true; return *this; } @@ -273,6 +275,7 @@ class create_webserver int _max_thread_stack_size = 0; bool _use_ssl = false; bool _use_ipv6 = false; + bool _use_dual_stack = false; bool _debug = false; bool _pedantic = false; std::string _https_mem_key = ""; diff --git a/src/httpserver/webserver.hpp b/src/httpserver/webserver.hpp index 661b6eef..c1ca06de 100644 --- a/src/httpserver/webserver.hpp +++ b/src/httpserver/webserver.hpp @@ -154,6 +154,7 @@ class webserver const int max_thread_stack_size; const bool use_ssl; const bool use_ipv6; + const bool use_dual_stack; const bool debug; const bool pedantic; const std::string https_mem_key; diff --git a/src/webserver.cpp b/src/webserver.cpp index 3340eb01..6b7dd47f 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -149,6 +149,7 @@ webserver::webserver(const create_webserver& params): max_thread_stack_size(params._max_thread_stack_size), use_ssl(params._use_ssl), use_ipv6(params._use_ipv6), + use_dual_stack(params._use_dual_stack), debug(params._debug), pedantic(params._pedantic), https_mem_key(params._https_mem_key), @@ -311,6 +312,8 @@ bool webserver::start(bool blocking) start_conf |= MHD_USE_SSL; if(use_ipv6) start_conf |= MHD_USE_IPv6; + if(use_dual_stack) + start_conf |= MHD_USE_DUAL_STACK; if(debug) start_conf |= MHD_USE_DEBUG; if(pedantic) diff --git a/test/integ/ws_start_stop.cpp b/test/integ/ws_start_stop.cpp index 57d1acdb..7aa185e8 100644 --- a/test/integ/ws_start_stop.cpp +++ b/test/integ/ws_start_stop.cpp @@ -143,6 +143,59 @@ LT_BEGIN_AUTO_TEST(ws_start_stop_suite, start_stop) } LT_END_AUTO_TEST(start_stop) +#if defined(IPV6_TESTS_ENABLED) + +LT_BEGIN_AUTO_TEST(ws_start_stop_suite, ipv6) + { + webserver ws = create_webserver(8080).use_ipv6(); + ok_resource ok; + ws.register_resource("base", &ok); + ws.start(false); + + curl_global_init(CURL_GLOBAL_ALL); + std::string s; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "http://localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V6); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "OK"); + curl_easy_cleanup(curl); + + ws.stop(); + } +LT_END_AUTO_TEST(ipv6) + +LT_BEGIN_AUTO_TEST(ws_start_stop_suite, dual_stack) + { + webserver ws = create_webserver(8080).use_dual_stack(); + ok_resource ok; + ws.register_resource("base", &ok); + ws.start(false); + + curl_global_init(CURL_GLOBAL_ALL); + std::string s; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "http://localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V6); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "OK"); + curl_easy_cleanup(curl); + + ws.stop(); + } +LT_END_AUTO_TEST(dual_stack) + +#endif LT_BEGIN_AUTO_TEST(ws_start_stop_suite, sweet_kill) webserver ws = create_webserver(8080); From b7a72457d19d2578bcf0c2ba7d75b862f7ae05f1 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Tue, 25 Aug 2020 13:46:40 -0700 Subject: [PATCH 496/623] cleanup travis build file (#207) Remove references to windows specific steps that are now unecessary as windows builds have been moved to appveyor. --- .travis.yml | 65 +++++++++++++---------------------------------------- 1 file changed, 16 insertions(+), 49 deletions(-) diff --git a/.travis.yml b/.travis.yml index 4b525dba..7ae7cfec 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,54 +9,24 @@ env: - DEBUG="debug" COVERAGE="coverage" - DEBUG="nodebug" COVERAGE="nocoverage" - LINKING="static" -before_cache: -- |- - case $TRAVIS_OS_NAME in - windows) - # https://unix.stackexchange.com/a/137322/107554 - $msys2 pacman --sync --clean --noconfirm - ;; - esac -cache: - directories: - - $HOME/AppData/Local/Temp/chocolatey - - /C/tools/msys64 before_install: - eval "${MATRIX_EVAL}" - - if [ "$TRAVIS_OS_NAME" != "windows" ]; then export LDFLAGS="$LDFLAGS -L/usr/local/lib -L/usr/lib"; fi - - if [ "$TRAVIS_OS_NAME" != "windows" ]; then export PATH=$PATH:/usr/local/lib; fi - - if [ "$TRAVIS_OS_NAME" != "windows" ]; then export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib; fi - - if [ "$TRAVIS_OS_NAME" != "windows" ]; then export DYLD_LIBRARY_PATH=$DYLD_LIBRARY_PATH:/usr/local/lib; fi - - if [ "$TRAVIS_OS_NAME" != "windows" ]; then export buildshell=''; fi + - export LDFLAGS="$LDFLAGS -L/usr/local/lib -L/usr/lib" + - export PATH=$PATH:/usr/local/lib + - export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib + - export DYLD_LIBRARY_PATH=$DYLD_LIBRARY_PATH:/usr/local/lib - if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo apt-get install info install-info; fi - if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo pip install codecov; fi - if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo pip install gcovr; fi - if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo apt-get install cppcheck; fi - if [ "$TRAVIS_OS_NAME" = "osx" ]; then export CFLAGS='-mtune=generic'; fi - if [ "$TRAVIS_OS_NAME" = "osx" ]; then export IPV6_TESTS_ENABLED="true"; fi - - |- - case $TRAVIS_OS_NAME in - windows) - [[ ! -f C:/tools/msys64/msys2_shell.cmd ]] && rm -rf C:/tools/msys64 - choco uninstall -y mingw - choco upgrade --no-progress -y msys2 - export msys2='cmd //C RefreshEnv.cmd ' - export msys2+='& set MSYS=winsymlinks:nativestrict ' - export msys2+='& C:\\tools\\msys64\\msys2_shell.cmd -defterm -no-start' - export buildshell="$msys2 -mingw64 -full-path -here -c "\"\$@"\" --" - export msys2+=" -msys2 -c "\"\$@"\" --" - $msys2 pacman --sync --noconfirm --disable-download-timeout --needed mingw-w64-x86_64-toolchain - $msys2 pacman -Syu --noconfirm --disable-download-timeout autoconf libtool automake make autoconf-archive pkg-config mingw-w64-x86_64-libsystre mingw-w64-x86_64-doxygen mingw-w64-x86_64-gnutls mingw-w64-x86_64-graphviz mingw-w64-x86_64-curl - export PATH=/C/tools/msys64/mingw64/bin:$PATH - export MAKE=mingw32-make # so that Autotools can find it - ;; - esac - curl https://s3.amazonaws.com/libhttpserver/libmicrohttpd_releases/libmicrohttpd-0.9.59.tar.gz -o libmicrohttpd-0.9.59.tar.gz - tar -xzf libmicrohttpd-0.9.59.tar.gz - cd libmicrohttpd-0.9.59 - - if [ "$TRAVIS_OS_NAME" != "windows" ]; then $buildshell ./configure --disable-examples; else $buildshell ./configure --disable-examples --enable-poll=no; fi; - - $buildshell make - - if [ "$TRAVIS_OS_NAME" != "windows" ]; then sudo make install; else $buildshell make install; fi + - ./configure --disable-examples + - make + - sudo make install - cd .. - if [ "$BUILD_TYPE" = "asan" ]; then export CFLAGS='-fsanitize=address'; export CXXLAGS='-fsanitize=address'; export LDFLAGS='-fsanitize=address'; fi - if [ "$BUILD_TYPE" = "msan" ]; then export CFLAGS='-fsanitize=memory'; export CXXLAGS='-fsanitize=memory'; export LDFLAGS='-fsanitize=memory'; fi @@ -64,25 +34,24 @@ before_install: - if [ "$BUILD_TYPE" = "tsan" ]; then export CFLAGS='-fsanitize=thread'; export CXXLAGS='-fsanitize=thread'; export LDFLAGS='-fsanitize=thread'; fi - if [ "$BUILD_TYPE" = "ubsan" ]; then export export CFLAGS='-fsanitize=undefined'; export CXXLAGS='-fsanitize=undefined'; export LDFLAGS='-fsanitize=undefined'; fi install: - - $buildshell ./bootstrap + - ./bootstrap - mkdir build - cd build - - if [ "$TRAVIS_OS_NAME" = "windows" ]; then export MANIFEST_TOOL='no'; fi - if [ "$LINKING" = "static" ]; then - $buildshell ../configure --enable-static --disable-fastopen; + ../configure --enable-static --disable-fastopen; elif [ "$DEBUG" = "debug" ] && [ "$COVERAGE" = "coverage" ]; then - $buildshell ../configure --enable-debug --enable-coverage --disable-shared --disable-fastopen; + ../configure --enable-debug --enable-coverage --disable-shared --disable-fastopen; elif [ "$DEBUG" = "debug" ]; then - $buildshell ../configure --enable-debug --disable-shared --disable-fastopen; + ../configure --enable-debug --disable-shared --disable-fastopen; elif [ "$VALGRIND" = "valgrind" ]; then - $buildshell ../configure --enable-debug --disable-fastopen --disable-valgrind-helgrind --disable-valgrind-drd --disable-valgrind-sgcheck; + ../configure --enable-debug --disable-fastopen --disable-valgrind-helgrind --disable-valgrind-drd --disable-valgrind-sgcheck; else - $buildshell ../configure --disable-fastopen; + ../configure --disable-fastopen; fi - - $buildshell make + - make script: - - $buildshell make check - - $buildshell cat test/test-suite.log + - make check + - cat test/test-suite.log - if [ "$VALGRIND" = "valgrind" ]; then make check-valgrind; fi; - if [ "$VALGRIND" = "valgrind" ]; then cat test/test-suite-memcheck.log; fi; - if [ "$TRAVIS_OS_NAME" = "linux" ]; then cd ../src/; cppcheck --error-exitcode=1 .; cd ../build; fi @@ -104,8 +73,6 @@ script: ./benchmark_threads 8080 & sleep 5 && ab -n 10000000 -c 100 localhost:8080/plaintext fi -after_script: - - if [ "$TRAVIS_OS_NAME" = "windows" ]; then taskkill //F //PID $(ps -Wla | tr -s ' ' | grep gpg | cut -f2 -d' ') ; fi after_success: - if [ "$DEBUG" = "debug" ] && [ "$COVERAGE" = "coverage" ] && [ "$TRAVIS_OS_NAME" = "linux" ]; then bash <(curl -s https://codecov.io/bash); fi matrix: From 44360e95c2571c66501d573efc792437ca255f79 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Wed, 26 Aug 2020 22:47:51 -0700 Subject: [PATCH 497/623] Add YWIU checks to travis (#208) Adds the YWIU checks to travis. Fixed the include statements to pass the tests. Forces code to be tested on appveyor too (in order to have this tested) - more generally this is useful because apparent travis-only change might affect windows too (like in this case). --- .travis.yml | 55 ++++++++++++++++++-- appveyor.yml | 8 --- custom_iwyu.imp | 17 ++++++ src/basic_auth_fail_response.cpp | 4 ++ src/deferred_response.cpp | 3 ++ src/details/http_endpoint.cpp | 10 +++- src/digest_auth_fail_response.cpp | 4 ++ src/file_response.cpp | 6 ++- src/http_request.cpp | 4 +- src/http_resource.cpp | 11 ++-- src/http_response.cpp | 10 +--- src/http_utils.cpp | 9 ++-- src/httpserver/basic_auth_fail_response.hpp | 5 ++ src/httpserver/deferred_response.hpp | 8 ++- src/httpserver/digest_auth_fail_response.hpp | 5 ++ src/httpserver/file_response.hpp | 4 ++ src/httpserver/http_request.hpp | 12 ++--- src/httpserver/http_resource.hpp | 18 +++---- src/httpserver/http_response.hpp | 4 -- src/httpserver/http_utils.hpp | 7 ++- src/httpserver/string_response.hpp | 5 ++ src/httpserver/webserver.hpp | 31 +++++------ src/string_response.cpp | 4 ++ src/string_utilities.cpp | 3 -- src/webserver.cpp | 15 ++---- 25 files changed, 165 insertions(+), 97 deletions(-) create mode 100644 custom_iwyu.imp diff --git a/.travis.yml b/.travis.yml index 7ae7cfec..5aa66c2e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,6 +11,28 @@ env: - LINKING="static" before_install: - eval "${MATRIX_EVAL}" + # Installing iwyu manually because clang and iwyu paths won't match on Ubuntu otherwise. + - if [ "$IWYU" = "iwyu" ]; then + CLANG_VERSION=`clang --version | grep version | cut -f3 -d' ' | cut -f1 -d'-'` ; + CLANG_PKG_VERSION=`echo $CLANG_VERSION | cut -f1,2 -d'.'` + CLANG_PREFIX_PATH="/usr/local/clang-${CLANG_VERSION}/lib/clang/${CLANG_VERSION}" ; + CLANG_BIN_PATH="/usr/local/clang-${CLANG_VERSION}/bin" ; + git clone https://github.com/include-what-you-use/include-what-you-use.git ; + cd include-what-you-use ; + echo "$CLANG_PKG_VERSION" | grep '\.[0-9]$' ; + if [ $? -eq 0 ]; then + git checkout clang_${CLANG_PKG_VERSION} ; + else + git checkout clang_${CLANG_PKG_VERSION}.0 ; + fi; + cd .. ; + mkdir build_iwyu ; + cd build_iwyu ; + cmake -G "Unix Makefiles" -DCMAKE_PREFIX_PATH=$CLANG_PREFIX_PATH -DCMAKE_C_COMPILER=$CLANG_BIN_PATH/clang -DCMAKE_CXX_COMPILER=$CLANG_BIN_PATH/clang++ ../include-what-you-use ; + make ; + sudo make install ; + cd .. ; + fi - export LDFLAGS="$LDFLAGS -L/usr/local/lib -L/usr/lib" - export PATH=$PATH:/usr/local/lib - export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib @@ -37,7 +59,8 @@ install: - ./bootstrap - mkdir build - cd build - - if [ "$LINKING" = "static" ]; then + - | + if [ "$LINKING" = "static" ]; then ../configure --enable-static --disable-fastopen; elif [ "$DEBUG" = "debug" ] && [ "$COVERAGE" = "coverage" ]; then ../configure --enable-debug --enable-coverage --disable-shared --disable-fastopen; @@ -45,13 +68,23 @@ install: ../configure --enable-debug --disable-shared --disable-fastopen; elif [ "$VALGRIND" = "valgrind" ]; then ../configure --enable-debug --disable-fastopen --disable-valgrind-helgrind --disable-valgrind-drd --disable-valgrind-sgcheck; + elif [ "$IWYU" = "iwyu" ]; then + ../configure --disable-examples; else ../configure --disable-fastopen; fi - - make + # Make or run iwyu. If running iwyu, check for the result code to be 2 (IWYU always returns an error code, if it is 2, no corrections are necessary). + - | + if [ "$IWYU" = "iwyu" ]; then + make -k CXX='/usr/local/bin/include-what-you-use -Xiwyu --mapping_file=${top_builddir}/../custom_iwyu.imp' CXXFLAGS="-isystem ${CLANG_PREFIX_PATH}/include -std=c++11 -DHTTPSERVER_COMPILATION -D_REENTRANT $CXXFLAGS" ; + if [ $? -ne 2 ]; then + return 1; + fi + else + make; + fi script: - - make check - - cat test/test-suite.log + - if [ "$IWYU" != "iwyu" ]; then make check; cat test/test-suite.log; fi - if [ "$VALGRIND" = "valgrind" ]; then make check-valgrind; fi; - if [ "$VALGRIND" = "valgrind" ]; then cat test/test-suite-memcheck.log; fi; - if [ "$TRAVIS_OS_NAME" = "linux" ]; then cd ../src/; cppcheck --error-exitcode=1 .; cd ../build; fi @@ -309,3 +342,17 @@ matrix: - clang-9 env: - MATRIX_EVAL="CC=clang-9 && CXX=clang++-9" + - os: linux + compiler: clang + addons: + apt: + sources: + - llvm-toolchain-xenial-7 + - ubuntu-toolchain-r-test + packages: + - iwyu + - cmake + - llvm-dev + - libclang-dev + env: + - MATRIX_EVAL="IWYU=iwyu" diff --git a/appveyor.yml b/appveyor.yml index 4fc56074..c86df7e5 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,13 +1,5 @@ platform: x64 -branches: - except: - - /.*travis.*/ -skip_commits: - message: /travis/ - files: - - .travis.yml - environment: matrix: - compiler: msys2 diff --git a/custom_iwyu.imp b/custom_iwyu.imp new file mode 100644 index 00000000..e814a310 --- /dev/null +++ b/custom_iwyu.imp @@ -0,0 +1,17 @@ +[ + { include: ["\"microhttpd.h\"", "private", "", "public"] }, + { include: ["", "private", "", "public"] }, + + { symbol: ["std::exception", "private", "", "public"]}, + { symbol: ["std::shared_ptr", "private", "", "public"]}, + { symbol: ["std::uint16_t", "private", "", "public"]}, + { symbol: ["std::uint64_t", "private", "", "public"]}, + { symbol: ["std::istringstream", "private", "", "public"]}, + { symbol: ["std::stringstream", "private", "", "public"]}, + { symbol: ["std::ifstream", "private", "", "public"]}, + + { symbol: ["uint16_t", "private", "", "public"]}, + { symbol: ["uint64_t", "private", "", "public"]}, + + { symbol: ["MHD_Connection", "private", "", "public"]}, +] diff --git a/src/basic_auth_fail_response.cpp b/src/basic_auth_fail_response.cpp index 500fceb6..49d9f5a9 100644 --- a/src/basic_auth_fail_response.cpp +++ b/src/basic_auth_fail_response.cpp @@ -19,6 +19,10 @@ */ #include "httpserver/basic_auth_fail_response.hpp" +#include + +struct MHD_Connection; +struct MHD_Response; using namespace std; diff --git a/src/deferred_response.cpp b/src/deferred_response.cpp index 08033c80..f3d44527 100644 --- a/src/deferred_response.cpp +++ b/src/deferred_response.cpp @@ -19,6 +19,9 @@ */ #include "httpserver/deferred_response.hpp" +#include + +struct MHD_Response; using namespace std; diff --git a/src/details/http_endpoint.cpp b/src/details/http_endpoint.cpp index be76ddde..584fb507 100644 --- a/src/details/http_endpoint.cpp +++ b/src/details/http_endpoint.cpp @@ -18,12 +18,18 @@ USA */ +#include +#include +#include +#include +#include #include +#include +#include +#include #include "httpserver/details/http_endpoint.hpp" - #include "httpserver/http_utils.hpp" -#include "httpserver/string_utilities.hpp" using namespace std; diff --git a/src/digest_auth_fail_response.cpp b/src/digest_auth_fail_response.cpp index d2fd5263..ca95526d 100644 --- a/src/digest_auth_fail_response.cpp +++ b/src/digest_auth_fail_response.cpp @@ -19,6 +19,10 @@ */ #include "httpserver/digest_auth_fail_response.hpp" +#include + +struct MHD_Connection; +struct MHD_Response; using namespace std; diff --git a/src/file_response.cpp b/src/file_response.cpp index 1503e4bd..66d13a1c 100644 --- a/src/file_response.cpp +++ b/src/file_response.cpp @@ -19,8 +19,12 @@ */ #include "httpserver/file_response.hpp" - #include +#include +#include +#include + +struct MHD_Response; using namespace std; diff --git a/src/http_request.cpp b/src/http_request.cpp index be342c7c..d7d8e2af 100644 --- a/src/http_request.cpp +++ b/src/http_request.cpp @@ -20,9 +20,9 @@ */ #include "httpserver/http_request.hpp" - +#include +#include #include - #include "httpserver/http_utils.hpp" #include "httpserver/string_utilities.hpp" diff --git a/src/http_resource.cpp b/src/http_resource.cpp index 0fc6402a..46bdb0de 100644 --- a/src/http_resource.cpp +++ b/src/http_resource.cpp @@ -19,15 +19,10 @@ */ #include "httpserver/http_resource.hpp" - -#include - -#include "httpserver/http_request.hpp" -#include "httpserver/http_response.hpp" -#include "httpserver/http_utils.hpp" +#include #include "httpserver/string_response.hpp" -#include "httpserver/string_utilities.hpp" -#include "httpserver/webserver.hpp" + +namespace httpserver { class http_response; } using namespace std; diff --git a/src/http_response.cpp b/src/http_response.cpp index c502f2ac..2d261e3d 100644 --- a/src/http_response.cpp +++ b/src/http_response.cpp @@ -19,16 +19,10 @@ */ #include "httpserver/http_response.hpp" - -#include -#include -#include -#include +#include #include -#include - +#include #include "httpserver/http_utils.hpp" -#include "httpserver/webserver.hpp" using namespace std; diff --git a/src/http_utils.cpp b/src/http_utils.cpp index c9d3aa66..caefabc5 100644 --- a/src/http_utils.cpp +++ b/src/http_utils.cpp @@ -24,9 +24,6 @@ #include #include #else // WIN32 check -#if defined(__FreeBSD__) -#include -#endif // FreeBSD #include #include #include @@ -36,10 +33,10 @@ #include #include -#include -#include #include -#include +#include +#include +#include #include #include #include diff --git a/src/httpserver/basic_auth_fail_response.hpp b/src/httpserver/basic_auth_fail_response.hpp index c7e2d476..a28fa3d9 100644 --- a/src/httpserver/basic_auth_fail_response.hpp +++ b/src/httpserver/basic_auth_fail_response.hpp @@ -25,8 +25,13 @@ #ifndef _BASIC_AUTH_FAIL_RESPONSE_HPP_ #define _BASIC_AUTH_FAIL_RESPONSE_HPP_ +#include +#include "http_utils.hpp" #include "httpserver/string_response.hpp" +struct MHD_Connection; +struct MHD_Response; + namespace httpserver { diff --git a/src/httpserver/deferred_response.hpp b/src/httpserver/deferred_response.hpp index 2193f960..9e4601e2 100644 --- a/src/httpserver/deferred_response.hpp +++ b/src/httpserver/deferred_response.hpp @@ -25,10 +25,16 @@ #ifndef _DEFERRED_RESPONSE_HPP_ #define _DEFERRED_RESPONSE_HPP_ +#include +#include +#include #include - +#include +#include "http_utils.hpp" #include "httpserver/string_response.hpp" +struct MHD_Response; + namespace httpserver { diff --git a/src/httpserver/digest_auth_fail_response.hpp b/src/httpserver/digest_auth_fail_response.hpp index 292f477b..50abcee2 100644 --- a/src/httpserver/digest_auth_fail_response.hpp +++ b/src/httpserver/digest_auth_fail_response.hpp @@ -25,8 +25,13 @@ #ifndef _DIGEST_AUTH_FAIL_RESPONSE_HPP_ #define _DIGEST_AUTH_FAIL_RESPONSE_HPP_ +#include +#include "http_utils.hpp" #include "httpserver/string_response.hpp" +struct MHD_Connection; +struct MHD_Response; + namespace httpserver { diff --git a/src/httpserver/file_response.hpp b/src/httpserver/file_response.hpp index cb80c114..0c9386fb 100644 --- a/src/httpserver/file_response.hpp +++ b/src/httpserver/file_response.hpp @@ -25,8 +25,12 @@ #ifndef _FILE_RESPONSE_HPP_ #define _FILE_RESPONSE_HPP_ +#include +#include "http_utils.hpp" #include "httpserver/http_response.hpp" +struct MHD_Response; + namespace httpserver { diff --git a/src/httpserver/http_request.hpp b/src/httpserver/http_request.hpp index 62e52751..6aacbfe6 100644 --- a/src/httpserver/http_request.hpp +++ b/src/httpserver/http_request.hpp @@ -25,6 +25,10 @@ #ifndef _HTTP_REQUEST_HPP_ #define _HTTP_REQUEST_HPP_ +#include + +#include +#include #include #include #include @@ -38,14 +42,6 @@ struct MHD_Connection; namespace httpserver { -class webserver; - -namespace http -{ - class header_comparator; - class arg_comparator; -}; - /** * Class representing an abstraction for an Http Request. It is used from classes using these apis to receive information through http protocol. **/ diff --git a/src/httpserver/http_resource.hpp b/src/httpserver/http_resource.hpp index 7841b96b..04f67cb8 100644 --- a/src/httpserver/http_resource.hpp +++ b/src/httpserver/http_resource.hpp @@ -28,24 +28,18 @@ #ifdef DEBUG #include #endif + #include #include #include +#include -#include "httpserver/http_response.hpp" - -namespace httpserver -{ +namespace httpserver { class http_request; } +namespace httpserver { class http_response; } -class webserver; -class http_request; +namespace httpserver { -namespace details -{ - -std::shared_ptr empty_render(const http_request& r); - -}; +namespace details { std::shared_ptr empty_render(const http_request& r); }; /** * Class representing a callable http resource. diff --git a/src/httpserver/http_response.hpp b/src/httpserver/http_response.hpp index 33c1df42..1f3f0971 100644 --- a/src/httpserver/http_response.hpp +++ b/src/httpserver/http_response.hpp @@ -27,11 +27,7 @@ #include #include -#include #include -#include -#include - #include "httpserver/http_utils.hpp" struct MHD_Connection; diff --git a/src/httpserver/http_utils.hpp b/src/httpserver/http_utils.hpp index a8121977..e2aa0339 100644 --- a/src/httpserver/http_utils.hpp +++ b/src/httpserver/http_utils.hpp @@ -43,9 +43,14 @@ #endif #include +#include + +#if !defined(__MINGW32__) +#include +#endif + #include #include -#include #include #include #include diff --git a/src/httpserver/string_response.hpp b/src/httpserver/string_response.hpp index 1c2183d0..43e7580d 100644 --- a/src/httpserver/string_response.hpp +++ b/src/httpserver/string_response.hpp @@ -25,8 +25,13 @@ #ifndef _STRING_RESPONSE_HPP_ #define _STRING_RESPONSE_HPP_ +#include +#include +#include "http_utils.hpp" #include "httpserver/http_response.hpp" +struct MHD_Response; + namespace httpserver { diff --git a/src/httpserver/webserver.hpp b/src/httpserver/webserver.hpp index c1ca06de..02d626c5 100644 --- a/src/httpserver/webserver.hpp +++ b/src/httpserver/webserver.hpp @@ -30,35 +30,32 @@ #define NOT_METHOD_ERROR "Method not Acceptable" #define GENERIC_ERROR "Internal Error" +#include #include +#include +#include #include -#include -#include + +#if !defined(__MINGW32__) +#include +#endif + #include #include #include -#include #include -#include -#include +#include "http_utils.hpp" #include "httpserver/create_webserver.hpp" #include "httpserver/details/http_endpoint.hpp" -#include "httpserver/http_response.hpp" -namespace httpserver { +namespace httpserver { class http_resource; } +namespace httpserver { class http_response; } +namespace httpserver { namespace details { struct modded_request; } } -class http_resource; -class create_webserver; +struct MHD_Connection; -namespace http { -struct ip_representation; -struct httpserver_ska; -}; - -namespace details { - struct modded_request; -} +namespace httpserver { /** * Class representing the webserver. Main class of the apis. diff --git a/src/string_response.cpp b/src/string_response.cpp index 7720d61b..75a557f8 100644 --- a/src/string_response.cpp +++ b/src/string_response.cpp @@ -19,6 +19,10 @@ */ #include "httpserver/string_response.hpp" +#include +#include + +struct MHD_Response; using namespace std; diff --git a/src/string_utilities.cpp b/src/string_utilities.cpp index c557fe06..a3937f2e 100644 --- a/src/string_utilities.cpp +++ b/src/string_utilities.cpp @@ -22,9 +22,6 @@ #include #include -#include -#include -#include #include #include #include diff --git a/src/webserver.cpp b/src/webserver.cpp index 6b7dd47f..38542f2a 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -31,26 +31,16 @@ #if defined(__CYGWIN__) #include #endif -#include +#include #include #endif #include -#include -#include #include #include -#include #include #include #include -#include -#include -#include -#include -#include -#include -#include #include #include #include @@ -67,7 +57,8 @@ #include "httpserver/http_response.hpp" #include "httpserver/http_utils.hpp" #include "httpserver/string_response.hpp" -#include "httpserver/string_utilities.hpp" + +struct MHD_Connection; #define _REENTRANT 1 From f4caf637090b5ff39bfc146b0ecd633f670c3697 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Thu, 27 Aug 2020 08:13:19 -0700 Subject: [PATCH 498/623] Moved to travis.com --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 344c1ae6..f3bdf4c1 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ Copyright (C) 2011-2019 Sebastiano Merlino. # The libhttpserver reference manual -[![Build Status](https://travis-ci.org/etr/libhttpserver.png?branch=master)](https://travis-ci.org/etr/libhttpserver) +[![Build Status](https://api.travis-ci.com/etr/libhttpserver.svg?branch=master)](https://travis-ci.com/etr/libhttpserver) [![Build status](https://ci.appveyor.com/api/projects/status/ktoy6ewkrf0q1hw6/branch/master?svg=true)](https://ci.appveyor.com/project/etr/libhttpserver/branch/master) [![codecov](https://codecov.io/gh/etr/libhttpserver/branch/master/graph/badge.svg)](https://codecov.io/gh/etr/libhttpserver) [![Codacy Badge](https://api.codacy.com/project/badge/Grade/5fa4bdc3815b4c10977f3badefedecd6)](https://www.codacy.com/app/etr/libhttpserver?utm_source=github.com&utm_medium=referral&utm_content=etr/libhttpserver&utm_campaign=Badge_Grade) From f647e33fbe860b40ccf87edf8db9e461f39e7fc3 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sat, 12 Sep 2020 21:35:25 -0700 Subject: [PATCH 499/623] Add support for codeql (#210) CodeQL security checks will be performed on each push to master and each PR --- .github/workflows/codeql-analysis.yml | 73 +++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 .github/workflows/codeql-analysis.yml diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml new file mode 100644 index 00000000..fb1041db --- /dev/null +++ b/.github/workflows/codeql-analysis.yml @@ -0,0 +1,73 @@ +name: "CodeQL" + +on: + push: + branches: [master] + pull_request: + # The branches below must be a subset of the branches above + branches: [master] + schedule: + - cron: '0 4 * * 4' + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + + strategy: + fail-fast: false + matrix: + # Override automatic language detection by changing the below list + # Supported options are ['csharp', 'cpp', 'go', 'java', 'javascript', 'python'] + language: ['cpp'] + # Learn more... + # https://docs.github.com/en/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#overriding-automatic-language-detection + + steps: + - name: Checkout repository + uses: actions/checkout@v2 + with: + # We must fetch at least the immediate parents so that if this is + # a pull request then we can checkout the head. + fetch-depth: 2 + + # If this run was triggered by a pull request event, then checkout + # the head of the pull request instead of the merge commit. + - run: git checkout HEAD^2 + if: ${{ github.event_name == 'pull_request' }} + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v1 + with: + languages: ${{ matrix.language }} + + # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + #- name: Autobuild + # uses: github/codeql-action/autobuild@v1 + + # ℹ️ Command-line programs to run using the OS shell. + # 📚 https://git.io/JvXDl + + # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines + # and modify them (or add more) to build your code if your project + # uses a compiled language + + - name: Install libmicrohttpd dependency + run: | + curl https://s3.amazonaws.com/libhttpserver/libmicrohttpd_releases/libmicrohttpd-0.9.59.tar.gz -o libmicrohttpd-0.9.59.tar.gz ; + tar -xzf libmicrohttpd-0.9.59.tar.gz ; + cd libmicrohttpd-0.9.59 ; + ./configure --disable-examples ; + make ; + sudo make install ; + + - name: Manual steps to build the library + run: | + ./bootstrap ; + ./configure --enable-same-directory-build; + make ; + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v1 From 8ad6755e5636ff6939c48b0c5a587364892236a1 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sat, 12 Sep 2020 21:50:45 -0700 Subject: [PATCH 500/623] Restrict CodeQL analysis only to src directory --- .github/workflows/codeql-analysis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index fb1041db..82db4982 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -41,6 +41,7 @@ jobs: uses: github/codeql-action/init@v1 with: languages: ${{ matrix.language }} + paths : src # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) From c9746044d4b5c849f2b484087e110dd2a6e55308 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sat, 12 Sep 2020 21:58:48 -0700 Subject: [PATCH 501/623] CodeQL scan back the whole repo. No need to restrict - false positives on test and dependencies can be managed directly on github. --- .github/workflows/codeql-analysis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 82db4982..fb1041db 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -41,7 +41,6 @@ jobs: uses: github/codeql-action/init@v1 with: languages: ${{ matrix.language }} - paths : src # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) From a0e5b766d391e1464e60d31c1b96766cec78e5b8 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sat, 21 Nov 2020 07:24:47 -0800 Subject: [PATCH 502/623] Updated ChangeLog. --- ChangeLog | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/ChangeLog b/ChangeLog index 72bbe973..3d801c40 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,15 @@ +Sat Nov 21 07:20:00 2020 -0800 + Added support on build for CodeQL security checks. + Moved builds to travis.com + Added IWYU checks as part of build and cleaned-up accordingly. + Introduced dual-stack support. + Added OS specific tips, and cleaned up some compiler warnings. + Updates to readme and documentation. + Slight performances improvement by allowing to skip a copy in + string_response constructor. + Moved windows builds to AppVeyor. + Made the library compatible with libmicrohttpd v0.9.71 and above. + Sat Jun 6 10:21:05 2020 -0800 Prevent use of regex in http_endpoint outside of registration which could allow DOS attacks. From 721bfe7c246a917ba135fcb458cd9c716aa74a33 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sat, 21 Nov 2020 07:41:53 -0800 Subject: [PATCH 503/623] Specify worker image Attempt to fix appveyor inability to connect to pacman repos. --- appveyor.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/appveyor.yml b/appveyor.yml index c86df7e5..db5df45c 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -5,6 +5,7 @@ environment: - compiler: msys2 MINGW_CHOST: x86_64-w64-mingw32 MSYS2_ARCH: x86_64 + APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019 init: - 'echo Building libhttpserver %version% for Windows' - 'echo System architecture: %PLATFORM%' From fca01ca4544b87241af1c5ea78d581973f7a4485 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sat, 21 Nov 2020 07:43:51 -0800 Subject: [PATCH 504/623] Use overwrite instead of force Force is deprecated in the new version on appveyor --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index db5df45c..ce0c08d4 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -14,7 +14,7 @@ init: - 'echo Repo build commit is: %APPVEYOR_REPO_COMMIT%' - 'echo Cygwin root is: %CYG_ROOT%' install: - - 'if "%compiler%"=="msys2" C:\msys64\msys2_shell.cmd -defterm -no-start -msys2 -c "pacman --noconfirm --force -S --needed mingw-w64-$MSYS2_ARCH-{libtool,make,pkg-config,libsystre,doxygen,gnutls,graphviz,curl}"' + - 'if "%compiler%"=="msys2" C:\msys64\msys2_shell.cmd -defterm -no-start -msys2 -c "pacman --noconfirm --overwrite -S --needed mingw-w64-$MSYS2_ARCH-{libtool,make,pkg-config,libsystre,doxygen,gnutls,graphviz,curl}"' - 'if "%compiler%"=="msys2" C:\msys64\msys2_shell.cmd -defterm -no-start -mingw64 -full-path -here -c "cd $APPVEYOR_BUILD_FOLDER && curl https://s3.amazonaws.com/libhttpserver/libmicrohttpd_releases/libmicrohttpd-0.9.59.tar.gz -o libmicrohttpd-0.9.59.tar.gz"' - 'if "%compiler%"=="msys2" C:\msys64\msys2_shell.cmd -defterm -no-start -mingw64 -full-path -here -c "cd $APPVEYOR_BUILD_FOLDER && tar -xzf libmicrohttpd-0.9.59.tar.gz"' - 'if "%compiler%"=="msys2" C:\msys64\msys2_shell.cmd -defterm -no-start -mingw64 -full-path -here -c "cd $APPVEYOR_BUILD_FOLDER/libmicrohttpd-0.9.59 && ./configure --disable-examples --enable-poll=no --prefix /C/msys64 && make && make install"' From 9ef6cc6c09d8ddf42fb3f0c7491ad74041947889 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sat, 21 Nov 2020 07:44:41 -0800 Subject: [PATCH 505/623] Remove overwrite or force options --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index ce0c08d4..127c8d63 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -14,7 +14,7 @@ init: - 'echo Repo build commit is: %APPVEYOR_REPO_COMMIT%' - 'echo Cygwin root is: %CYG_ROOT%' install: - - 'if "%compiler%"=="msys2" C:\msys64\msys2_shell.cmd -defterm -no-start -msys2 -c "pacman --noconfirm --overwrite -S --needed mingw-w64-$MSYS2_ARCH-{libtool,make,pkg-config,libsystre,doxygen,gnutls,graphviz,curl}"' + - 'if "%compiler%"=="msys2" C:\msys64\msys2_shell.cmd -defterm -no-start -msys2 -c "pacman --noconfirm -S --needed mingw-w64-$MSYS2_ARCH-{libtool,make,pkg-config,libsystre,doxygen,gnutls,graphviz,curl}"' - 'if "%compiler%"=="msys2" C:\msys64\msys2_shell.cmd -defterm -no-start -mingw64 -full-path -here -c "cd $APPVEYOR_BUILD_FOLDER && curl https://s3.amazonaws.com/libhttpserver/libmicrohttpd_releases/libmicrohttpd-0.9.59.tar.gz -o libmicrohttpd-0.9.59.tar.gz"' - 'if "%compiler%"=="msys2" C:\msys64\msys2_shell.cmd -defterm -no-start -mingw64 -full-path -here -c "cd $APPVEYOR_BUILD_FOLDER && tar -xzf libmicrohttpd-0.9.59.tar.gz"' - 'if "%compiler%"=="msys2" C:\msys64\msys2_shell.cmd -defterm -no-start -mingw64 -full-path -here -c "cd $APPVEYOR_BUILD_FOLDER/libmicrohttpd-0.9.59 && ./configure --disable-examples --enable-poll=no --prefix /C/msys64 && make && make install"' From 70820d4708684582694340107726906319392987 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sat, 21 Nov 2020 11:13:52 -0800 Subject: [PATCH 506/623] Add RDP access to AppVeyor VM --- appveyor.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/appveyor.yml b/appveyor.yml index 127c8d63..fa44b1df 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -13,6 +13,7 @@ init: - 'echo Build folder is: %APPVEYOR_BUILD_FOLDER%' - 'echo Repo build commit is: %APPVEYOR_REPO_COMMIT%' - 'echo Cygwin root is: %CYG_ROOT%' + - ps: iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1')) install: - 'if "%compiler%"=="msys2" C:\msys64\msys2_shell.cmd -defterm -no-start -msys2 -c "pacman --noconfirm -S --needed mingw-w64-$MSYS2_ARCH-{libtool,make,pkg-config,libsystre,doxygen,gnutls,graphviz,curl}"' - 'if "%compiler%"=="msys2" C:\msys64\msys2_shell.cmd -defterm -no-start -mingw64 -full-path -here -c "cd $APPVEYOR_BUILD_FOLDER && curl https://s3.amazonaws.com/libhttpserver/libmicrohttpd_releases/libmicrohttpd-0.9.59.tar.gz -o libmicrohttpd-0.9.59.tar.gz"' From d7c2e0256a511dcfb018f28cf71b13bbc69cc5ab Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sat, 21 Nov 2020 12:20:05 -0800 Subject: [PATCH 507/623] Disable threaded tests on windows Appveyor cannot manage threads in its last iteration. --- test/integ/threaded.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/test/integ/threaded.cpp b/test/integ/threaded.cpp index 1b29d512..5a2c82ed 100644 --- a/test/integ/threaded.cpp +++ b/test/integ/threaded.cpp @@ -25,6 +25,12 @@ #include "httpserver.hpp" #include "littletest.hpp" +#if defined(_WIN32) && ! defined(__CYGWIN__) +#define _WINDOWS +#endif + +#ifndef _WINDOWS + using namespace httpserver; using namespace std; @@ -73,3 +79,5 @@ LT_END_AUTO_TEST(base) LT_BEGIN_AUTO_TEST_ENV() AUTORUN_TESTS() LT_END_AUTO_TEST_ENV() + +#endif From 2e2d32886c5e59a482ca479829562af46a08029d Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sat, 21 Nov 2020 13:15:59 -0800 Subject: [PATCH 508/623] Ignore threading tests on windows --- test/integ/threaded.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/test/integ/threaded.cpp b/test/integ/threaded.cpp index 5a2c82ed..eaf7d6e7 100644 --- a/test/integ/threaded.cpp +++ b/test/integ/threaded.cpp @@ -18,6 +18,12 @@ USA */ +#if defined(_WIN32) && ! defined(__CYGWIN__) +#define _WINDOWS +#endif + +#ifndef _WINDOWS + #include #include #include @@ -25,12 +31,6 @@ #include "httpserver.hpp" #include "littletest.hpp" -#if defined(_WIN32) && ! defined(__CYGWIN__) -#define _WINDOWS -#endif - -#ifndef _WINDOWS - using namespace httpserver; using namespace std; From 52ce5380e6f202d2675074e8cc29bc0463a08a46 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sat, 21 Nov 2020 14:14:36 -0800 Subject: [PATCH 509/623] Disable threading tests on windows --- test/integ/threaded.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/test/integ/threaded.cpp b/test/integ/threaded.cpp index eaf7d6e7..3e1576c5 100644 --- a/test/integ/threaded.cpp +++ b/test/integ/threaded.cpp @@ -22,8 +22,6 @@ #define _WINDOWS #endif -#ifndef _WINDOWS - #include #include #include @@ -45,22 +43,29 @@ class ok_resource : public http_resource LT_BEGIN_SUITE(threaded_suite) +#ifndef _WINDOWS webserver* ws; +#endif void set_up() { +#ifndef _WINDOWS ws = new webserver(create_webserver(8080).start_method(http::http_utils::INTERNAL_SELECT).max_threads(5)); ws->start(false); +#endif } void tear_down() { +#ifndef _WINDOWS ws->stop(); delete ws; +#endif } LT_END_SUITE(threaded_suite) LT_BEGIN_AUTO_TEST(threaded_suite, base) +#ifndef _WINDOWS ok_resource resource; ws->register_resource("base", &resource); curl_global_init(CURL_GLOBAL_ALL); @@ -74,10 +79,9 @@ LT_BEGIN_AUTO_TEST(threaded_suite, base) res = curl_easy_perform(curl); LT_ASSERT_EQ(res, 0); curl_easy_cleanup(curl); +#endif LT_END_AUTO_TEST(base) LT_BEGIN_AUTO_TEST_ENV() AUTORUN_TESTS() LT_END_AUTO_TEST_ENV() - -#endif From 7cb4eb8454ab936fcf2c54a61cc4d71a65f14680 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sat, 21 Nov 2020 14:34:17 -0800 Subject: [PATCH 510/623] Suppress thread tests on windows --- test/integ/ws_start_stop.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/integ/ws_start_stop.cpp b/test/integ/ws_start_stop.cpp index 7aa185e8..aca06486 100644 --- a/test/integ/ws_start_stop.cpp +++ b/test/integ/ws_start_stop.cpp @@ -75,6 +75,8 @@ LT_BEGIN_SUITE(ws_start_stop_suite) } LT_END_SUITE(ws_start_stop_suite) +#ifndef _WINDOWS + LT_BEGIN_AUTO_TEST(ws_start_stop_suite, start_stop) { webserver ws = create_webserver(8080); @@ -602,6 +604,8 @@ LT_BEGIN_AUTO_TEST(ws_start_stop_suite, custom_error_resources) ws.stop(); LT_END_AUTO_TEST(custom_error_resources) +#endif + LT_BEGIN_AUTO_TEST_ENV() AUTORUN_TESTS() LT_END_AUTO_TEST_ENV() From ec973dc883b0d33f81c7f69b66dd5770ba14e695 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Fri, 11 Dec 2020 14:07:51 -0800 Subject: [PATCH 511/623] Fix heap buffer overflow (#215) * Fix potential issue with heap buffer overflow. Thanks to sgbhat2 for fuzztesting the library and finding out. * Added unit test to cover new codepath in unescaping --- src/http_utils.cpp | 12 +++++++----- test/unit/http_utils_test.cpp | 18 +++++++++++++++++- 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/src/http_utils.cpp b/src/http_utils.cpp index caefabc5..53c2230f 100644 --- a/src/http_utils.cpp +++ b/src/http_utils.cpp @@ -33,6 +33,7 @@ #include #include +#include #include #include #include @@ -304,12 +305,13 @@ size_t http_unescape(std::string& val) { if (val.empty()) return 0; - int rpos = 0; - int wpos = 0; + unsigned int rpos = 0; + unsigned int wpos = 0; unsigned int num; + unsigned int size = val.size(); - while ('\0' != val[rpos]) + while (rpos < size && val[rpos] != '\0') { switch (val[rpos]) { @@ -319,8 +321,8 @@ size_t http_unescape(std::string& val) rpos++; break; case '%': - if ( (1 == sscanf (val.substr(rpos + 1).c_str(), "%2x", &num)) || - (1 == sscanf (val.substr(rpos + 1).c_str(), "%2X", &num)) + if (size > rpos + 2 && ((1 == sscanf (val.substr(rpos + 1, 2).c_str(), "%2x", &num)) || + (1 == sscanf (val.substr(rpos + 1, 2).c_str(), "%2X", &num))) ) { val[wpos] = (unsigned char) num; diff --git a/test/unit/http_utils_test.cpp b/test/unit/http_utils_test.cpp index 6ef9cb99..792ef59d 100644 --- a/test/unit/http_utils_test.cpp +++ b/test/unit/http_utils_test.cpp @@ -67,7 +67,7 @@ LT_BEGIN_AUTO_TEST(http_utils_suite, unescape) LT_END_AUTO_TEST(unescape) LT_BEGIN_AUTO_TEST(http_utils_suite, unescape_plus) - char* with_plus = (char*) malloc(6 * sizeof(char)); + char* with_plus = (char*) malloc(4 * sizeof(char)); sprintf(with_plus, "%s", "A+B"); std::string string_with_plus = with_plus; int expected_size = http::http_unescape(string_with_plus); @@ -82,6 +82,22 @@ LT_BEGIN_AUTO_TEST(http_utils_suite, unescape_plus) free(expected); LT_END_AUTO_TEST(unescape_plus) +LT_BEGIN_AUTO_TEST(http_utils_suite, unescape_partial_marker) + char* with_marker = (char*) malloc(6 * sizeof(char)); + sprintf(with_marker, "%s", "A+B%0"); + std::string string_with_marker = with_marker; + int expected_size = http::http_unescape(string_with_marker); + + char* expected = (char*) malloc(6 * sizeof(char)); + sprintf(expected, "%s", "A B%0"); + + LT_CHECK_EQ(string_with_marker, string(expected)); + LT_CHECK_EQ(expected_size, 5); + + free(with_marker); + free(expected); +LT_END_AUTO_TEST(unescape_partial_marker) + LT_BEGIN_AUTO_TEST(http_utils_suite, tokenize_url) string value = "test/this/url/here"; string expected_arr[] = { "test", "this", "url", "here" }; From 00dc269e1f3328817d43b0571141b52d3ec614ac Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 28 Feb 2021 21:08:06 -0800 Subject: [PATCH 512/623] Move off travis to github actions (#222) * Add CI builds to github actions * Ensures that make is run from build * Install CURL to support testing * Fix tools installation * Open 8080 port on the container Needed to run tests * Cannot set ports explicitly * Print tests results * Print test logs on failure * Print log on failure * Update lib dependencies * Run ldconfig only on linux * Add caching for CURL on mac Speeds up builds * Restrict CURL building to mac only * Always install curl from source * fix curl install * cache the entire curl directory * ubuntu use system curl * Compile CURL on mac with ssl enabled * Cache libmicrohttpd * Add memory checks * just install clang * Set envs using github actions syntax * Set flags for memory checks * Add tests on extra gcc/g++ versions * Add extra clang versions * Add valgrind and iwyu checks * Print clang versions and paths * Print clang path * Simplify iwyu Attempt to use system iwyu * fix iwyu path * Install additional clang to support iwyu builds * Use compile iwyu * fix clang paths * Fix clang paths * Fix clang path * fix iwyu import * Print iwyu resources * Build iwyu again * Use proper pkg version for clang in iwyu * Fix path to compiler in iwyu * Set iwyu root path * Use llvm root path * No need to pass sys libs in CXXFLAS * use clang-7 with iwyu * Fix version fetching for clang-7 * Use clang-7 from S3 * Use clang-7 options in iwyu * Fix to clang-7 * Fix bin path * Use clang-9 with iwyu * spice path using llvm tools * fix tool version * build iwyu in place * force c++14 compatibility * fix typo * Fix path * Use system iwyu and reference libraries * Install IWYU only * Temporarily disable iwyu installation * No custom config for iwyu * Fix compiler for iwyu * Use clang 5 with iwyu * IWYU with clang-9 * Remove resource dir printing * Fix includes to match iwyu * Handle IWYU error code * Handle IWYU error code * Add performance tests and cleanup * Use github actions syntax for selective builds * Fix typo * Use explicit IP address on ab * remove travis config --- .github/workflows/verify-build.yml | 470 +++++++++++++++++++++++++++++ .travis.yml | 358 ---------------------- src/basic_auth_fail_response.cpp | 1 + src/deferred_response.cpp | 1 + src/details/http_endpoint.cpp | 2 +- src/digest_auth_fail_response.cpp | 1 + src/file_response.cpp | 1 + src/http_resource.cpp | 1 + src/http_utils.cpp | 1 - src/string_response.cpp | 1 + src/webserver.cpp | 3 + 11 files changed, 480 insertions(+), 360 deletions(-) create mode 100644 .github/workflows/verify-build.yml delete mode 100644 .travis.yml diff --git a/.github/workflows/verify-build.yml b/.github/workflows/verify-build.yml new file mode 100644 index 00000000..09464e0d --- /dev/null +++ b/.github/workflows/verify-build.yml @@ -0,0 +1,470 @@ +name: "Verify Build" + +on: + push: + branches: [master] + pull_request: + # The branches below must be a subset of the branches above + branches: [master] + schedule: + - cron: '0 0 * * 0' + +jobs: + verify: + name: Verify + runs-on: ${{ matrix.os }} + env: + DEBUG: ${{ matrix.debug }} + COVERAGE: ${{ matrix.coverage }} + LINKING: ${{ matrix.linking }} + BUILD_TYPE: ${{ matrix.build-type }} + CC: ${{ matrix.c-compiler }} + CXX: ${{ matrix.cc-compiler }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, macos-latest] + test-group: [basic] + compiler-family: [none] + c-compiler: [gcc, clang] + cc-compiler: [g++, clang++] + debug: [debug, nodebug] + coverage: [coverage, nocoverage] + linking: [dynamic, static] + build-type: [classic] + exclude: + - c-compiler: gcc + cc-compiler: clang++ + - c-compiler: clang + cc-compiler: g++ + - c-compiler: clang + debug: debug + - debug: debug + coverage: nocoverage + - debug: nodebug + coverage: coverage + - linking: static + debug: debug + - linking: static + coverage: coverage + include: + - test-group: extra + os: ubuntu-latest + build-type: asan + compiler-family: clang + c-compiler: clang-3.9 + cc-compiler: clang++-3.9 + debug: debug + coverage: nocoverage + - test-group: extra + os: ubuntu-latest + build-type: msan + compiler-family: clang + c-compiler: clang-3.9 + cc-compiler: clang++-3.9 + debug: debug + coverage: nocoverage + - test-group: extra + os: ubuntu-latest + build-type: lsan + compiler-family: clang + c-compiler: clang-3.9 + cc-compiler: clang++-3.9 + debug: debug + coverage: nocoverage + - test-group: extra + os: ubuntu-latest + build-type: tsan + compiler-family: clang + c-compiler: clang-3.9 + cc-compiler: clang++-3.9 + debug: debug + coverage: nocoverage + - test-group: extra + os: ubuntu-latest + build-type: ubsan + compiler-family: clang + c-compiler: clang-3.9 + cc-compiler: clang++-3.9 + debug: debug + coverage: nocoverage + - test-group: extra + os: ubuntu-latest + build-type: none + compiler-family: gcc + c-compiler: gcc-5 + cc-compiler: g++-5 + debug: nodebug + coverage: nocoverage + - test-group: extra + os: ubuntu-latest + build-type: none + compiler-family: gcc + c-compiler: gcc-6 + cc-compiler: g++-6 + debug: nodebug + coverage: nocoverage + - test-group: extra + os: ubuntu-latest + build-type: none + compiler-family: gcc + c-compiler: gcc-7 + cc-compiler: g++-7 + debug: nodebug + coverage: nocoverage + - test-group: extra + os: ubuntu-latest + build-type: none + compiler-family: gcc + c-compiler: gcc-8 + cc-compiler: g++-8 + debug: nodebug + coverage: nocoverage + - test-group: extra + os: ubuntu-latest + build-type: none + compiler-family: gcc + c-compiler: gcc-9 + cc-compiler: g++-9 + debug: nodebug + coverage: nocoverage + - test-group: extra + os: ubuntu-latest + build-type: none + compiler-family: clang + c-compiler: clang-3.9 + cc-compiler: clang++-3.9 + debug: nodebug + coverage: nocoverage + - test-group: extra + os: ubuntu-latest + build-type: none + compiler-family: clang + c-compiler: clang-4.0 + cc-compiler: clang++-4.0 + debug: nodebug + coverage: nocoverage + - test-group: extra + os: ubuntu-latest + build-type: none + compiler-family: clang + c-compiler: clang-5.0 + cc-compiler: clang++-5.0 + debug: nodebug + coverage: nocoverage + - test-group: extra + os: ubuntu-latest + build-type: none + compiler-family: clang + c-compiler: clang-6.0 + cc-compiler: clang++-6.0 + debug: nodebug + coverage: nocoverage + - test-group: extra + os: ubuntu-latest + build-type: none + compiler-family: clang + c-compiler: clang-7 + cc-compiler: clang++-7 + debug: nodebug + coverage: nocoverage + - test-group: extra + os: ubuntu-latest + build-type: none + compiler-family: clang + c-compiler: clang-8 + cc-compiler: clang++-8 + debug: nodebug + coverage: nocoverage + - test-group: extra + os: ubuntu-latest + build-type: none + compiler-family: clang + c-compiler: clang-9 + cc-compiler: clang++-9 + debug: nodebug + coverage: nocoverage + - test-group: extra + os: ubuntu-latest + build-type: valgrind + compiler-family: gcc + c-compiler: gcc-7 + cc-compiler: g++-7 + debug: nodebug + coverage: nocoverage + - test-group: extra + os: ubuntu-latest + build-type: iwyu + compiler-family: clang + c-compiler: clang-9 + cc-compiler: clang++-9 + debug: nodebug + coverage: nocoverage + - test-group: performance + os: ubuntu-latest + build-type: select + compiler-family: gcc + c-compiler: gcc-7 + cc-compiler: g++-7 + debug: nodebug + coverage: nocoverage + - test-group: performance + os: ubuntu-latest + build-type: nodelay + compiler-family: gcc + c-compiler: gcc-7 + cc-compiler: g++-7 + debug: nodebug + coverage: nocoverage + - test-group: performance + os: ubuntu-latest + build-type: threads + compiler-family: gcc + c-compiler: gcc-7 + cc-compiler: g++-7 + debug: nodebug + coverage: nocoverage + steps: + - name: Checkout repository + uses: actions/checkout@v2 + with: + # We must fetch at least the immediate parents so that if this is + # a pull request then we can checkout the head. + fetch-depth: 2 + + # If this run was triggered by a pull request event, then checkout + # the head of the pull request instead of the merge commit. + - run: git checkout HEAD^2 + if: ${{ github.event_name == 'pull_request' }} + + - name: Install Ubuntu test sources (for extra builds) + run: | + sudo add-apt-repository ppa:ubuntu-toolchain-r/test ; + sudo apt-get update ; + if: ${{ matrix.test-group == 'extra' && matrix.os == 'ubuntu-latest' }} + + - name: Install apache benchmark if needed + run: sudo apt-get install apache2-utils ; + if: ${{ matrix.test-group == 'performance' && matrix.os == 'ubuntu-latest' }} + + - name: Install optional clang if needed + run: sudo apt-get install ${{ matrix.c-compiler }} + if: ${{ matrix.compiler-family == 'clang' && matrix.os == 'ubuntu-latest' }} + + - name: Install optional gcc if needed + run: sudo apt-get install ${{ matrix.cc-compiler }} + if: ${{ matrix.compiler-family == 'gcc' && matrix.os == 'ubuntu-latest' }} + + - name: Install valgrind if needed + run: sudo apt-get install valgrind valgrind-dbg + if: ${{ matrix.build-type == 'valgrind' && matrix.os == 'ubuntu-latest' }} + + - name: Install IWYU dependencies if needed + run: | + # Use same deps used by iwyu in their setup for travis + sudo apt-get install llvm-9-dev llvm-9-tools libclang-9-dev ; + # Use same CMAKE used by iwyu in their setup for travis + wget -O cmake.sh https://cmake.org/files/v3.10/cmake-3.10.0-Linux-x86_64.sh ; + sudo sh cmake.sh --skip-license --exclude-subdir --prefix=/usr/local ; + if: ${{ matrix.build-type == 'iwyu' && matrix.os == 'ubuntu-latest' }} + + - name: IWYU from cache (for testing) + id: cache-IWYU + uses: actions/cache@v2 + with: + path: include-what-you-use + key: ${{ matrix.os }}-include-what-you-use-pre-built + if: ${{ matrix.build-type == 'iwyu' && matrix.os == 'ubuntu-latest' }} + + # Installing iwyu manually because clang and iwyu paths won't match on Ubuntu otherwise. + - name: Build IWYU if requested + run: | + CLANG_ROOT_PATH=`llvm-config-9 --prefix` ; + CLANG_BIN_PATH=`llvm-config-9 --bindir` ; + curl "https://libhttpserver.s3.amazonaws.com/travis_stuff/include-what-you-use-clang-9.tgz" -o "include-what-you-use-clang-9.tgz" ; + tar -xzf "include-what-you-use-clang-9.tgz" ; + cd include-what-you-use ; + mkdir build_iwyu ; + cd build_iwyu ; + cmake -G "Unix Makefiles" -DCMAKE_PREFIX_PATH=$CLANG_ROOT_PATH -DCMAKE_C_COMPILER=$CLANG_BIN_PATH/clang -DCMAKE_CXX_COMPILER=$CLANG_BIN_PATH/clang++ ../ ; + make ; + sudo make install ; + if: ${{ matrix.build-type == 'iwyu' && matrix.os == 'ubuntu-latest' && steps.cache-IWYU.outputs.cache-hit != 'true' }} + + - name: Install IWYU if requested + run: | + cd include-what-you-use/build_iwyu ; + sudo make install ; + if: ${{ matrix.build-type == 'iwyu' && matrix.os == 'ubuntu-latest' }} + + - name: CURL from cache (for testing) + id: cache-CURL + uses: actions/cache@v2 + with: + path: curl-7.75.0 + key: ${{ matrix.os }}-CURL-pre-built + if: ${{ matrix.os == 'macos-latest' }} + + - name: Build CURL (for testing) + run: | + curl https://libhttpserver.s3.amazonaws.com/travis_stuff/curl-7.75.0.tar.gz -o curl-7.75.0.tar.gz ; + tar -xzf curl-7.75.0.tar.gz ; + cd curl-7.75.0 ; + if [ "$matrix.os" = "ubuntu-latest" ]; then ./configure ; else ./configure --with-darwinssl ; fi + make ; + if: ${{ matrix.os == 'macos-latest' && steps.cache-CURL.outputs.cache-hit != 'true' }} + + - name: Install CURL (for testing on mac only) + run: cd curl-7.75.0 ; sudo make install ; + if: ${{ matrix.os == 'macos-latest' }} + + - name: Install CURL (for testing on linux) + run: sudo apt-get install libcurl4-openssl-dev + if: ${{ matrix.os == 'ubuntu-latest' }} + + - name: Install autotools on mac + run: brew install autoconf automake libtool + if: ${{ matrix.os == 'macos-latest' }} + + - name: Setup coverage dependencies (only on linux) + run: | + sudo apt-get install info install-info ; + sudo pip install codecov ; + sudo pip install gcovr ; + sudo apt-get install cppcheck ; + if: ${{ matrix.os == 'ubuntu-latest' }} + + - name: Fetch libmicrohttpd from cache + id: cache-libmicrohttpd + uses: actions/cache@v2 + with: + path: libmicrohttpd-0.9.59 + key: ${{ matrix.os }}-libmicrohttpd-pre-built + + - name: Build libmicrohttpd dependency (if not cached) + run: | + curl https://s3.amazonaws.com/libhttpserver/libmicrohttpd_releases/libmicrohttpd-0.9.59.tar.gz -o libmicrohttpd-0.9.59.tar.gz ; + tar -xzf libmicrohttpd-0.9.59.tar.gz ; + cd libmicrohttpd-0.9.59 ; + ./configure --disable-examples ; + make ; + if: steps.cache-libmicrohttpd.outputs.cache-hit != 'true' + + - name: Install libmicrohttpd + run: cd libmicrohttpd-0.9.59 ; sudo make install ; + + - name: Refresh links to shared libs + run: sudo ldconfig ; + if: ${{ matrix.os == 'ubuntu-latest' }} + + - name: Run libhttpserver configure + run: | + # Set memory check flags. They need to stay in step as env variables don't propagate across steps. + if [ "$BUILD_TYPE" = "asan" ]; then export CFLAGS='-fsanitize=address'; export CXXLAGS='-fsanitize=address'; export LDFLAGS='-fsanitize=address'; fi + if [ "$BUILD_TYPE" = "msan" ]; then export CFLAGS='-fsanitize=memory'; export CXXLAGS='-fsanitize=memory'; export LDFLAGS='-fsanitize=memory'; fi + if [ "$BUILD_TYPE" = "lsan" ]; then export CFLAGS='-fsanitize=leak'; export CXXLAGS='-fsanitize=leak'; export LDFLAGS='-fsanitize=leak'; fi + if [ "$BUILD_TYPE" = "tsan" ]; then export CFLAGS='-fsanitize=thread'; export CXXLAGS='-fsanitize=thread'; export LDFLAGS='-fsanitize=thread'; fi + if [ "$BUILD_TYPE" = "ubsan" ]; then export export CFLAGS='-fsanitize=undefined'; export CXXLAGS='-fsanitize=undefined'; export LDFLAGS='-fsanitize=undefined'; fi + + # Additional flags on mac. They need to stay in step as env variables don't propagate across steps. + if [ "$matrix.os" = "macos-latest" ]; then + export CFLAGS='-mtune=generic' ; + export IPV6_TESTS_ENABLED="true" ; + fi + + ./bootstrap ; + mkdir build ; + cd build ; + if [ "$LINKING" = "static" ]; then + ../configure --enable-static --disable-fastopen; + elif [ "$DEBUG" = "debug" ] && [ "$COVERAGE" = "coverage" ]; then + ../configure --enable-debug --enable-coverage --disable-shared --disable-fastopen; + elif [ "$DEBUG" = "debug" ]; then + ../configure --enable-debug --disable-shared --disable-fastopen; + elif [ "$BUILD_TYPE" = "valgrind" ]; then + ../configure --enable-debug --disable-fastopen --disable-valgrind-helgrind --disable-valgrind-drd --disable-valgrind-sgcheck; + elif [ "$BUILD_TYPE" = "iwyu" ]; then + ../configure --disable-examples; + else + ../configure --disable-fastopen; + fi + + # Make or run iwyu. If running iwyu, check for the result code to be 2 (IWYU always returns an error code, if it is 2, no corrections are necessary). + - name: Make or run IWYU + run: | + # IWYU always return an error code. If it returns "2" it indicates a success so we manage this within the function below. + function safe_make_iwyu() { + { + make -k CXX='/usr/local/bin/include-what-you-use -Xiwyu --mapping_file=${top_builddir}/../custom_iwyu.imp' CXXFLAGS="-std=c++11 -DHTTPSERVER_COMPILATION -D_REENTRANT $CXXFLAGS" ; + } || { + if [ $? -ne 2 ]; then + return 1; + else + return 0; + fi + } + + return 0; + } + + cd build ; + if [ "$BUILD_TYPE" = "iwyu" ]; then + safe_make_iwyu ; + else + make ; + fi + + - name: Run tests + run: | + cd build ; + make check; + if: ${{ matrix.build-type != 'iwyu' }} + + - name: Print tests results + run: | + cd build ; + cat test/test-suite.log ; + if: ${{ failure() && matrix.build-type != 'iwyu' }} + + - name: Run Valgrind checks + run: | + cd build ; + make check-valgrind ; + cat test/test-suite-memcheck.log ; + if: ${{ matrix.build-type == 'valgrind' }} + + - name: Run cppcheck + run: | + cd src/ ; + cppcheck --error-exitcode=1 . ; + if: ${{ matrix.os == 'ubuntu-latest' }} + + - name: Run performance tests (select) + run: | + cd build + cd examples + ./benchmark_select 8080 $(nproc) & + sleep 5 && ab -n 1000000 -c 100 127.0.0.1:8080/plaintext + if: ${{ matrix.build-type == 'select' }} + + - name: Run performance tests (nodelay) + run: | + cd build + cd examples + ./benchmark_nodelay 8080 $(nproc) & + sleep 5 && ab -n 1000000 -c 100 127.0.0.1:8080/plaintext + if: ${{ matrix.build-type == 'nodelay' }} + + - name: Run performance tests (threads) + run: | + cd build + cd examples + ./benchmark_threads 8080 & + sleep 5 && ab -n 1000000 -c 100 127.0.0.1:8080/plaintext + if: ${{ matrix.build-type == 'threads' }} + + - name: Push code coverage data + run: | + cd build ; + bash <(curl -s https://codecov.io/bash) ; + if: ${{ matrix.os == 'ubuntu-latest' && matrix.c-compiler == 'gcc' && matrix.debug == 'debug' && matrix.coverage == 'coverage' && success() }} diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 5aa66c2e..00000000 --- a/.travis.yml +++ /dev/null @@ -1,358 +0,0 @@ -language: cpp -os: - - linux - - osx -compiler: - - gcc - - clang -env: - - DEBUG="debug" COVERAGE="coverage" - - DEBUG="nodebug" COVERAGE="nocoverage" - - LINKING="static" -before_install: - - eval "${MATRIX_EVAL}" - # Installing iwyu manually because clang and iwyu paths won't match on Ubuntu otherwise. - - if [ "$IWYU" = "iwyu" ]; then - CLANG_VERSION=`clang --version | grep version | cut -f3 -d' ' | cut -f1 -d'-'` ; - CLANG_PKG_VERSION=`echo $CLANG_VERSION | cut -f1,2 -d'.'` - CLANG_PREFIX_PATH="/usr/local/clang-${CLANG_VERSION}/lib/clang/${CLANG_VERSION}" ; - CLANG_BIN_PATH="/usr/local/clang-${CLANG_VERSION}/bin" ; - git clone https://github.com/include-what-you-use/include-what-you-use.git ; - cd include-what-you-use ; - echo "$CLANG_PKG_VERSION" | grep '\.[0-9]$' ; - if [ $? -eq 0 ]; then - git checkout clang_${CLANG_PKG_VERSION} ; - else - git checkout clang_${CLANG_PKG_VERSION}.0 ; - fi; - cd .. ; - mkdir build_iwyu ; - cd build_iwyu ; - cmake -G "Unix Makefiles" -DCMAKE_PREFIX_PATH=$CLANG_PREFIX_PATH -DCMAKE_C_COMPILER=$CLANG_BIN_PATH/clang -DCMAKE_CXX_COMPILER=$CLANG_BIN_PATH/clang++ ../include-what-you-use ; - make ; - sudo make install ; - cd .. ; - fi - - export LDFLAGS="$LDFLAGS -L/usr/local/lib -L/usr/lib" - - export PATH=$PATH:/usr/local/lib - - export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib - - export DYLD_LIBRARY_PATH=$DYLD_LIBRARY_PATH:/usr/local/lib - - if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo apt-get install info install-info; fi - - if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo pip install codecov; fi - - if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo pip install gcovr; fi - - if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo apt-get install cppcheck; fi - - if [ "$TRAVIS_OS_NAME" = "osx" ]; then export CFLAGS='-mtune=generic'; fi - - if [ "$TRAVIS_OS_NAME" = "osx" ]; then export IPV6_TESTS_ENABLED="true"; fi - - curl https://s3.amazonaws.com/libhttpserver/libmicrohttpd_releases/libmicrohttpd-0.9.59.tar.gz -o libmicrohttpd-0.9.59.tar.gz - - tar -xzf libmicrohttpd-0.9.59.tar.gz - - cd libmicrohttpd-0.9.59 - - ./configure --disable-examples - - make - - sudo make install - - cd .. - - if [ "$BUILD_TYPE" = "asan" ]; then export CFLAGS='-fsanitize=address'; export CXXLAGS='-fsanitize=address'; export LDFLAGS='-fsanitize=address'; fi - - if [ "$BUILD_TYPE" = "msan" ]; then export CFLAGS='-fsanitize=memory'; export CXXLAGS='-fsanitize=memory'; export LDFLAGS='-fsanitize=memory'; fi - - if [ "$BUILD_TYPE" = "lsan" ]; then export CFLAGS='-fsanitize=leak'; export CXXLAGS='-fsanitize=leak'; export LDFLAGS='-fsanitize=leak'; fi - - if [ "$BUILD_TYPE" = "tsan" ]; then export CFLAGS='-fsanitize=thread'; export CXXLAGS='-fsanitize=thread'; export LDFLAGS='-fsanitize=thread'; fi - - if [ "$BUILD_TYPE" = "ubsan" ]; then export export CFLAGS='-fsanitize=undefined'; export CXXLAGS='-fsanitize=undefined'; export LDFLAGS='-fsanitize=undefined'; fi -install: - - ./bootstrap - - mkdir build - - cd build - - | - if [ "$LINKING" = "static" ]; then - ../configure --enable-static --disable-fastopen; - elif [ "$DEBUG" = "debug" ] && [ "$COVERAGE" = "coverage" ]; then - ../configure --enable-debug --enable-coverage --disable-shared --disable-fastopen; - elif [ "$DEBUG" = "debug" ]; then - ../configure --enable-debug --disable-shared --disable-fastopen; - elif [ "$VALGRIND" = "valgrind" ]; then - ../configure --enable-debug --disable-fastopen --disable-valgrind-helgrind --disable-valgrind-drd --disable-valgrind-sgcheck; - elif [ "$IWYU" = "iwyu" ]; then - ../configure --disable-examples; - else - ../configure --disable-fastopen; - fi - # Make or run iwyu. If running iwyu, check for the result code to be 2 (IWYU always returns an error code, if it is 2, no corrections are necessary). - - | - if [ "$IWYU" = "iwyu" ]; then - make -k CXX='/usr/local/bin/include-what-you-use -Xiwyu --mapping_file=${top_builddir}/../custom_iwyu.imp' CXXFLAGS="-isystem ${CLANG_PREFIX_PATH}/include -std=c++11 -DHTTPSERVER_COMPILATION -D_REENTRANT $CXXFLAGS" ; - if [ $? -ne 2 ]; then - return 1; - fi - else - make; - fi -script: - - if [ "$IWYU" != "iwyu" ]; then make check; cat test/test-suite.log; fi - - if [ "$VALGRIND" = "valgrind" ]; then make check-valgrind; fi; - - if [ "$VALGRIND" = "valgrind" ]; then cat test/test-suite-memcheck.log; fi; - - if [ "$TRAVIS_OS_NAME" = "linux" ]; then cd ../src/; cppcheck --error-exitcode=1 .; cd ../build; fi - - | - if [ "$PERFORMANCE" = "select" ]; then - cd examples - ./benchmark_select 8080 $(nproc) & - sleep 5 && ab -n 10000000 -c 100 localhost:8080/plaintext - fi - - | - if [ "$PERFORMANCE" = "nodelay" ]; then - cd examples - ./benchmark_nodelay 8080 $(nproc) & - sleep 5 && ab -n 10000000 -c 100 localhost:8080/plaintext - fi - - | - if [ "$PERFORMANCE" = "threads" ]; then - cd examples - ./benchmark_threads 8080 & - sleep 5 && ab -n 10000000 -c 100 localhost:8080/plaintext - fi -after_success: - - if [ "$DEBUG" = "debug" ] && [ "$COVERAGE" = "coverage" ] && [ "$TRAVIS_OS_NAME" = "linux" ]; then bash <(curl -s https://codecov.io/bash); fi -matrix: - exclude: - - compiler: clang - env: DEBUG="debug" COVERAGE="coverage" - include: - - os: linux - addons: - apt: - sources: - - ubuntu-toolchain-r-test - - llvm-toolchain-precise-3.8 - packages: - - clang-3.8 - env: MATRIX_EVAL="BUILD_TYPE=asan && CC=clang-3.8 && CXX=clang++-3.8 && DEBUG=debug && COVERAGE=nocoverage" - - os: linux - addons: - apt: - sources: - - ubuntu-toolchain-r-test - - llvm-toolchain-precise-3.8 - packages: - - clang-3.8 - env: MATRIX_EVAL="BUILD_TYPE=msan && CC=clang-3.8 && CXX=clang++-3.8 && DEBUG=debug && COVERAGE=nocoverage" - - os: linux - addons: - apt: - sources: - - ubuntu-toolchain-r-test - - llvm-toolchain-precise-3.8 - packages: - - clang-3.8 - env: MATRIX_EVAL="BUILD_TYPE=lsan && CC=clang-3.8 && CXX=clang++-3.8 && DEBUG=debug && COVERAGE=nocoverage" - - os: linux - addons: - apt: - sources: - - ubuntu-toolchain-r-test - - llvm-toolchain-precise-3.8 - packages: - - clang-3.8 - env: MATRIX_EVAL="BUILD_TYPE=tsan && CC=clang-3.8 && CXX=clang++-3.8 && DEBUG=debug && COVERAGE=nocoverage" - - os: linux - addons: - apt: - sources: - - ubuntu-toolchain-r-test - - llvm-toolchain-precise-3.8 - packages: - - clang-3.8 - env: MATRIX_EVAL="BUILD_TYPE=ubsan && CC=clang-3.8 && CXX=clang++-3.8 && DEBUG=debug && COVERAGE=nocoverage" - - os: linux - addons: - apt: - sources: - - ubuntu-toolchain-r-test - packages: - - g++-5 - env: - - MATRIX_EVAL="CC=gcc-5 && CXX=g++-5" - - os: linux - addons: - apt: - sources: - - ubuntu-toolchain-r-test - packages: - - g++-6 - env: - - MATRIX_EVAL="CC=gcc-6 && CXX=g++-6" - - os: linux - addons: - apt: - sources: - - ubuntu-toolchain-r-test - packages: - - g++-7 - env: - - MATRIX_EVAL="CC=gcc-7 && CXX=g++-7" - - os: linux - addons: - apt: - sources: - - ubuntu-toolchain-r-test - packages: - - g++-8 - env: - - MATRIX_EVAL="CC=gcc-8 && CXX=g++-8" - - os: linux - addons: - apt: - sources: - - ubuntu-toolchain-r-test - packages: - - g++-9 - env: - - MATRIX_EVAL="CC=gcc-9 && CXX=g++-9" - - os: linux - addons: - apt: - sources: - - ubuntu-toolchain-r-test - packages: - - g++-7 - - valgrind - - valgrind-dbg - env: - - MATRIX_EVAL="CC=gcc-7 && CXX=g++-7 && VALGRIND=valgrind" - - os: linux - addons: - apt: - sources: - - ubuntu-toolchain-r-test - packages: - - g++-7 - - apache2-utils - env: - - MATRIX_EVAL="CC=gcc-7 && CXX=g++-7 && PERFORMANCE=select" - - os: linux - addons: - apt: - sources: - - ubuntu-toolchain-r-test - packages: - - g++-7 - - apache2-utils - env: - - MATRIX_EVAL="CC=gcc-7 && CXX=g++-7 && PERFORMANCE=nodelay" - - os: linux - addons: - apt: - sources: - - ubuntu-toolchain-r-test - packages: - - g++-7 - - apache2-utils - env: - - MATRIX_EVAL="CC=gcc-7 && CXX=g++-7 && PERFORMANCE=threads" - # works on Precise and Trusty - - os: linux - addons: - apt: - sources: - - ubuntu-toolchain-r-test - - llvm-toolchain-precise-3.6 - packages: - - clang-3.6 - env: - - MATRIX_EVAL="CC=clang-3.6 && CXX=clang++-3.6" - # works on Precise and Trusty - - os: linux - addons: - apt: - sources: - - ubuntu-toolchain-r-test - - llvm-toolchain-precise-3.7 - packages: - - clang-3.7 - env: - - MATRIX_EVAL="CC=clang-3.7 && CXX=clang++-3.7" - # works on Precise and Trusty - - os: linux - addons: - apt: - sources: - - ubuntu-toolchain-r-test - - llvm-toolchain-precise-3.8 - packages: - - clang-3.8 - env: - - MATRIX_EVAL="CC=clang-3.8 && CXX=clang++-3.8" - # works on Trusty - - os: linux - addons: - apt: - sources: - - llvm-toolchain-trusty-3.9 - packages: - - clang-3.9 - env: - - MATRIX_EVAL="CC=clang-3.9 && CXX=clang++-3.9" - # works on Trusty - - os: linux - addons: - apt: - packages: - - clang-4.0 - env: - - MATRIX_EVAL="CC=clang-4.0 && CXX=clang++-4.0" - # works on Trusty - - os: linux - addons: - apt: - packages: - - clang-5.0 - env: - - MATRIX_EVAL="CC=clang-5.0 && CXX=clang++-5.0" - - os: linux - addons: - apt: - packages: - - clang-6.0 - env: - - MATRIX_EVAL="CC=clang-6.0 && CXX=clang++-6.0" - - os: linux - addons: - apt: - sources: - - llvm-toolchain-xenial-7 - - ubuntu-toolchain-r-test - packages: - - clang-7 - env: - - MATRIX_EVAL="CC=clang-7 && CXX=clang++-7" - - os: linux - addons: - apt: - sources: - - llvm-toolchain-xenial-8 - - ubuntu-toolchain-r-test - packages: - - clang-8 - env: - - MATRIX_EVAL="CC=clang-8 && CXX=clang++-8" - - os: linux - addons: - apt: - sources: - - llvm-toolchain-xenial-9 - - ubuntu-toolchain-r-test - - sourceline: 'deb https://apt.llvm.org/xenial/ llvm-toolchain-xenial-9 main' - key_url: 'https://apt.llvm.org/llvm-snapshot.gpg.key' - packages: - - clang-9 - env: - - MATRIX_EVAL="CC=clang-9 && CXX=clang++-9" - - os: linux - compiler: clang - addons: - apt: - sources: - - llvm-toolchain-xenial-7 - - ubuntu-toolchain-r-test - packages: - - iwyu - - cmake - - llvm-dev - - libclang-dev - env: - - MATRIX_EVAL="IWYU=iwyu" diff --git a/src/basic_auth_fail_response.cpp b/src/basic_auth_fail_response.cpp index 49d9f5a9..36ea59bd 100644 --- a/src/basic_auth_fail_response.cpp +++ b/src/basic_auth_fail_response.cpp @@ -20,6 +20,7 @@ #include "httpserver/basic_auth_fail_response.hpp" #include +#include struct MHD_Connection; struct MHD_Response; diff --git a/src/deferred_response.cpp b/src/deferred_response.cpp index f3d44527..916b1174 100644 --- a/src/deferred_response.cpp +++ b/src/deferred_response.cpp @@ -20,6 +20,7 @@ #include "httpserver/deferred_response.hpp" #include +#include struct MHD_Response; diff --git a/src/details/http_endpoint.cpp b/src/details/http_endpoint.cpp index 584fb507..5d65aa9e 100644 --- a/src/details/http_endpoint.cpp +++ b/src/details/http_endpoint.cpp @@ -19,13 +19,13 @@ */ #include +#include #include #include #include #include #include #include -#include #include #include "httpserver/details/http_endpoint.hpp" diff --git a/src/digest_auth_fail_response.cpp b/src/digest_auth_fail_response.cpp index ca95526d..f232eac2 100644 --- a/src/digest_auth_fail_response.cpp +++ b/src/digest_auth_fail_response.cpp @@ -20,6 +20,7 @@ #include "httpserver/digest_auth_fail_response.hpp" #include +#include struct MHD_Connection; struct MHD_Response; diff --git a/src/file_response.cpp b/src/file_response.cpp index 66d13a1c..64396f10 100644 --- a/src/file_response.cpp +++ b/src/file_response.cpp @@ -23,6 +23,7 @@ #include #include #include +#include struct MHD_Response; diff --git a/src/http_resource.cpp b/src/http_resource.cpp index 46bdb0de..67fb555b 100644 --- a/src/http_resource.cpp +++ b/src/http_resource.cpp @@ -20,6 +20,7 @@ #include "httpserver/http_resource.hpp" #include +#include #include "httpserver/string_response.hpp" namespace httpserver { class http_response; } diff --git a/src/http_utils.cpp b/src/http_utils.cpp index 53c2230f..7f1ea0a6 100644 --- a/src/http_utils.cpp +++ b/src/http_utils.cpp @@ -33,7 +33,6 @@ #include #include -#include #include #include #include diff --git a/src/string_response.cpp b/src/string_response.cpp index 75a557f8..dec3c66a 100644 --- a/src/string_response.cpp +++ b/src/string_response.cpp @@ -21,6 +21,7 @@ #include "httpserver/string_response.hpp" #include #include +#include struct MHD_Response; diff --git a/src/webserver.cpp b/src/webserver.cpp index 38542f2a..8de4a9e7 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -41,6 +41,9 @@ #include #include #include +#include +#include +#include #include #include #include From a0998419f0542d4eb93cda9704800e9cf962ddc9 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 28 Feb 2021 21:12:22 -0800 Subject: [PATCH 513/623] Removing travis badge and adding github actions --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index f3bdf4c1..bc4b06dd 100644 --- a/README.md +++ b/README.md @@ -9,8 +9,7 @@ Copyright (C) 2011-2019 Sebastiano Merlino. --> # The libhttpserver reference manual - -[![Build Status](https://api.travis-ci.com/etr/libhttpserver.svg?branch=master)](https://travis-ci.com/etr/libhttpserver) +![GA: Build Status](https://github.com/etr/libhttpserver/actions/workflows/verify-build.yml/badge.svg) [![Build status](https://ci.appveyor.com/api/projects/status/ktoy6ewkrf0q1hw6/branch/master?svg=true)](https://ci.appveyor.com/project/etr/libhttpserver/branch/master) [![codecov](https://codecov.io/gh/etr/libhttpserver/branch/master/graph/badge.svg)](https://codecov.io/gh/etr/libhttpserver) [![Codacy Badge](https://api.codacy.com/project/badge/Grade/5fa4bdc3815b4c10977f3badefedecd6)](https://www.codacy.com/app/etr/libhttpserver?utm_source=github.com&utm_medium=referral&utm_content=etr/libhttpserver&utm_campaign=Badge_Grade) From b7ae7507b5e89077d858221d2daa8db1f4344918 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Mon, 1 Mar 2021 21:53:58 -0800 Subject: [PATCH 514/623] Simplify management of libmicrohttpd dependency (#223) Removes compilation options to manage usage of select/poll/epoll. The code now makes use of MHD_USE_AUTO flag internally which automatically decides the best dispatch method. --- AUTHORS | 3 ++ ChangeLog | 27 ++++++----- README.md | 2 +- configure.ac | 89 ++--------------------------------- src/httpserver/http_utils.hpp | 20 +------- 5 files changed, 26 insertions(+), 115 deletions(-) diff --git a/AUTHORS b/AUTHORS index 8f314bd1..06796b4c 100644 --- a/AUTHORS +++ b/AUTHORS @@ -42,3 +42,6 @@ bcsgh (Github: https://github.com/bcsgh) - Management of large uploads Walter Landry Jagat + +- Simplification of microhttpd dependency management +Christian Grothoff diff --git a/ChangeLog b/ChangeLog index 3d801c40..b11cd6eb 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,18 +1,21 @@ +Thu Feb 25 20:27:12 2021 -0800 + Simplified dependency management for libmicrohttpd + Sat Nov 21 07:20:00 2020 -0800 - Added support on build for CodeQL security checks. - Moved builds to travis.com - Added IWYU checks as part of build and cleaned-up accordingly. - Introduced dual-stack support. - Added OS specific tips, and cleaned up some compiler warnings. - Updates to readme and documentation. - Slight performances improvement by allowing to skip a copy in - string_response constructor. - Moved windows builds to AppVeyor. - Made the library compatible with libmicrohttpd v0.9.71 and above. + Added support on build for CodeQL security checks. + Moved builds to travis.com + Added IWYU checks as part of build and cleaned-up accordingly. + Introduced dual-stack support. + Added OS specific tips, and cleaned up some compiler warnings. + Updates to readme and documentation. + Slight performances improvement by allowing to skip a copy in + string_response constructor. + Moved windows builds to AppVeyor. + Made the library compatible with libmicrohttpd v0.9.71 and above. Sat Jun 6 10:21:05 2020 -0800 - Prevent use of regex in http_endpoint outside of registration which could - allow DOS attacks. + Prevent use of regex in http_endpoint outside of registration which could + allow DOS attacks. Sat May 16 07:20:00 2020 -0800 General performance improvements (reduced use of regex, lazy-building of diff --git a/README.md b/README.md index bc4b06dd..6830e75a 100644 --- a/README.md +++ b/README.md @@ -79,7 +79,7 @@ libhttpserver can be used without any dependencies aside from libmicrohttpd. The minimum versions required are: * g++ >= 5.5.0 or clang-3.6 -* libmicrohttpd >= 0.9.52 +* libmicrohttpd >= 0.9.53 * [Optionally]: for TLS (HTTPS) support, you'll need [libgnutls](http://www.gnutls.org/). * [Optionally]: to compile the code-reference, you'll need [doxygen](http://www.doxygen.nl/). diff --git a/configure.ac b/configure.ac index 8fc0e314..1bdba3fd 100644 --- a/configure.ac +++ b/configure.ac @@ -22,7 +22,7 @@ AC_PREREQ(2.57) m4_define([libhttpserver_MAJOR_VERSION],[0])dnl m4_define([libhttpserver_MINOR_VERSION],[18])dnl -m4_define([libhttpserver_REVISION],[1])dnl +m4_define([libhttpserver_REVISION],[2])dnl m4_define([libhttpserver_PKG_VERSION],[libhttpserver_MAJOR_VERSION.libhttpserver_MINOR_VERSION.libhttpserver_REVISION])dnl m4_define([libhttpserver_LDF_VERSION],[libhttpserver_MAJOR_VERSION:libhttpserver_MINOR_VERSION:libhttpserver_REVISION])dnl AC_INIT([libhttpserver], libhttpserver_PKG_VERSION, [electrictwister2000@gmail.com]) @@ -97,17 +97,17 @@ AC_CHECK_HEADER([gnutls/gnutls.h],[have_gnutls="yes"],[AC_MSG_WARN("gnutls/gnutl if test x"$host" = x"$build"; then AC_CHECK_HEADER([microhttpd.h], AC_CHECK_LIB([microhttpd], [MHD_get_fdset2], - [AC_MSG_CHECKING([for libmicrohttpd >= 0.9.52]) + [AC_MSG_CHECKING([for libmicrohttpd >= 0.9.53]) AC_COMPILE_IFELSE( [AC_LANG_SOURCE([ #include - #if (MHD_VERSION < 0x00095102) - #error needs at least version 0.9.52 + #if (MHD_VERSION < 0x00095300) + #error needs at least version 0.9.53 #endif int main () { return 0; } ])], [], - [AC_MSG_ERROR("libmicrohttpd is too old - install libmicrohttpd >= 0.9.52")] + [AC_MSG_ERROR("libmicrohttpd is too old - install libmicrohttpd >= 0.9.53")] ) ], [AC_MSG_ERROR(["libmicrohttpd not found"])] @@ -157,83 +157,6 @@ if test x"$fastopen" = x"yes"; then fi fi -AC_ARG_ENABLE([[poll]], - [AS_HELP_STRING([[--enable-poll[=ARG]]], [enable poll support (yes, no, auto) [auto]])], - [enable_poll=${enableval}], - [enable_poll='auto'] - ) - -if test "$enable_poll" != "no"; then - if test "$os_is_native_w32" != "yes"; then - AC_CHECK_HEADERS([poll.h], - [ - AC_CHECK_FUNCS([poll], [have_poll='yes'], [have_poll='no']) - ], [], [AC_INCLUDES_DEFAULT]) - else - AC_MSG_CHECKING([for WSAPoll()]) - AC_LINK_IFELSE([ - AC_LANG_PROGRAM([[ -#include - ]], [[ -WSAPOLLFD fda[2]; -WSAPoll(fda, 2, 0);]])], - [ - have_poll='yes' - AC_DEFINE([HAVE_POLL],[1]) - ], [have_poll='no']) - AC_MSG_RESULT([$have_poll]) - fi - if test "$enable_poll" = "yes" && test "$have_poll" != "yes"; then - AC_MSG_ERROR([[Support for poll was explicitly requested but cannot be enabled on this platform.]]) - fi - enable_poll="$have_poll" -fi - -if test x"$enable_poll" = x"yes"; then - AM_CXXFLAGS="$AM_CXXFLAGS -DENABLE_POLL" - AM_CFLAGS="$AM_CXXFLAGS -DENABLE_POLL" -fi - -AC_ARG_ENABLE([[epoll]], - [AS_HELP_STRING([[--enable-epoll[=ARG]]], [enable epoll support (yes, no, auto) [auto]])], - [enable_epoll=${enableval}], - [enable_epoll='auto'] - ) - -if test "$enable_epoll" != "no"; then - AX_HAVE_EPOLL - if test "${ax_cv_have_epoll}" = "yes"; then - AC_DEFINE([[EPOLL_SUPPORT]],[[1]],[Define to 1 to enable epoll support]) - enable_epoll='yes' - else - if test "$enable_epoll" = "yes"; then - AC_MSG_ERROR([[Support for epoll was explicitly requested but cannot be enabled on this platform.]]) - fi - enable_epoll='no' - fi -fi - -AM_CONDITIONAL([MHD_HAVE_EPOLL], [[test "x$enable_epoll" = xyes]]) - -if test "x$enable_epoll" = "xyes"; then - AC_CACHE_CHECK([for epoll_create1()], [mhd_cv_have_epoll_create1], [ - AC_LINK_IFELSE([ - AC_LANG_PROGRAM([[ -#include - ]], [[ -int fd; -fd = epoll_create1(EPOLL_CLOEXEC);]])], - [mhd_cv_have_epoll_create1=yes], - [mhd_cv_have_epoll_create1=no])]) - AS_IF([test "x$mhd_cv_have_epoll_create1" = "xyes"],[ - AC_DEFINE([[HAVE_EPOLL_CREATE1]], [[1]], [Define if you have epoll_create1 function.])]) -fi - -if test x"$enable_epoll" = x"yes"; then - AM_CXXFLAGS="$AM_CXXFLAGS -DENABLE_EPOLL" - AM_CFLAGS="$AM_CXXFLAGS -DENABLE_EPOLL" -fi - AC_MSG_CHECKING([whether to link statically]) AC_ARG_ENABLE([static], [AS_HELP_STRING([--enable-static], @@ -371,8 +294,6 @@ AC_MSG_NOTICE([Configuration Summary: Debug : ${debugit} TLS Enabled : ${have_gnutls} TCP_FASTOPEN : ${is_fastopen_supported} - poll support : ${enable_poll=no} - epoll support : ${enable_epoll=no} Static : ${static} Build examples : ${enable_examples} ]) diff --git a/src/httpserver/http_utils.hpp b/src/httpserver/http_utils.hpp index e2aa0339..338b3f70 100644 --- a/src/httpserver/http_utils.hpp +++ b/src/httpserver/http_utils.hpp @@ -86,24 +86,8 @@ class http_utils enum start_method_T { -#if defined(__MINGW32__) || defined(__CYGWIN__) - #ifdef ENABLE_POLL - INTERNAL_SELECT = MHD_USE_SELECT_INTERNALLY | MHD_USE_POLL, - #else - INTERNAL_SELECT = MHD_USE_SELECT_INTERNALLY, - #endif -#else - #ifdef ENABLE_EPOLL - INTERNAL_SELECT = MHD_USE_SELECT_INTERNALLY | MHD_USE_EPOLL | MHD_USE_EPOLL_TURBO, - #else - INTERNAL_SELECT = MHD_USE_SELECT_INTERNALLY, - #endif -#endif -#ifdef ENABLE_POLL - THREAD_PER_CONNECTION = MHD_USE_THREAD_PER_CONNECTION | MHD_USE_POLL -#else - THREAD_PER_CONNECTION = MHD_USE_THREAD_PER_CONNECTION -#endif + INTERNAL_SELECT = MHD_USE_SELECT_INTERNALLY | MHD_USE_AUTO, + THREAD_PER_CONNECTION = MHD_USE_THREAD_PER_CONNECTION | MHD_USE_AUTO }; enum policy_T From 6d1df063501c9a7b75d68958fa3105a154a75cf0 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 7 Mar 2021 21:15:38 -0800 Subject: [PATCH 515/623] Add checks using cpplint (#209) This also enables extra warnings and starts treating all warnings as errors --- .github/workflows/verify-build.yml | 16 + CPPLINT.cfg | 4 + ChangeLog | 4 + configure.ac | 9 +- examples/allowing_disallowing_methods.cpp | 16 +- examples/basic_authentication.cpp | 26 +- examples/benchmark_nodelay.cpp | 54 +- examples/benchmark_select.cpp | 54 +- examples/benchmark_threads.cpp | 54 +- examples/custom_access_log.cpp | 16 +- examples/custom_error.cpp | 26 +- examples/deferred_with_accumulator.cpp | 26 +- examples/digest_authentication.cpp | 31 +- examples/handlers.cpp | 24 +- examples/hello_with_get_arg.cpp | 16 +- examples/hello_world.cpp | 46 +- examples/minimal_deferred.cpp | 25 +- examples/minimal_file_response.cpp | 18 +- examples/minimal_hello_world.cpp | 16 +- examples/minimal_https.cpp | 16 +- examples/minimal_ip_ban.cpp | 17 +- examples/service.cpp | 166 ++--- examples/setting_headers.cpp | 20 +- examples/url_registration.cpp | 36 +- src/basic_auth_fail_response.cpp | 18 +- src/deferred_response.cpp | 15 +- src/details/http_endpoint.cpp | 90 +-- src/digest_auth_fail_response.cpp | 20 +- src/file_response.cpp | 23 +- src/http_request.cpp | 206 ++---- src/http_resource.cpp | 41 +- src/http_response.cpp | 64 +- src/http_utils.cpp | 513 ++++++-------- src/httpserver.hpp | 6 +- src/httpserver/basic_auth_fail_response.hpp | 43 +- src/httpserver/create_webserver.hpp | 589 ++++++++-------- src/httpserver/deferred_response.hpp | 82 ++- src/httpserver/details/http_endpoint.hpp | 322 +++++---- src/httpserver/details/modded_request.hpp | 36 +- src/httpserver/digest_auth_fail_response.hpp | 74 +-- src/httpserver/file_response.hpp | 49 +- src/httpserver/http_request.hpp | 589 ++++++++-------- src/httpserver/http_resource.hpp | 352 +++++----- src/httpserver/http_response.hpp | 221 +++--- src/httpserver/http_utils.hpp | 468 +++++++------ src/httpserver/string_response.hpp | 49 +- src/httpserver/string_utilities.hpp | 24 +- src/httpserver/webserver.hpp | 345 +++++----- src/string_response.cpp | 17 +- src/string_utilities.cpp | 50 +- src/webserver.cpp | 665 ++++++++----------- test/CPPLINT.cfg | 1 + test/integ/authentication.cpp | 86 ++- test/integ/ban_system.cpp | 37 +- test/integ/basic.cpp | 497 +++++++------- test/integ/deferred.cpp | 87 ++- test/integ/nodelay.cpp | 30 +- test/integ/threaded.cpp | 32 +- test/integ/ws_start_stop.cpp | 124 ++-- test/littletest.hpp | 8 +- test/test_content_empty | 0 test/unit/http_endpoint_test.cpp | 12 +- test/unit/http_utils_test.cpp | 267 ++++---- test/unit/string_utilities_test.cpp | 36 +- 64 files changed, 3177 insertions(+), 3747 deletions(-) create mode 100644 CPPLINT.cfg create mode 100644 test/CPPLINT.cfg create mode 100755 test/test_content_empty diff --git a/.github/workflows/verify-build.yml b/.github/workflows/verify-build.yml index 09464e0d..eafc6ca8 100644 --- a/.github/workflows/verify-build.yml +++ b/.github/workflows/verify-build.yml @@ -224,6 +224,14 @@ jobs: cc-compiler: g++-7 debug: nodebug coverage: nocoverage + - test-group: extra + os: ubuntu-latest + build-type: lint + compiler-family: gcc + c-compiler: gcc-7 + cc-compiler: g++-7 + debug: debug + coverage: nocoverage steps: - name: Checkout repository uses: actions/checkout@v2 @@ -258,6 +266,10 @@ jobs: - name: Install valgrind if needed run: sudo apt-get install valgrind valgrind-dbg if: ${{ matrix.build-type == 'valgrind' && matrix.os == 'ubuntu-latest' }} + + - name: Install cpplint if needed + run: sudo pip3 install cpplint ; + if: ${{ matrix.build-type == 'lint' && matrix.os == 'ubuntu-latest' }} - name: Install IWYU dependencies if needed run: | @@ -357,6 +369,10 @@ jobs: run: sudo ldconfig ; if: ${{ matrix.os == 'ubuntu-latest' }} + - name: Run cpplint on code + run: cpplint --extensions=cpp,hpp --headers=hpp --recursive . ; + if: ${{ matrix.build-type == 'lint' && matrix.os == 'ubuntu-latest' }} + - name: Run libhttpserver configure run: | # Set memory check flags. They need to stay in step as env variables don't propagate across steps. diff --git a/CPPLINT.cfg b/CPPLINT.cfg new file mode 100644 index 00000000..9add2a08 --- /dev/null +++ b/CPPLINT.cfg @@ -0,0 +1,4 @@ +linelength=200 +headers=hpp +extensions=cpp,hpp +filter=-test/littletest.hpp diff --git a/ChangeLog b/ChangeLog index b11cd6eb..3b40a2c5 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +Sun Mar 07 20:02:10 2021 -0800 + Cleaned code to support cpplint and extra warnings. + Use pointers in place of non-const references. + Thu Feb 25 20:27:12 2021 -0800 Simplified dependency management for libmicrohttpd diff --git a/configure.ac b/configure.ac index 1bdba3fd..26a1dc5b 100644 --- a/configure.ac +++ b/configure.ac @@ -21,8 +21,8 @@ AC_PREREQ(2.57) m4_define([libhttpserver_MAJOR_VERSION],[0])dnl -m4_define([libhttpserver_MINOR_VERSION],[18])dnl -m4_define([libhttpserver_REVISION],[2])dnl +m4_define([libhttpserver_MINOR_VERSION],[19])dnl +m4_define([libhttpserver_REVISION],[0])dnl m4_define([libhttpserver_PKG_VERSION],[libhttpserver_MAJOR_VERSION.libhttpserver_MINOR_VERSION.libhttpserver_REVISION])dnl m4_define([libhttpserver_LDF_VERSION],[libhttpserver_MAJOR_VERSION:libhttpserver_MINOR_VERSION:libhttpserver_REVISION])dnl AC_INIT([libhttpserver], libhttpserver_PKG_VERSION, [electrictwister2000@gmail.com]) @@ -194,8 +194,8 @@ AM_LDFLAGS="-lstdc++" if test x"$debugit" = x"yes"; then AC_DEFINE([DEBUG],[],[Debug Mode]) - AM_CXXFLAGS="$AM_CXXFLAGS -DDEBUG -g -Wall -Wno-uninitialized -O0" - AM_CFLAGS="$AM_CXXFLAGS -DDEBUG -g -Wall -Wno-uninitialized -O0" + AM_CXXFLAGS="$AM_CXXFLAGS -DDEBUG -g -Wall -Wextra -Werror -pedantic -std=c++14 -Wno-unused-command-line-argument -O0" + AM_CFLAGS="$AM_CXXFLAGS -DDEBUG -g -Wall -Wextra -Werror -pedantic -Wno-unused-command-line-argument -O0" else AC_DEFINE([NDEBUG],[],[No-debug Mode]) AM_CXXFLAGS="$AM_CXXFLAGS -O3" @@ -270,6 +270,7 @@ AC_SUBST(EXT_LIB_PATH) AC_SUBST(EXT_LIBS) AC_CONFIG_FILES([test/test_content:test/test_content]) +AC_CONFIG_FILES([test/test_content_empty:test/test_content_empty]) AC_CONFIG_FILES([test/cert.pem:test/cert.pem]) AC_CONFIG_FILES([test/key.pem:test/key.pem]) AC_CONFIG_FILES([test/test_root_ca.pem:test/test_root_ca.pem]) diff --git a/examples/allowing_disallowing_methods.cpp b/examples/allowing_disallowing_methods.cpp index 69295af4..a0578f5e 100644 --- a/examples/allowing_disallowing_methods.cpp +++ b/examples/allowing_disallowing_methods.cpp @@ -20,17 +20,15 @@ #include -using namespace httpserver; - -class hello_world_resource : public http_resource { -public: - const std::shared_ptr render(const http_request&) { - return std::shared_ptr(new string_response("Hello, World!")); - } +class hello_world_resource : public httpserver::http_resource { + public: + const std::shared_ptr render(const httpserver::http_request&) { + return std::shared_ptr(new httpserver::string_response("Hello, World!")); + } }; -int main(int argc, char** argv) { - webserver ws = create_webserver(8080); +int main() { + httpserver::webserver ws = httpserver::create_webserver(8080); hello_world_resource hwr; hwr.disallow_all(); diff --git a/examples/basic_authentication.cpp b/examples/basic_authentication.cpp index 84de823e..cc90f55b 100644 --- a/examples/basic_authentication.cpp +++ b/examples/basic_authentication.cpp @@ -20,23 +20,19 @@ #include -using namespace httpserver; - -class user_pass_resource : public httpserver::http_resource -{ - public: - const std::shared_ptr render_GET(const http_request& req) - { - if (req.get_user() != "myuser" || req.get_pass() != "mypass") - { - return std::shared_ptr(new basic_auth_fail_response("FAIL", "test@example.com")); - } - return std::shared_ptr(new string_response(req.get_user() + " " + req.get_pass(), 200, "text/plain")); - } +class user_pass_resource : public httpserver::http_resource { + public: + const std::shared_ptr render_GET(const httpserver::http_request& req) { + if (req.get_user() != "myuser" || req.get_pass() != "mypass") { + return std::shared_ptr(new httpserver::basic_auth_fail_response("FAIL", "test@example.com")); + } + + return std::shared_ptr(new httpserver::string_response(req.get_user() + " " + req.get_pass(), 200, "text/plain")); + } }; -int main(int argc, char** argv) { - webserver ws = create_webserver(8080); +int main() { + httpserver::webserver ws = httpserver::create_webserver(8080); user_pass_resource hwr; ws.register_resource("/hello", &hwr); diff --git a/examples/benchmark_nodelay.cpp b/examples/benchmark_nodelay.cpp index 418f9f1c..c1a6c1e7 100755 --- a/examples/benchmark_nodelay.cpp +++ b/examples/benchmark_nodelay.cpp @@ -1,3 +1,23 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + #include #include @@ -6,31 +26,29 @@ #define PATH "/plaintext" #define BODY "Hello, World!" -using namespace httpserver; - -class hello_world_resource : public http_resource { - public: - hello_world_resource(const std::shared_ptr& resp): - resp(resp) - { - } +class hello_world_resource : public httpserver::http_resource { + public: + explicit hello_world_resource(const std::shared_ptr& resp): + resp(resp) { + } - const std::shared_ptr render(const http_request&) { - return resp; - } + const std::shared_ptr render(const httpserver::http_request&) { + return resp; + } - private: - std::shared_ptr resp; + private: + std::shared_ptr resp; }; -int main(int argc, char** argv) -{ - webserver ws = create_webserver(atoi(argv[1])) - .start_method(http::http_utils::INTERNAL_SELECT) +int main(int argc, char** argv) { + std::ignore = argc; + + httpserver::webserver ws = httpserver::create_webserver(atoi(argv[1])) + .start_method(httpserver::http::http_utils::INTERNAL_SELECT) .tcp_nodelay() .max_threads(atoi(argv[2])); - std::shared_ptr hello = std::shared_ptr(new string_response(BODY, 200)); + std::shared_ptr hello = std::shared_ptr(new httpserver::string_response(BODY, 200)); hello->with_header("Server", "libhttpserver"); hello_world_resource hwr(hello); diff --git a/examples/benchmark_select.cpp b/examples/benchmark_select.cpp index 62a18140..1edc1c00 100755 --- a/examples/benchmark_select.cpp +++ b/examples/benchmark_select.cpp @@ -1,3 +1,23 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + #include #include @@ -6,30 +26,28 @@ #define PATH "/plaintext" #define BODY "Hello, World!" -using namespace httpserver; - -class hello_world_resource : public http_resource { - public: - hello_world_resource(const std::shared_ptr& resp): - resp(resp) - { - } +class hello_world_resource : public httpserver::http_resource { + public: + explicit hello_world_resource(const std::shared_ptr& resp): + resp(resp) { + } - const std::shared_ptr render(const http_request&) { - return resp; - } + const std::shared_ptr render(const httpserver::http_request&) { + return resp; + } - private: - std::shared_ptr resp; + private: + std::shared_ptr resp; }; -int main(int argc, char** argv) -{ - webserver ws = create_webserver(atoi(argv[1])) - .start_method(http::http_utils::INTERNAL_SELECT) +int main(int argc, char** argv) { + std::ignore = argc; + + httpserver::webserver ws = httpserver::create_webserver(atoi(argv[1])) + .start_method(httpserver::http::http_utils::INTERNAL_SELECT) .max_threads(atoi(argv[2])); - std::shared_ptr hello = std::shared_ptr(new string_response(BODY, 200)); + std::shared_ptr hello = std::shared_ptr(new httpserver::string_response(BODY, 200)); hello->with_header("Server", "libhttpserver"); hello_world_resource hwr(hello); diff --git a/examples/benchmark_threads.cpp b/examples/benchmark_threads.cpp index 827b1c35..1afe4dfb 100755 --- a/examples/benchmark_threads.cpp +++ b/examples/benchmark_threads.cpp @@ -1,3 +1,23 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + #include #include @@ -6,29 +26,27 @@ #define PATH "/plaintext" #define BODY "Hello, World!" -using namespace httpserver; - -class hello_world_resource : public http_resource { - public: - hello_world_resource(const std::shared_ptr& resp): - resp(resp) - { - } +class hello_world_resource : public httpserver::http_resource { + public: + explicit hello_world_resource(const std::shared_ptr& resp): + resp(resp) { + } - const std::shared_ptr render(const http_request&) { - return resp; - } + const std::shared_ptr render(const httpserver::http_request&) { + return resp; + } - private: - std::shared_ptr resp; + private: + std::shared_ptr resp; }; -int main(int argc, char** argv) -{ - webserver ws = create_webserver(atoi(argv[1])) - .start_method(http::http_utils::THREAD_PER_CONNECTION); +int main(int argc, char** argv) { + std::ignore = argc; + + httpserver::webserver ws = httpserver::create_webserver(atoi(argv[1])) + .start_method(httpserver::http::http_utils::THREAD_PER_CONNECTION); - std::shared_ptr hello = std::shared_ptr(new string_response(BODY, 200)); + std::shared_ptr hello = std::shared_ptr(new httpserver::string_response(BODY, 200)); hello->with_header("Server", "libhttpserver"); hello_world_resource hwr(hello); diff --git a/examples/custom_access_log.cpp b/examples/custom_access_log.cpp index afbc1774..dd2e4aa8 100644 --- a/examples/custom_access_log.cpp +++ b/examples/custom_access_log.cpp @@ -22,21 +22,19 @@ #include -using namespace httpserver; - void custom_access_log(const std::string& url) { std::cout << "ACCESSING: " << url << std::endl; } -class hello_world_resource : public http_resource { -public: - const std::shared_ptr render(const http_request&) { - return std::shared_ptr(new string_response("Hello, World!")); - } +class hello_world_resource : public httpserver::http_resource { + public: + const std::shared_ptr render(const httpserver::http_request&) { + return std::shared_ptr(new httpserver::string_response("Hello, World!")); + } }; -int main(int argc, char** argv) { - webserver ws = create_webserver(8080) +int main() { + httpserver::webserver ws = httpserver::create_webserver(8080) .log_access(custom_access_log); hello_world_resource hwr; diff --git a/examples/custom_error.cpp b/examples/custom_error.cpp index 034f334c..8e720835 100644 --- a/examples/custom_error.cpp +++ b/examples/custom_error.cpp @@ -20,27 +20,23 @@ #include -using namespace httpserver; - -const std::shared_ptr not_found_custom(const http_request& req) -{ - return std::shared_ptr(new string_response("Not found custom", 404, "text/plain")); +const std::shared_ptr not_found_custom(const httpserver::http_request&) { + return std::shared_ptr(new httpserver::string_response("Not found custom", 404, "text/plain")); } -const std::shared_ptr not_allowed_custom(const http_request& req) -{ - return std::shared_ptr(new string_response("Not allowed custom", 405, "text/plain")); +const std::shared_ptr not_allowed_custom(const httpserver::http_request&) { + return std::shared_ptr(new httpserver::string_response("Not allowed custom", 405, "text/plain")); } -class hello_world_resource : public http_resource { -public: - const std::shared_ptr render(const http_request&) { - return std::shared_ptr(new string_response("Hello, World!")); - } +class hello_world_resource : public httpserver::http_resource { + public: + const std::shared_ptr render(const httpserver::http_request&) { + return std::shared_ptr(new httpserver::string_response("Hello, World!")); + } }; -int main(int argc, char** argv) { - webserver ws = create_webserver(8080) +int main() { + httpserver::webserver ws = httpserver::create_webserver(8080) .not_found_resource(not_found_custom) .method_not_allowed_resource(not_allowed_custom); diff --git a/examples/deferred_with_accumulator.cpp b/examples/deferred_with_accumulator.cpp index 06578415..a55e4cd8 100644 --- a/examples/deferred_with_accumulator.cpp +++ b/examples/deferred_with_accumulator.cpp @@ -19,16 +19,16 @@ */ #include -#include -#include +// cpplint errors on chrono and thread because they are replaced (in Chromium) by other google libraries. +// This is not an issue here. +#include // NOLINT [build/c++11] +#include // NOLINT [build/c++11] #include -using namespace httpserver; - std::atomic counter; -ssize_t test_callback (std::shared_ptr > closure_data, char* buf, size_t max) { +ssize_t test_callback(std::shared_ptr > closure_data, char* buf, size_t max) { int reqid; if (closure_data == nullptr) { reqid = -1; @@ -54,16 +54,16 @@ ssize_t test_callback (std::shared_ptr > closure_data, char* bu } } -class deferred_resource : public http_resource { - public: - const std::shared_ptr render_GET(const http_request& req) { - std::shared_ptr > closure_data(new std::atomic(counter++)); - return std::shared_ptr > >(new deferred_response >(test_callback, closure_data, "cycle callback response")); - } +class deferred_resource : public httpserver::http_resource { + public: + const std::shared_ptr render_GET(const httpserver::http_request&) { + std::shared_ptr > closure_data(new std::atomic(counter++)); + return std::shared_ptr > >(new httpserver::deferred_response >(test_callback, closure_data, "cycle callback response")); + } }; -int main(int argc, char** argv) { - webserver ws = create_webserver(8080); +int main() { + httpserver::webserver ws = httpserver::create_webserver(8080); deferred_resource hwr; ws.register_resource("/hello", &hwr); diff --git a/examples/digest_authentication.cpp b/examples/digest_authentication.cpp index 55f119a0..c919cf0e 100644 --- a/examples/digest_authentication.cpp +++ b/examples/digest_authentication.cpp @@ -22,26 +22,23 @@ #define MY_OPAQUE "11733b200778ce33060f31c9af70a870ba96ddd4" -using namespace httpserver; - class digest_resource : public httpserver::http_resource { -public: - const std::shared_ptr render_GET(const http_request& req) { - if (req.get_digested_user() == "") { - return std::shared_ptr(new digest_auth_fail_response("FAIL", "test@example.com", MY_OPAQUE, true)); - } - else { - bool reload_nonce = false; - if(!req.check_digest_auth("test@example.com", "mypass", 300, reload_nonce)) { - return std::shared_ptr(new digest_auth_fail_response("FAIL", "test@example.com", MY_OPAQUE, reload_nonce)); - } - } - return std::shared_ptr(new string_response("SUCCESS", 200, "text/plain")); - } + public: + const std::shared_ptr render_GET(const httpserver::http_request& req) { + if (req.get_digested_user() == "") { + return std::shared_ptr(new httpserver::digest_auth_fail_response("FAIL", "test@example.com", MY_OPAQUE, true)); + } else { + bool reload_nonce = false; + if (!req.check_digest_auth("test@example.com", "mypass", 300, &reload_nonce)) { + return std::shared_ptr(new httpserver::digest_auth_fail_response("FAIL", "test@example.com", MY_OPAQUE, reload_nonce)); + } + } + return std::shared_ptr(new httpserver::string_response("SUCCESS", 200, "text/plain")); + } }; -int main(int argc, char** argv) { - webserver ws = create_webserver(8080); +int main() { + httpserver::webserver ws = httpserver::create_webserver(8080); digest_resource hwr; ws.register_resource("/hello", &hwr); diff --git a/examples/handlers.cpp b/examples/handlers.cpp index 4ddad426..2fce8ff1 100644 --- a/examples/handlers.cpp +++ b/examples/handlers.cpp @@ -20,21 +20,19 @@ #include -using namespace httpserver; - -class hello_world_resource : public http_resource { -public: - const std::shared_ptr render_GET(const http_request&) { - return std::shared_ptr(new string_response("GET: Hello, World!")); - } - - const std::shared_ptr render(const http_request&) { - return std::shared_ptr(new string_response("OTHER: Hello, World!")); - } +class hello_world_resource : public httpserver::http_resource { + public: + const std::shared_ptr render_GET(const httpserver::http_request&) { + return std::shared_ptr(new httpserver::string_response("GET: Hello, World!")); + } + + const std::shared_ptr render(const httpserver::http_request&) { + return std::shared_ptr(new httpserver::string_response("OTHER: Hello, World!")); + } }; -int main(int argc, char** argv) { - webserver ws = create_webserver(8080); +int main() { + httpserver::webserver ws = httpserver::create_webserver(8080); hello_world_resource hwr; ws.register_resource("/hello", &hwr); diff --git a/examples/hello_with_get_arg.cpp b/examples/hello_with_get_arg.cpp index 07ae90c1..90e13025 100644 --- a/examples/hello_with_get_arg.cpp +++ b/examples/hello_with_get_arg.cpp @@ -20,17 +20,15 @@ #include -using namespace httpserver; - -class hello_world_resource : public http_resource { -public: - const std::shared_ptr render(const http_request& req) { - return std::shared_ptr(new string_response("Hello: " + req.get_arg("name"))); - } +class hello_world_resource : public httpserver::http_resource { + public: + const std::shared_ptr render(const httpserver::http_request& req) { + return std::shared_ptr(new httpserver::string_response("Hello: " + req.get_arg("name"))); + } }; -int main(int argc, char** argv) { - webserver ws = create_webserver(8080); +int main() { + httpserver::webserver ws = httpserver::create_webserver(8080); hello_world_resource hwr; ws.register_resource("/hello", &hwr); diff --git a/examples/hello_world.cpp b/examples/hello_world.cpp index 6f8b4aad..880c811a 100755 --- a/examples/hello_world.cpp +++ b/examples/hello_world.cpp @@ -22,46 +22,38 @@ #include -using namespace httpserver; - -class hello_world_resource : public http_resource { - public: - const std::shared_ptr render(const http_request&); - void set_some_data(const std::string &s) {data = s;} - std::string data; +class hello_world_resource : public httpserver::http_resource { + public: + const std::shared_ptr render(const httpserver::http_request&); + void set_some_data(const std::string &s) {data = s;} + std::string data; }; -//using the render method you are able to catch each type of request you receive -const std::shared_ptr hello_world_resource::render(const http_request& req) -{ - //it is possible to store data inside the resource object that can be altered - //through the requests +// Using the render method you are able to catch each type of request you receive +const std::shared_ptr hello_world_resource::render(const httpserver::http_request& req) { + // It is possible to store data inside the resource object that can be altered through the requests std::cout << "Data was: " << data << std::endl; std::string datapar = req.get_arg("data"); set_some_data(datapar == "" ? "no data passed!!!" : datapar); std::cout << "Now data is:" << data << std::endl; - //it is possible to send a response initializing an http_string_response - //that reads the content to send in response from a string. - return std::shared_ptr(new string_response("Hello World!!!", 200)); + // It is possible to send a response initializing an http_string_response that reads the content to send in response from a string. + return std::shared_ptr(new httpserver::string_response("Hello World!!!", 200)); } -int main() -{ - //it is possible to create a webserver passing a great number of parameters. - //In this case we are just passing the port and the number of thread running. - webserver ws = create_webserver(8080).start_method(http::http_utils::INTERNAL_SELECT).max_threads(5); +int main() { + // It is possible to create a webserver passing a great number of parameters. In this case we are just passing the port and the number of thread running. + httpserver::webserver ws = httpserver::create_webserver(8080).start_method(httpserver::http::http_utils::INTERNAL_SELECT).max_threads(5); hello_world_resource hwr; - //this way we are registering the hello_world_resource to answer for the endpoint - //"/hello". The requested method is called (if the request is a GET we call the render_GET - //method. In case that the specific render method is not implemented, the generic "render" - //method is called. + // This way we are registering the hello_world_resource to answer for the endpoint + // "/hello". The requested method is called (if the request is a GET we call the render_GET + // method. In case that the specific render method is not implemented, the generic "render" + // method is called. ws.register_resource("/hello", &hwr, true); - //This way we are putting the created webserver in listen. We pass true in order to have - //a blocking call; if we want the call to be non-blocking we can just pass false to the - //method. + // This way we are putting the created webserver in listen. We pass true in order to have + // a blocking call; if we want the call to be non-blocking we can just pass false to the method. ws.start(true); return 0; } diff --git a/examples/minimal_deferred.cpp b/examples/minimal_deferred.cpp index a7a3e51d..32880303 100644 --- a/examples/minimal_deferred.cpp +++ b/examples/minimal_deferred.cpp @@ -20,31 +20,30 @@ #include -using namespace httpserver; - static int counter = 0; -ssize_t test_callback (std::shared_ptr closure_data, char* buf, size_t max) { +ssize_t test_callback(std::shared_ptr closure_data, char* buf, size_t max) { + std::ignore = closure_data; + if (counter == 2) { return -1; - } - else { + } else { memset(buf, 0, max); - strcat(buf, " test "); + snprintf(buf, max, "%s", " test "); counter++; return std::string(buf).size(); } } -class deferred_resource : public http_resource { - public: - const std::shared_ptr render_GET(const http_request& req) { - return std::shared_ptr >(new deferred_response(test_callback, nullptr, "cycle callback response")); - } +class deferred_resource : public httpserver::http_resource { + public: + const std::shared_ptr render_GET(const httpserver::http_request&) { + return std::shared_ptr >(new httpserver::deferred_response(test_callback, nullptr, "cycle callback response")); + } }; -int main(int argc, char** argv) { - webserver ws = create_webserver(8080); +int main() { + httpserver::webserver ws = httpserver::create_webserver(8080); deferred_resource hwr; ws.register_resource("/hello", &hwr); diff --git a/examples/minimal_file_response.cpp b/examples/minimal_file_response.cpp index b82d2929..cc292281 100644 --- a/examples/minimal_file_response.cpp +++ b/examples/minimal_file_response.cpp @@ -20,19 +20,15 @@ #include -using namespace httpserver; - -class file_response_resource : public http_resource -{ - public: - const std::shared_ptr render_GET(const http_request& req) - { - return std::shared_ptr(new file_response("test_content", 200, "text/plain")); - } +class file_response_resource : public httpserver::http_resource { + public: + const std::shared_ptr render_GET(const httpserver::http_request&) { + return std::shared_ptr(new httpserver::file_response("test_content", 200, "text/plain")); + } }; -int main(int argc, char** argv) { - webserver ws = create_webserver(8080); +int main() { + httpserver::webserver ws = httpserver::create_webserver(8080); file_response_resource hwr; ws.register_resource("/hello", &hwr); diff --git a/examples/minimal_hello_world.cpp b/examples/minimal_hello_world.cpp index 76489a0e..c6dc22a5 100644 --- a/examples/minimal_hello_world.cpp +++ b/examples/minimal_hello_world.cpp @@ -20,17 +20,15 @@ #include -using namespace httpserver; - -class hello_world_resource : public http_resource { -public: - const std::shared_ptr render(const http_request&) { - return std::shared_ptr(new string_response("Hello, World!")); - } +class hello_world_resource : public httpserver::http_resource { + public: + const std::shared_ptr render(const httpserver::http_request&) { + return std::shared_ptr(new httpserver::string_response("Hello, World!")); + } }; -int main(int argc, char** argv) { - webserver ws = create_webserver(8080); +int main() { + httpserver::webserver ws = httpserver::create_webserver(8080); hello_world_resource hwr; ws.register_resource("/hello", &hwr); diff --git a/examples/minimal_https.cpp b/examples/minimal_https.cpp index def0452a..957f4427 100644 --- a/examples/minimal_https.cpp +++ b/examples/minimal_https.cpp @@ -20,17 +20,15 @@ #include -using namespace httpserver; - -class hello_world_resource : public http_resource { -public: - const std::shared_ptr render(const http_request&) { - return std::shared_ptr(new string_response("Hello, World!")); - } +class hello_world_resource : public httpserver::http_resource { + public: + const std::shared_ptr render(const httpserver::http_request&) { + return std::shared_ptr(new httpserver::string_response("Hello, World!")); + } }; -int main(int argc, char** argv) { - webserver ws = create_webserver(8080) +int main() { + httpserver::webserver ws = httpserver::create_webserver(8080) .use_ssl() .https_mem_key("key.pem") .https_mem_cert("cert.pem"); diff --git a/examples/minimal_ip_ban.cpp b/examples/minimal_ip_ban.cpp index ea0923e7..9d4f5ba6 100644 --- a/examples/minimal_ip_ban.cpp +++ b/examples/minimal_ip_ban.cpp @@ -20,18 +20,15 @@ #include -using namespace httpserver; - -class hello_world_resource : public http_resource { -public: - const std::shared_ptr render(const http_request&) { - return std::shared_ptr(new string_response("Hello, World!")); - } +class hello_world_resource : public httpserver::http_resource { + public: + const std::shared_ptr render(const httpserver::http_request&) { + return std::shared_ptr(new httpserver::string_response("Hello, World!")); + } }; -int main(int argc, char** argv) { - webserver ws = create_webserver(8080) - .default_policy(http::http_utils::REJECT); +int main() { + httpserver::webserver ws = httpserver::create_webserver(8080).default_policy(httpserver::http::http_utils::REJECT); ws.allow_ip("127.0.0.1"); diff --git a/examples/service.cpp b/examples/service.cpp index e20c9a11..8880d96a 100644 --- a/examples/service.cpp +++ b/examples/service.cpp @@ -25,189 +25,161 @@ #include -using namespace httpserver; - -bool verbose=false; - -class service_resource: public http_resource { -public: - service_resource(); - - ~service_resource(); - - const std::shared_ptr render_GET(const http_request &req); - const std::shared_ptr render_PUT(const http_request &req); - const std::shared_ptr render_POST(const http_request &req); - const std::shared_ptr render(const http_request &req); - const std::shared_ptr render_HEAD(const http_request &req); - const std::shared_ptr render_OPTIONS(const http_request &req); - const std::shared_ptr render_CONNECT(const http_request &req); - const std::shared_ptr render_DELETE(const http_request &req); - -private: - - +bool verbose = false; + +class service_resource: public httpserver::http_resource { + public: + service_resource(); + + ~service_resource(); + + const std::shared_ptr render_GET(const httpserver::http_request &req); + const std::shared_ptr render_PUT(const httpserver::http_request &req); + const std::shared_ptr render_POST(const httpserver::http_request &req); + const std::shared_ptr render(const httpserver::http_request &req); + const std::shared_ptr render_HEAD(const httpserver::http_request &req); + const std::shared_ptr render_OPTIONS(const httpserver::http_request &req); + const std::shared_ptr render_CONNECT(const httpserver::http_request &req); + const std::shared_ptr render_DELETE(const httpserver::http_request &req); }; -service_resource::service_resource() -{} +service_resource::service_resource() { } -service_resource::~service_resource() -{} +service_resource::~service_resource() { } -const std::shared_ptr -service_resource::render_GET(const http_request &req) -{ +const std::shared_ptr service_resource::render_GET(const httpserver::http_request &req) { std::cout << "service_resource::render_GET()" << std::endl; if (verbose) std::cout << req; - string_response* res = new string_response("GET response", 200); + httpserver::string_response* res = new httpserver::string_response("GET response", 200); if (verbose) std::cout << *res; - return std::shared_ptr(res); + return std::shared_ptr(res); } -const std::shared_ptr -service_resource::render_PUT(const http_request &req) -{ +const std::shared_ptr service_resource::render_PUT(const httpserver::http_request &req) { std::cout << "service_resource::render_PUT()" << std::endl; if (verbose) std::cout << req; - string_response* res = new string_response("PUT response", 200); + httpserver::string_response* res = new httpserver::string_response("PUT response", 200); if (verbose) std::cout << *res; - return std::shared_ptr(res); + return std::shared_ptr(res); } - -const std::shared_ptr -service_resource::render_POST(const http_request &req) -{ +const std::shared_ptr service_resource::render_POST(const httpserver::http_request &req) { std::cout << "service_resource::render_POST()" << std::endl; if (verbose) std::cout << req; - string_response* res = new string_response("POST response", 200); + httpserver::string_response* res = new httpserver::string_response("POST response", 200); if (verbose) std::cout << *res; - return std::shared_ptr(res); + return std::shared_ptr(res); } -const std::shared_ptr -service_resource::render(const http_request &req) -{ +const std::shared_ptr service_resource::render(const httpserver::http_request &req) { std::cout << "service_resource::render()" << std::endl; if (verbose) std::cout << req; - string_response* res = new string_response("generic response", 200); + httpserver::string_response* res = new httpserver::string_response("generic response", 200); if (verbose) std::cout << *res; - return std::shared_ptr(res); + return std::shared_ptr(res); } - -const std::shared_ptr -service_resource::render_HEAD(const http_request &req) -{ +const std::shared_ptr service_resource::render_HEAD(const httpserver::http_request &req) { std::cout << "service_resource::render_HEAD()" << std::endl; if (verbose) std::cout << req; - string_response* res = new string_response("HEAD response", 200); + httpserver::string_response* res = new httpserver::string_response("HEAD response", 200); if (verbose) std::cout << *res; - return std::shared_ptr(res); + return std::shared_ptr(res); } -const std::shared_ptr -service_resource::render_OPTIONS(const http_request &req) -{ +const std::shared_ptr service_resource::render_OPTIONS(const httpserver::http_request &req) { std::cout << "service_resource::render_OPTIONS()" << std::endl; if (verbose) std::cout << req; - string_response* res = new string_response("OPTIONS response", 200); + httpserver::string_response* res = new httpserver::string_response("OPTIONS response", 200); if (verbose) std::cout << *res; - return std::shared_ptr(res); + return std::shared_ptr(res); } -const std::shared_ptr -service_resource::render_CONNECT(const http_request &req) -{ +const std::shared_ptr service_resource::render_CONNECT(const httpserver::http_request &req) { std::cout << "service_resource::render_CONNECT()" << std::endl; if (verbose) std::cout << req; - string_response* res = new string_response("CONNECT response", 200); + httpserver::string_response* res = new httpserver::string_response("CONNECT response", 200); if (verbose) std::cout << *res; - return std::shared_ptr(res); + return std::shared_ptr(res); } -const std::shared_ptr -service_resource::render_DELETE(const http_request &req) -{ +const std::shared_ptr service_resource::render_DELETE(const httpserver::http_request &req) { std::cout << "service_resource::render_DELETE()" << std::endl; if (verbose) std::cout << req; - string_response* res = new string_response("DELETE response", 200); + httpserver::string_response* res = new httpserver::string_response("DELETE response", 200); if (verbose) std::cout << *res; - return std::shared_ptr(res); + return std::shared_ptr(res); } -void usage() -{ +void usage() { std::cout << "Usage:" << std::endl << "service [-p ][-s [-k ][-c ]][-v]" << std::endl; } -int main(int argc, char **argv) -{ - uint16_t port=8080; - int c; - const char *key="key.pem"; - const char *cert="cert.pem"; - bool secure=false; - - while ((c = getopt(argc,argv,"p:k:c:sv?")) != EOF) { - switch (c) { - case 'p': - port=strtoul(optarg,NULL,10); - break; +int main(int argc, char **argv) { + uint16_t port = 8080; + int c; + const char *key = "key.pem"; + const char *cert = "cert.pem"; + bool secure = false; + + while ((c = getopt(argc, argv, "p:k:c:sv?")) != EOF) { + switch (c) { + case 'p': + port = strtoul(optarg, NULL, 10); + break; case 'k': key = optarg; break; case 'c': - cert=optarg; + cert = optarg; break; case 's': - secure=true; + secure = true; break; case 'v': - verbose=true; + verbose = true; break; - default: + default: usage(); exit(1); - break; - } - } + break; + } + } - std::cout << "Using port " << port << std::endl; + std::cout << "Using port " << port << std::endl; if (secure) { std::cout << "Key: " << key << " Certificate: " << cert << std::endl; @@ -216,7 +188,7 @@ int main(int argc, char **argv) // // Use builder to define webserver configuration options // - create_webserver cw = create_webserver(port).max_threads(5); + httpserver::create_webserver cw = httpserver::create_webserver(port).max_threads(5); if (secure) { cw.use_ssl().https_mem_key(key).https_mem_cert(cert); @@ -225,18 +197,18 @@ int main(int argc, char **argv) // // Create webserver using the configured options // - webserver ws = cw; + httpserver::webserver ws = cw; // // Create and register service resource available at /service // - service_resource res; - ws.register_resource("/service",&res,true); + service_resource res; + ws.register_resource("/service", &res, true); // // Start and block the webserver // - ws.start(true); + ws.start(true); - return 0; + return 0; } diff --git a/examples/setting_headers.cpp b/examples/setting_headers.cpp index 0fb73c79..41f2498c 100644 --- a/examples/setting_headers.cpp +++ b/examples/setting_headers.cpp @@ -20,19 +20,17 @@ #include -using namespace httpserver; - -class hello_world_resource : public http_resource { -public: - const std::shared_ptr render(const http_request&) { - std::shared_ptr response = std::shared_ptr(new string_response("Hello, World!")); - response->with_header("MyHeader", "MyValue"); - return response; - } +class hello_world_resource : public httpserver::http_resource { + public: + const std::shared_ptr render(const httpserver::http_request&) { + std::shared_ptr response = std::shared_ptr(new httpserver::string_response("Hello, World!")); + response->with_header("MyHeader", "MyValue"); + return response; + } }; -int main(int argc, char** argv) { - webserver ws = create_webserver(8080); +int main() { + httpserver::webserver ws = httpserver::create_webserver(8080); hello_world_resource hwr; ws.register_resource("/hello", &hwr); diff --git a/examples/url_registration.cpp b/examples/url_registration.cpp index 0f8da6ad..34c3197b 100644 --- a/examples/url_registration.cpp +++ b/examples/url_registration.cpp @@ -20,31 +20,29 @@ #include -using namespace httpserver; - -class hello_world_resource : public http_resource { -public: - const std::shared_ptr render(const http_request&) { - return std::shared_ptr(new string_response("Hello, World!")); - } +class hello_world_resource : public httpserver::http_resource { + public: + const std::shared_ptr render(const httpserver::http_request&) { + return std::shared_ptr(new httpserver::string_response("Hello, World!")); + } }; -class handling_multiple_resource : public http_resource { -public: - const std::shared_ptr render(const http_request& req) { - return std::shared_ptr(new string_response("Your URL: " + req.get_path())); - } +class handling_multiple_resource : public httpserver::http_resource { + public: + const std::shared_ptr render(const httpserver::http_request& req) { + return std::shared_ptr(new httpserver::string_response("Your URL: " + req.get_path())); + } }; -class url_args_resource : public http_resource { -public: - const std::shared_ptr render(const http_request& req) { - return std::shared_ptr(new string_response("ARGS: " + req.get_arg("arg1") + " and " + req.get_arg("arg2"))); - } +class url_args_resource : public httpserver::http_resource { + public: + const std::shared_ptr render(const httpserver::http_request& req) { + return std::shared_ptr(new httpserver::string_response("ARGS: " + req.get_arg("arg1") + " and " + req.get_arg("arg2"))); + } }; -int main(int argc, char** argv) { - webserver ws = create_webserver(8080); +int main() { + httpserver::webserver ws = httpserver::create_webserver(8080); hello_world_resource hwr; ws.register_resource("/hello", &hwr); diff --git a/src/basic_auth_fail_response.cpp b/src/basic_auth_fail_response.cpp index 36ea59bd..0e00cdc1 100644 --- a/src/basic_auth_fail_response.cpp +++ b/src/basic_auth_fail_response.cpp @@ -25,18 +25,10 @@ struct MHD_Connection; struct MHD_Response; -using namespace std; - -namespace httpserver -{ - -int basic_auth_fail_response::enqueue_response(MHD_Connection* connection, MHD_Response* response) -{ - return MHD_queue_basic_auth_fail_response( - connection, - realm.c_str(), - response - ); -} +namespace httpserver { +int basic_auth_fail_response::enqueue_response(MHD_Connection* connection, MHD_Response* response) { + return MHD_queue_basic_auth_fail_response(connection, realm.c_str(), response); } + +} // namespace httpserver diff --git a/src/deferred_response.cpp b/src/deferred_response.cpp index 916b1174..fa42134f 100644 --- a/src/deferred_response.cpp +++ b/src/deferred_response.cpp @@ -24,19 +24,14 @@ struct MHD_Response; -using namespace std; +namespace httpserver { -namespace httpserver -{ +namespace details { -namespace details -{ - -MHD_Response* get_raw_response_helper(void* cls, ssize_t (*cb)(void*, uint64_t, char*, size_t)) -{ +MHD_Response* get_raw_response_helper(void* cls, ssize_t (*cb)(void*, uint64_t, char*, size_t)) { return MHD_create_response_from_callback(MHD_SIZE_UNKNOWN, 1024, cb, cls, NULL); } -} +} // namespace details -} +} // namespace httpserver diff --git a/src/details/http_endpoint.cpp b/src/details/http_endpoint.cpp index 5d65aa9e..3bccea6d 100644 --- a/src/details/http_endpoint.cpp +++ b/src/details/http_endpoint.cpp @@ -22,7 +22,8 @@ #include #include #include -#include +// Disabling lint error on regex (the only reason it errors is because the Chromium team prefers google/re2) +#include // NOLINT [build/c++11] #include #include #include @@ -31,32 +32,20 @@ #include "httpserver/details/http_endpoint.hpp" #include "httpserver/http_utils.hpp" -using namespace std; +using std::string; +using std::vector; -namespace httpserver -{ +namespace httpserver { -namespace details -{ +namespace details { -using namespace http; - -http_endpoint::~http_endpoint() -{ +http_endpoint::~http_endpoint() { } -http_endpoint::http_endpoint -( - const string& url, - bool family, - bool registration, - bool use_regex -): +http_endpoint::http_endpoint(const string& url, bool family, bool registration, bool use_regex): family_url(family), - reg_compiled(false) -{ - if (use_regex && !registration) - { + reg_compiled(false) { + if (use_regex && !registration) { throw std::invalid_argument("Cannot use regex if not during registration"); } @@ -69,24 +58,20 @@ http_endpoint::http_endpoint url_complete = url; #endif - if (url_complete[url_complete.size() - 1] == '/') - { + if (url_complete[url_complete.size() - 1] == '/') { url_complete = url_complete.substr(0, url_complete.size() - 1); } - if (url_complete[0] != '/') - { + if (url_complete[0] != '/') { url_complete = "/" + url_complete; } - parts = http_utils::tokenize_url(url); + parts = httpserver::http::http_utils::tokenize_url(url); string buffered; bool first = true; - for (unsigned int i = 0; i < parts.size(); i++) - { - if(!registration) - { + for (unsigned int i = 0; i < parts.size(); i++) { + if (!registration) { url_normalized += (first ? "" : "/") + parts[i]; first = false; @@ -95,15 +80,11 @@ http_endpoint::http_endpoint continue; } - if((parts[i] != "") && (parts[i][0] != '{')) - { - if(first) - { + if ((parts[i] != "") && (parts[i][0] != '{')) { + if (first) { url_normalized = (parts[i][0] == '^' ? "" : url_normalized) + parts[i]; first = false; - } - else - { + } else { url_normalized += "/" + parts[i]; } url_pieces.push_back(parts[i]); @@ -111,8 +92,9 @@ http_endpoint::http_endpoint continue; } - if((parts[i].size() < 3) || (parts[i][0] != '{') || (parts[i][parts[i].size() - 1] != '}')) + if ((parts[i].size() < 3) || (parts[i][0] != '{') || (parts[i][parts[i].size() - 1] != '}')) { throw std::invalid_argument("Bad URL format"); + } std::string::size_type bar = parts[i].find_first_of('|'); url_pars.push_back(parts[i].substr(1, bar != string::npos ? bar - 1 : parts[i].size() - 2)); @@ -125,15 +107,11 @@ http_endpoint::http_endpoint url_pieces.push_back(parts[i]); } - if(use_regex) - { + if (use_regex) { url_normalized += "$"; - try - { + try { re_url_normalized = std::regex(url_normalized, std::regex::extended | std::regex::icase | std::regex::nosubs); - } - catch (std::regex_error& e) - { + } catch (std::regex_error& e) { throw std::invalid_argument("Not a valid regex in URL: " + url_normalized); } reg_compiled = true; @@ -148,12 +126,10 @@ http_endpoint::http_endpoint(const http_endpoint& h): chunk_positions(h.chunk_positions), re_url_normalized(h.re_url_normalized), family_url(h.family_url), - reg_compiled(h.reg_compiled) -{ + reg_compiled(h.reg_compiled) { } -http_endpoint& http_endpoint::operator =(const http_endpoint& h) -{ +http_endpoint& http_endpoint::operator =(const http_endpoint& h) { url_complete = h.url_complete; url_normalized = h.url_normalized; family_url = h.family_url; @@ -165,30 +141,26 @@ http_endpoint& http_endpoint::operator =(const http_endpoint& h) return *this; } -bool http_endpoint::operator <(const http_endpoint& b) const -{ +bool http_endpoint::operator <(const http_endpoint& b) const { COMPARATOR(url_normalized, b.url_normalized, std::toupper); } -bool http_endpoint::match(const http_endpoint& url) const -{ +bool http_endpoint::match(const http_endpoint& url) const { if (!reg_compiled) throw std::invalid_argument("Cannot run match. Regex suppressed."); - if(!family_url || url.url_pieces.size() < url_pieces.size()) - { + if (!family_url || url.url_pieces.size() < url_pieces.size()) { return regex_match(url.url_complete, re_url_normalized); } string nn = "/"; bool first = true; - for(unsigned int i = 0; i < url_pieces.size(); i++) - { + for (unsigned int i = 0; i < url_pieces.size(); i++) { nn += (first ? "" : "/") + url.url_pieces[i]; first = false; } return regex_match(nn, re_url_normalized); } -}; +} // namespace details -}; +} // namespace httpserver diff --git a/src/digest_auth_fail_response.cpp b/src/digest_auth_fail_response.cpp index f232eac2..82749710 100644 --- a/src/digest_auth_fail_response.cpp +++ b/src/digest_auth_fail_response.cpp @@ -25,20 +25,10 @@ struct MHD_Connection; struct MHD_Response; -using namespace std; - -namespace httpserver -{ - -int digest_auth_fail_response::enqueue_response(MHD_Connection* connection, MHD_Response* response) -{ - return MHD_queue_auth_fail_response( - connection, - realm.c_str(), - opaque.c_str(), - response, - reload_nonce ? MHD_YES : MHD_NO - ); -} +namespace httpserver { +int digest_auth_fail_response::enqueue_response(MHD_Connection* connection, MHD_Response* response) { + return MHD_queue_auth_fail_response(connection, realm.c_str(), opaque.c_str(), response, reload_nonce ? MHD_YES : MHD_NO); } + +} // namespace httpserver diff --git a/src/file_response.cpp b/src/file_response.cpp index 64396f10..623c7036 100644 --- a/src/file_response.cpp +++ b/src/file_response.cpp @@ -27,27 +27,16 @@ struct MHD_Response; -using namespace std; +namespace httpserver { -namespace httpserver -{ - -MHD_Response* file_response::get_raw_response() -{ +MHD_Response* file_response::get_raw_response() { int fd = open(filename.c_str(), O_RDONLY); size_t size = lseek(fd, 0, SEEK_END); - if(size) - { + if (size) { return MHD_create_response_from_fd(size, fd); - } - else - { - return MHD_create_response_from_buffer( - 0, - (void*) "", - MHD_RESPMEM_PERSISTENT - ); + } else { + return MHD_create_response_from_buffer(0, 0x0, MHD_RESPMEM_PERSISTENT); } } -} +} // namespace httpserver diff --git a/src/http_request.cpp b/src/http_request.cpp index d7d8e2af..0ac718e4 100644 --- a/src/http_request.cpp +++ b/src/http_request.cpp @@ -26,138 +26,95 @@ #include "httpserver/http_utils.hpp" #include "httpserver/string_utilities.hpp" -using namespace std; +namespace httpserver { -namespace httpserver -{ +const char http_request::EMPTY[] = ""; -const std::string http_request::EMPTY = ""; - -struct arguments_accumulator -{ +struct arguments_accumulator { unescaper_ptr unescaper; std::map* arguments; }; -void http_request::set_method(const std::string& method) -{ +void http_request::set_method(const std::string& method) { this->method = string_utilities::to_upper_copy(method); } -bool http_request::check_digest_auth( - const std::string& realm, - const std::string& password, - int nonce_timeout, - bool& reload_nonce -) const -{ +bool http_request::check_digest_auth(const std::string& realm, const std::string& password, int nonce_timeout, bool* reload_nonce) const { std::string digested_user = get_digested_user(); - int val = MHD_digest_auth_check( - underlying_connection, - realm.c_str(), - digested_user.c_str(), - password.c_str(), - nonce_timeout - ); - - if(val == MHD_INVALID_NONCE) - { - reload_nonce = true; + int val = MHD_digest_auth_check(underlying_connection, realm.c_str(), digested_user.c_str(), password.c_str(), nonce_timeout); + + if (val == MHD_INVALID_NONCE) { + *reload_nonce = true; return false; - } - else if(val == MHD_NO) - { - reload_nonce = false; + } else if (val == MHD_NO) { + *reload_nonce = false; return false; } - reload_nonce = false; + *reload_nonce = false; return true; } -const std::string http_request::get_connection_value(const std::string& key, enum MHD_ValueKind kind) const -{ - const char* header_c = MHD_lookup_connection_value( - underlying_connection, - kind, - key.c_str() - ); +const std::string http_request::get_connection_value(const std::string& key, enum MHD_ValueKind kind) const { + const char* header_c = MHD_lookup_connection_value(underlying_connection, kind, key.c_str()); if (header_c == NULL) return EMPTY; return header_c; } -MHD_Result http_request::build_request_header( - void *cls, - enum MHD_ValueKind kind, - const char *key, - const char *value -) -{ +MHD_Result http_request::build_request_header(void *cls, enum MHD_ValueKind kind, const char *key, const char *value) { + // Parameters needed to respect MHD interface, but not used in the implementation. + std::ignore = kind; + std::map* dhr = static_cast*>(cls); (*dhr)[key] = value; return MHD_YES; } -const std::map http_request::get_headerlike_values(enum MHD_ValueKind kind) const -{ +const std::map http_request::get_headerlike_values(enum MHD_ValueKind kind) const { std::map headers; - MHD_get_connection_values( - underlying_connection, - kind, - &build_request_header, - (void*) &headers - ); + MHD_get_connection_values(underlying_connection, kind, &build_request_header, reinterpret_cast(&headers)); return headers; } -const std::string http_request::get_header(const std::string& key) const -{ +const std::string http_request::get_header(const std::string& key) const { return get_connection_value(key, MHD_HEADER_KIND); } -const std::map http_request::get_headers() const -{ +const std::map http_request::get_headers() const { return get_headerlike_values(MHD_HEADER_KIND); } -const std::string http_request::get_footer(const std::string& key) const -{ +const std::string http_request::get_footer(const std::string& key) const { return get_connection_value(key, MHD_FOOTER_KIND); } -const std::map http_request::get_footers() const -{ +const std::map http_request::get_footers() const { return get_headerlike_values(MHD_FOOTER_KIND); } -const std::string http_request::get_cookie(const std::string& key) const -{ +const std::string http_request::get_cookie(const std::string& key) const { return get_connection_value(key, MHD_COOKIE_KIND); } -const std::map http_request::get_cookies() const -{ +const std::map http_request::get_cookies() const { return get_headerlike_values(MHD_COOKIE_KIND); } -const std::string http_request::get_arg(const std::string& key) const -{ +const std::string http_request::get_arg(const std::string& key) const { std::map::const_iterator it = args.find(key); - if(it != args.end()) - { + if (it != args.end()) { return it->second; } return get_connection_value(key, MHD_GET_ARGUMENT_KIND); } -const std::map http_request::get_args() const -{ +const std::map http_request::get_args() const { std::map arguments; arguments.insert(args.begin(), args.end()); @@ -165,73 +122,54 @@ const std::map http_request::get aa.unescaper = unescaper; aa.arguments = &arguments; - MHD_get_connection_values( - underlying_connection, - MHD_GET_ARGUMENT_KIND, - &build_request_args, - (void*) &aa - ); + MHD_get_connection_values(underlying_connection, MHD_GET_ARGUMENT_KIND, &build_request_args, reinterpret_cast(&aa)); return arguments; } -const std::string http_request::get_querystring() const -{ +const std::string http_request::get_querystring() const { std::string querystring = ""; - MHD_get_connection_values( - underlying_connection, - MHD_GET_ARGUMENT_KIND, - &build_request_querystring, - (void*) &querystring - ); + MHD_get_connection_values(underlying_connection, MHD_GET_ARGUMENT_KIND, &build_request_querystring, reinterpret_cast(&querystring)); return querystring; } -MHD_Result http_request::build_request_args( - void *cls, - enum MHD_ValueKind kind, - const char *key, - const char *arg_value -) -{ +MHD_Result http_request::build_request_args(void *cls, enum MHD_ValueKind kind, const char *key, const char *arg_value) { + // Parameters needed to respect MHD interface, but not used in the implementation. + std::ignore = kind; + arguments_accumulator* aa = static_cast(cls); std::string value = ((arg_value == NULL) ? "" : arg_value); - http::base_unescaper(value, aa->unescaper); + http::base_unescaper(&value, aa->unescaper); (*aa->arguments)[key] = value; return MHD_YES; } -MHD_Result http_request::build_request_querystring( - void *cls, - enum MHD_ValueKind kind, - const char *key, - const char *arg_value -) -{ +MHD_Result http_request::build_request_querystring(void *cls, enum MHD_ValueKind kind, const char *key, const char *arg_value) { + // Parameters needed to respect MHD interface, but not used in the implementation. + std::ignore = kind; + std::string* querystring = static_cast(cls); std::string value = ((arg_value == NULL) ? "" : arg_value); - { - char buf[std::string(key).size() + value.size() + 3]; - if(*querystring == "") - { - snprintf(buf, sizeof buf, "?%s=%s", key, value.c_str()); - *querystring = buf; - } - else - { - snprintf(buf, sizeof buf, "&%s=%s", key, value.c_str()); - *querystring += string(buf); - } + + int buffer_size = std::string(key).size() + value.size() + 3; + char* buf = new char[buffer_size]; + if (*querystring == "") { + snprintf(buf, buffer_size, "?%s=%s", key, value.c_str()); + *querystring = std::string(buf); + } else { + snprintf(buf, buffer_size, "&%s=%s", key, value.c_str()); + *querystring += std::string(buf); } + delete[] buf; + return MHD_YES; } -const std::string http_request::get_user() const -{ +const std::string http_request::get_user() const { char* username = 0x0; char* password = 0x0; @@ -246,8 +184,7 @@ const std::string http_request::get_user() const return user; } -const std::string http_request::get_pass() const -{ +const std::string http_request::get_pass() const { char* username = 0x0; char* password = 0x0; @@ -262,14 +199,12 @@ const std::string http_request::get_pass() const return pass; } -const std::string http_request::get_digested_user() const -{ +const std::string http_request::get_digested_user() const { char* digested_user_c = 0x0; digested_user_c = MHD_digest_auth_get_username(underlying_connection); std::string digested_user = EMPTY; - if (digested_user_c != 0x0) - { + if (digested_user_c != 0x0) { digested_user = digested_user_c; free(digested_user_c); } @@ -277,35 +212,26 @@ const std::string http_request::get_digested_user() const return digested_user; } -const std::string http_request::get_requestor() const -{ - const MHD_ConnectionInfo * conninfo = MHD_get_connection_info( - underlying_connection, - MHD_CONNECTION_INFO_CLIENT_ADDRESS - ); +const std::string http_request::get_requestor() const { + const MHD_ConnectionInfo * conninfo = MHD_get_connection_info(underlying_connection, MHD_CONNECTION_INFO_CLIENT_ADDRESS); return http::get_ip_str(conninfo->client_addr); } -unsigned short http_request::get_requestor_port() const -{ - const MHD_ConnectionInfo * conninfo = MHD_get_connection_info( - underlying_connection, - MHD_CONNECTION_INFO_CLIENT_ADDRESS - ); +uint16_t http_request::get_requestor_port() const { + const MHD_ConnectionInfo * conninfo = MHD_get_connection_info(underlying_connection, MHD_CONNECTION_INFO_CLIENT_ADDRESS); return http::get_port(conninfo->client_addr); } -std::ostream &operator<< (std::ostream &os, const http_request &r) -{ +std::ostream &operator<< (std::ostream &os, const http_request &r) { os << r.get_method() << " Request [user:\"" << r.get_user() << "\" pass:\"" << r.get_pass() << "\"] path:\"" << r.get_path() << "\"" << std::endl; - http::dump_header_map(os,"Headers",r.get_headers()); - http::dump_header_map(os,"Footers",r.get_footers()); - http::dump_header_map(os,"Cookies",r.get_cookies()); - http::dump_arg_map(os,"Query Args",r.get_args()); + http::dump_header_map(os, "Headers", r.get_headers()); + http::dump_header_map(os, "Footers", r.get_footers()); + http::dump_header_map(os, "Cookies", r.get_cookies()); + http::dump_arg_map(os, "Query Args", r.get_args()); os << " Version [ " << r.get_version() << " ] Requestor [ " << r.get_requestor() << " ] Port [ " << r.get_requestor_port() << " ]" << std::endl; @@ -313,4 +239,4 @@ std::ostream &operator<< (std::ostream &os, const http_request &r) return os; } -} +} // namespace httpserver diff --git a/src/http_resource.cpp b/src/http_resource.cpp index 67fb555b..6e75b542 100644 --- a/src/http_resource.cpp +++ b/src/http_resource.cpp @@ -25,32 +25,27 @@ namespace httpserver { class http_response; } -using namespace std; - -namespace httpserver -{ -//RESOURCE -void resource_init(map& allowed_methods) -{ - allowed_methods[MHD_HTTP_METHOD_GET] = true; - allowed_methods[MHD_HTTP_METHOD_POST] = true; - allowed_methods[MHD_HTTP_METHOD_PUT] = true; - allowed_methods[MHD_HTTP_METHOD_HEAD] = true; - allowed_methods[MHD_HTTP_METHOD_DELETE] = true; - allowed_methods[MHD_HTTP_METHOD_TRACE] = true; - allowed_methods[MHD_HTTP_METHOD_CONNECT] = true; - allowed_methods[MHD_HTTP_METHOD_OPTIONS] = true; - allowed_methods[MHD_HTTP_METHOD_PATCH] = true; +namespace httpserver { + +// RESOURCE +void resource_init(std::map* allowed_methods) { + (*allowed_methods)[MHD_HTTP_METHOD_GET] = true; + (*allowed_methods)[MHD_HTTP_METHOD_POST] = true; + (*allowed_methods)[MHD_HTTP_METHOD_PUT] = true; + (*allowed_methods)[MHD_HTTP_METHOD_HEAD] = true; + (*allowed_methods)[MHD_HTTP_METHOD_DELETE] = true; + (*allowed_methods)[MHD_HTTP_METHOD_TRACE] = true; + (*allowed_methods)[MHD_HTTP_METHOD_CONNECT] = true; + (*allowed_methods)[MHD_HTTP_METHOD_OPTIONS] = true; + (*allowed_methods)[MHD_HTTP_METHOD_PATCH] = true; } -namespace details -{ +namespace details { -shared_ptr empty_render(const http_request& r) -{ - return shared_ptr(new string_response()); +std::shared_ptr empty_render(const http_request&) { + return std::shared_ptr(new string_response()); } -}; +} // namespace details -}; +} // namespace httpserver diff --git a/src/http_response.cpp b/src/http_response.cpp index 2d261e3d..052e58f8 100644 --- a/src/http_response.cpp +++ b/src/http_response.cpp @@ -24,60 +24,44 @@ #include #include "httpserver/http_utils.hpp" -using namespace std; +namespace httpserver { -namespace httpserver -{ - -MHD_Response* http_response::get_raw_response() -{ - return MHD_create_response_from_buffer(0, (void*) "", MHD_RESPMEM_PERSISTENT); +MHD_Response* http_response::get_raw_response() { + return MHD_create_response_from_buffer(0, 0x0, MHD_RESPMEM_PERSISTENT); } -void http_response::decorate_response(MHD_Response* response) -{ - map::iterator it; - - for (it=headers.begin() ; it != headers.end(); ++it) - MHD_add_response_header( - response, - (*it).first.c_str(), - (*it).second.c_str() - ); - - for (it=footers.begin() ; it != footers.end(); ++it) - MHD_add_response_footer(response, - (*it).first.c_str(), - (*it).second.c_str() - ); - - for (it=cookies.begin(); it != cookies.end(); ++it) - MHD_add_response_header( - response, - "Set-Cookie", - ((*it).first + "=" + (*it).second).c_str() - ); +void http_response::decorate_response(MHD_Response* response) { + std::map::iterator it; + + for (it=headers.begin() ; it != headers.end(); ++it) { + MHD_add_response_header(response, (*it).first.c_str(), (*it).second.c_str()); + } + + for (it=footers.begin() ; it != footers.end(); ++it) { + MHD_add_response_footer(response, (*it).first.c_str(), (*it).second.c_str()); + } + + for (it=cookies.begin(); it != cookies.end(); ++it) { + MHD_add_response_header(response, "Set-Cookie", ((*it).first + "=" + (*it).second).c_str()); + } } -int http_response::enqueue_response(MHD_Connection* connection, MHD_Response* response) -{ +int http_response::enqueue_response(MHD_Connection* connection, MHD_Response* response) { return MHD_queue_response(connection, response_code, response); } -void http_response::shoutCAST() -{ +void http_response::shoutCAST() { response_code |= http::http_utils::shoutcast_response; } -std::ostream &operator<< (std::ostream &os, const http_response &r) -{ +std::ostream &operator<< (std::ostream &os, const http_response &r) { os << "Response [response_code:" << r.response_code << "]" << std::endl; - http::dump_header_map(os,"Headers",r.headers); - http::dump_header_map(os,"Footers",r.footers); - http::dump_header_map(os,"Cookies",r.cookies); + http::dump_header_map(os, "Headers", r.headers); + http::dump_header_map(os, "Footers", r.footers); + http::dump_header_map(os, "Cookies", r.cookies); return os; } -} +} // namespace httpserver diff --git a/src/http_utils.cpp b/src/http_utils.cpp index 7f1ea0a6..a79f8f91 100644 --- a/src/http_utils.cpp +++ b/src/http_utils.cpp @@ -20,16 +20,16 @@ #include "httpserver/http_utils.hpp" -#if defined(_WIN32) && ! defined(__CYGWIN__) +#if defined(_WIN32) && !defined(__CYGWIN__) #include #include -#else // WIN32 check +#else // WIN32 check #include #include #include #include #include -#endif // WIN32 check +#endif // WIN32 check #include #include @@ -44,29 +44,27 @@ #include "httpserver/string_utilities.hpp" #pragma GCC diagnostic ignored "-Warray-bounds" -#define CHECK_BIT(var,pos) ((var) & (1<<(pos))) -#define SET_BIT(var,pos) ((var) |= 1 << (pos)) -#define CLEAR_BIT(var,pos) ((var) &= ~(1<<(pos))) +#define CHECK_BIT(var, pos) ((var) & (1 << (pos))) +#define SET_BIT(var, pos) ((var) |= 1 << (pos)) +#define CLEAR_BIT(var, pos) ((var) &= ~(1 << (pos))) #if defined (__CYGWIN__) -#if ! defined (NI_MAXHOST) +#if !defined (NI_MAXHOST) #define NI_MAXHOST 1025 -#endif // NI_MAXHOST +#endif // NI_MAXHOST #ifndef __u_char_defined typedef unsigned char u_char; #define __u_char_defined -#endif // __u_char_defined +#endif // __u_char_defined -#endif // CYGWIN - -using namespace std; +#endif // CYGWIN namespace httpserver { namespace http { -/* See also: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html */ +// See also: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html const int http_utils::http_continue = MHD_HTTP_CONTINUE; const int http_utils::http_switching_protocol = MHD_HTTP_SWITCHING_PROTOCOLS; @@ -75,8 +73,7 @@ const int http_utils::http_processing = MHD_HTTP_PROCESSING; const int http_utils::http_ok = MHD_HTTP_OK; const int http_utils::http_created = MHD_HTTP_CREATED; const int http_utils::http_accepted = MHD_HTTP_ACCEPTED; -const int http_utils::http_non_authoritative_information = - MHD_HTTP_NON_AUTHORITATIVE_INFORMATION; +const int http_utils::http_non_authoritative_information = MHD_HTTP_NON_AUTHORITATIVE_INFORMATION; const int http_utils::http_no_content = MHD_HTTP_NO_CONTENT; const int http_utils::http_reset_content = MHD_HTTP_RESET_CONTENT; const int http_utils::http_partial_content = MHD_HTTP_PARTIAL_CONTENT; @@ -98,20 +95,16 @@ const int http_utils::http_forbidden = MHD_HTTP_FORBIDDEN; const int http_utils::http_not_found = MHD_HTTP_NOT_FOUND; const int http_utils::http_method_not_allowed = MHD_HTTP_METHOD_NOT_ALLOWED; const int http_utils::http_method_not_acceptable = MHD_HTTP_NOT_ACCEPTABLE; -const int http_utils::http_proxy_authentication_required = - MHD_HTTP_PROXY_AUTHENTICATION_REQUIRED; +const int http_utils::http_proxy_authentication_required = MHD_HTTP_PROXY_AUTHENTICATION_REQUIRED; const int http_utils::http_request_timeout = MHD_HTTP_REQUEST_TIMEOUT; const int http_utils::http_conflict = MHD_HTTP_CONFLICT; const int http_utils::http_gone = MHD_HTTP_GONE; const int http_utils::http_length_required = MHD_HTTP_LENGTH_REQUIRED; const int http_utils::http_precondition_failed = MHD_HTTP_PRECONDITION_FAILED; -const int http_utils::http_request_entity_too_large = - MHD_HTTP_PAYLOAD_TOO_LARGE; +const int http_utils::http_request_entity_too_large = MHD_HTTP_PAYLOAD_TOO_LARGE; const int http_utils::http_request_uri_too_long = MHD_HTTP_URI_TOO_LONG; -const int http_utils::http_unsupported_media_type = - MHD_HTTP_UNSUPPORTED_MEDIA_TYPE; -const int http_utils::http_requested_range_not_satisfiable = - MHD_HTTP_RANGE_NOT_SATISFIABLE; +const int http_utils::http_unsupported_media_type = MHD_HTTP_UNSUPPORTED_MEDIA_TYPE; +const int http_utils::http_requested_range_not_satisfiable = MHD_HTTP_RANGE_NOT_SATISFIABLE; const int http_utils::http_expectation_failed = MHD_HTTP_EXPECTATION_FAILED; const int http_utils::http_unprocessable_entity = MHD_HTTP_UNPROCESSABLE_ENTITY; const int http_utils::http_locked = MHD_HTTP_LOCKED; @@ -120,126 +113,90 @@ const int http_utils::http_unordered_collection = MHD_HTTP_UNORDERED_COLLECTION; const int http_utils::http_upgrade_required = MHD_HTTP_UPGRADE_REQUIRED; const int http_utils::http_retry_with = MHD_HTTP_RETRY_WITH; -const int http_utils::http_internal_server_error = - MHD_HTTP_INTERNAL_SERVER_ERROR; +const int http_utils::http_internal_server_error = MHD_HTTP_INTERNAL_SERVER_ERROR; const int http_utils::http_not_implemented = MHD_HTTP_NOT_IMPLEMENTED; const int http_utils::http_bad_gateway = MHD_HTTP_BAD_GATEWAY; const int http_utils::http_service_unavailable = MHD_HTTP_SERVICE_UNAVAILABLE; const int http_utils::http_gateway_timeout = MHD_HTTP_GATEWAY_TIMEOUT; -const int http_utils::http_version_not_supported = - MHD_HTTP_HTTP_VERSION_NOT_SUPPORTED; -const int http_utils::http_variant_also_negotiated = - MHD_HTTP_VARIANT_ALSO_NEGOTIATES; +const int http_utils::http_version_not_supported = MHD_HTTP_HTTP_VERSION_NOT_SUPPORTED; +const int http_utils::http_variant_also_negotiated = MHD_HTTP_VARIANT_ALSO_NEGOTIATES; const int http_utils::http_insufficient_storage = MHD_HTTP_INSUFFICIENT_STORAGE; -const int http_utils::http_bandwidth_limit_exceeded = - MHD_HTTP_BANDWIDTH_LIMIT_EXCEEDED; +const int http_utils::http_bandwidth_limit_exceeded = MHD_HTTP_BANDWIDTH_LIMIT_EXCEEDED; const int http_utils::http_not_extended = MHD_HTTP_NOT_EXTENDED; const int http_utils::shoutcast_response = MHD_ICY_FLAG; -const std::string http_utils::http_header_accept = MHD_HTTP_HEADER_ACCEPT; -const std::string http_utils::http_header_accept_charset = - MHD_HTTP_HEADER_ACCEPT_CHARSET; -const std::string http_utils::http_header_accept_encoding = - MHD_HTTP_HEADER_ACCEPT_ENCODING; -const std::string http_utils::http_header_accept_language = - MHD_HTTP_HEADER_ACCEPT_LANGUAGE; -const std::string http_utils::http_header_accept_ranges = - MHD_HTTP_HEADER_ACCEPT_RANGES; -const std::string http_utils::http_header_age = MHD_HTTP_HEADER_AGE; -const std::string http_utils::http_header_allow = MHD_HTTP_HEADER_ALLOW; -const std::string http_utils::http_header_authorization = - MHD_HTTP_HEADER_AUTHORIZATION; -const std::string http_utils::http_header_cache_control = - MHD_HTTP_HEADER_CACHE_CONTROL; -const std::string http_utils::http_header_connection = - MHD_HTTP_HEADER_CONNECTION; -const std::string http_utils::http_header_content_encoding = - MHD_HTTP_HEADER_CONTENT_ENCODING; -const std::string http_utils::http_header_content_language = - MHD_HTTP_HEADER_CONTENT_LANGUAGE; -const std::string http_utils::http_header_content_length = - MHD_HTTP_HEADER_CONTENT_LENGTH; -const std::string http_utils::http_header_content_location = - MHD_HTTP_HEADER_CONTENT_LOCATION; -const std::string http_utils::http_header_content_md5 = - MHD_HTTP_HEADER_CONTENT_MD5; -const std::string http_utils::http_header_content_range = - MHD_HTTP_HEADER_CONTENT_RANGE; -const std::string http_utils::http_header_content_type = - MHD_HTTP_HEADER_CONTENT_TYPE; -const std::string http_utils::http_header_date = MHD_HTTP_HEADER_DATE; -const std::string http_utils::http_header_etag = MHD_HTTP_HEADER_ETAG; -const std::string http_utils::http_header_expect = MHD_HTTP_HEADER_EXPECT; -const std::string http_utils::http_header_expires = MHD_HTTP_HEADER_EXPIRES; -const std::string http_utils::http_header_from = MHD_HTTP_HEADER_FROM; -const std::string http_utils::http_header_host = MHD_HTTP_HEADER_HOST; -const std::string http_utils::http_header_if_match = MHD_HTTP_HEADER_IF_MATCH; -const std::string http_utils::http_header_if_modified_since = - MHD_HTTP_HEADER_IF_MODIFIED_SINCE; -const std::string http_utils::http_header_if_none_match = - MHD_HTTP_HEADER_IF_NONE_MATCH; -const std::string http_utils::http_header_if_range = MHD_HTTP_HEADER_IF_RANGE; -const std::string http_utils::http_header_if_unmodified_since = - MHD_HTTP_HEADER_IF_UNMODIFIED_SINCE; -const std::string http_utils::http_header_last_modified = - MHD_HTTP_HEADER_LAST_MODIFIED; -const std::string http_utils::http_header_location = MHD_HTTP_HEADER_LOCATION; -const std::string http_utils::http_header_max_forwards = - MHD_HTTP_HEADER_MAX_FORWARDS; -const std::string http_utils::http_header_pragma = MHD_HTTP_HEADER_PRAGMA; -const std::string http_utils::http_header_proxy_authenticate = - MHD_HTTP_HEADER_PROXY_AUTHENTICATE; -const std::string http_utils::http_header_proxy_authentication = - MHD_HTTP_HEADER_PROXY_AUTHORIZATION; -const std::string http_utils::http_header_range = MHD_HTTP_HEADER_RANGE; -const std::string http_utils::http_header_referer = MHD_HTTP_HEADER_REFERER; -const std::string http_utils::http_header_retry_after = - MHD_HTTP_HEADER_RETRY_AFTER; -const std::string http_utils::http_header_server = MHD_HTTP_HEADER_SERVER; -const std::string http_utils::http_header_te = MHD_HTTP_HEADER_TE; -const std::string http_utils::http_header_trailer = MHD_HTTP_HEADER_TRAILER; -const std::string http_utils::http_header_transfer_encoding = - MHD_HTTP_HEADER_TRANSFER_ENCODING; -const std::string http_utils::http_header_upgrade = MHD_HTTP_HEADER_UPGRADE; -const std::string http_utils::http_header_user_agent = - MHD_HTTP_HEADER_USER_AGENT; -const std::string http_utils::http_header_vary = MHD_HTTP_HEADER_VARY; -const std::string http_utils::http_header_via = MHD_HTTP_HEADER_VIA; -const std::string http_utils::http_header_warning = MHD_HTTP_HEADER_WARNING; -const std::string http_utils::http_header_www_authenticate = - MHD_HTTP_HEADER_WWW_AUTHENTICATE; - -const std::string http_utils::http_version_1_0 = MHD_HTTP_VERSION_1_0; -const std::string http_utils::http_version_1_1 = MHD_HTTP_VERSION_1_1; - -const std::string http_utils::http_method_connect = MHD_HTTP_METHOD_CONNECT; -const std::string http_utils::http_method_delete = MHD_HTTP_METHOD_DELETE; -const std::string http_utils::http_method_get = MHD_HTTP_METHOD_GET; -const std::string http_utils::http_method_head = MHD_HTTP_METHOD_HEAD; -const std::string http_utils::http_method_options = MHD_HTTP_METHOD_OPTIONS; -const std::string http_utils::http_method_post = MHD_HTTP_METHOD_POST; -const std::string http_utils::http_method_put = MHD_HTTP_METHOD_PUT; -const std::string http_utils::http_method_trace = MHD_HTTP_METHOD_TRACE; -const std::string http_utils::http_method_patch = MHD_HTTP_METHOD_PATCH; - -const std::string http_utils::http_post_encoding_form_urlencoded = - MHD_HTTP_POST_ENCODING_FORM_URLENCODED; -const std::string http_utils::http_post_encoding_multipart_formdata = - MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA; - -const std::string http_utils::text_plain = "text/plain"; - -std::vector http_utils::tokenize_url( - const std::string& str, - const char separator -) -{ +const char* http_utils::http_header_accept = MHD_HTTP_HEADER_ACCEPT; +const char* http_utils::http_header_accept_charset = MHD_HTTP_HEADER_ACCEPT_CHARSET; +const char* http_utils::http_header_accept_encoding = MHD_HTTP_HEADER_ACCEPT_ENCODING; +const char* http_utils::http_header_accept_language = MHD_HTTP_HEADER_ACCEPT_LANGUAGE; +const char* http_utils::http_header_accept_ranges = MHD_HTTP_HEADER_ACCEPT_RANGES; +const char* http_utils::http_header_age = MHD_HTTP_HEADER_AGE; +const char* http_utils::http_header_allow = MHD_HTTP_HEADER_ALLOW; +const char* http_utils::http_header_authorization = MHD_HTTP_HEADER_AUTHORIZATION; +const char* http_utils::http_header_cache_control = MHD_HTTP_HEADER_CACHE_CONTROL; +const char* http_utils::http_header_connection = MHD_HTTP_HEADER_CONNECTION; +const char* http_utils::http_header_content_encoding = MHD_HTTP_HEADER_CONTENT_ENCODING; +const char* http_utils::http_header_content_language = MHD_HTTP_HEADER_CONTENT_LANGUAGE; +const char* http_utils::http_header_content_length = MHD_HTTP_HEADER_CONTENT_LENGTH; +const char* http_utils::http_header_content_location = MHD_HTTP_HEADER_CONTENT_LOCATION; +const char* http_utils::http_header_content_md5 = MHD_HTTP_HEADER_CONTENT_MD5; +const char* http_utils::http_header_content_range = MHD_HTTP_HEADER_CONTENT_RANGE; +const char* http_utils::http_header_content_type = MHD_HTTP_HEADER_CONTENT_TYPE; +const char* http_utils::http_header_date = MHD_HTTP_HEADER_DATE; +const char* http_utils::http_header_etag = MHD_HTTP_HEADER_ETAG; +const char* http_utils::http_header_expect = MHD_HTTP_HEADER_EXPECT; +const char* http_utils::http_header_expires = MHD_HTTP_HEADER_EXPIRES; +const char* http_utils::http_header_from = MHD_HTTP_HEADER_FROM; +const char* http_utils::http_header_host = MHD_HTTP_HEADER_HOST; +const char* http_utils::http_header_if_match = MHD_HTTP_HEADER_IF_MATCH; +const char* http_utils::http_header_if_modified_since = MHD_HTTP_HEADER_IF_MODIFIED_SINCE; +const char* http_utils::http_header_if_none_match = MHD_HTTP_HEADER_IF_NONE_MATCH; +const char* http_utils::http_header_if_range = MHD_HTTP_HEADER_IF_RANGE; +const char* http_utils::http_header_if_unmodified_since = MHD_HTTP_HEADER_IF_UNMODIFIED_SINCE; +const char* http_utils::http_header_last_modified = MHD_HTTP_HEADER_LAST_MODIFIED; +const char* http_utils::http_header_location = MHD_HTTP_HEADER_LOCATION; +const char* http_utils::http_header_max_forwards = MHD_HTTP_HEADER_MAX_FORWARDS; +const char* http_utils::http_header_pragma = MHD_HTTP_HEADER_PRAGMA; +const char* http_utils::http_header_proxy_authenticate = MHD_HTTP_HEADER_PROXY_AUTHENTICATE; +const char* http_utils::http_header_proxy_authentication = MHD_HTTP_HEADER_PROXY_AUTHORIZATION; +const char* http_utils::http_header_range = MHD_HTTP_HEADER_RANGE; +const char* http_utils::http_header_referer = MHD_HTTP_HEADER_REFERER; +const char* http_utils::http_header_retry_after = MHD_HTTP_HEADER_RETRY_AFTER; +const char* http_utils::http_header_server = MHD_HTTP_HEADER_SERVER; +const char* http_utils::http_header_te = MHD_HTTP_HEADER_TE; +const char* http_utils::http_header_trailer = MHD_HTTP_HEADER_TRAILER; +const char* http_utils::http_header_transfer_encoding = MHD_HTTP_HEADER_TRANSFER_ENCODING; +const char* http_utils::http_header_upgrade = MHD_HTTP_HEADER_UPGRADE; +const char* http_utils::http_header_user_agent = MHD_HTTP_HEADER_USER_AGENT; +const char* http_utils::http_header_vary = MHD_HTTP_HEADER_VARY; +const char* http_utils::http_header_via = MHD_HTTP_HEADER_VIA; +const char* http_utils::http_header_warning = MHD_HTTP_HEADER_WARNING; +const char* http_utils::http_header_www_authenticate = MHD_HTTP_HEADER_WWW_AUTHENTICATE; + +const char* http_utils::http_version_1_0 = MHD_HTTP_VERSION_1_0; +const char* http_utils::http_version_1_1 = MHD_HTTP_VERSION_1_1; + +const char* http_utils::http_method_connect = MHD_HTTP_METHOD_CONNECT; +const char* http_utils::http_method_delete = MHD_HTTP_METHOD_DELETE; +const char* http_utils::http_method_get = MHD_HTTP_METHOD_GET; +const char* http_utils::http_method_head = MHD_HTTP_METHOD_HEAD; +const char* http_utils::http_method_options = MHD_HTTP_METHOD_OPTIONS; +const char* http_utils::http_method_post = MHD_HTTP_METHOD_POST; +const char* http_utils::http_method_put = MHD_HTTP_METHOD_PUT; +const char* http_utils::http_method_trace = MHD_HTTP_METHOD_TRACE; +const char* http_utils::http_method_patch = MHD_HTTP_METHOD_PATCH; + +const char* http_utils::http_post_encoding_form_urlencoded = MHD_HTTP_POST_ENCODING_FORM_URLENCODED; +const char* http_utils::http_post_encoding_multipart_formdata = MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA; + +const char* http_utils::text_plain = "text/plain"; + +std::vector http_utils::tokenize_url(const std::string& str, const char separator) { return string_utilities::string_split(str, separator); } -std::string http_utils::standardize_url(const std::string& url) -{ +std::string http_utils::standardize_url(const std::string& url) { std::string n_url = url; std::string::iterator new_end = std::unique(n_url.begin(), n_url.end(), [](char a, char b) { return (a == b) && (a == '/'); }); @@ -249,168 +206,130 @@ std::string http_utils::standardize_url(const std::string& url) std::string result; - if (n_url_length > 1 && n_url[n_url_length - 1] == '/') - { + if (n_url_length > 1 && n_url[n_url_length - 1] == '/') { result = n_url.substr(0, n_url_length - 1); - } - else - { + } else { result = n_url; } return result; } -std::string get_ip_str(const struct sockaddr *sa, socklen_t maxlen) -{ +std::string get_ip_str(const struct sockaddr *sa) { if (!sa) throw std::invalid_argument("socket pointer is null"); char to_ret[NI_MAXHOST]; - if (AF_INET6 == sa->sa_family) - { - inet_ntop(AF_INET6, &(((sockaddr_in6*) sa)->sin6_addr), to_ret, INET6_ADDRSTRLEN); + if (AF_INET6 == sa->sa_family) { + inet_ntop(AF_INET6, &((reinterpret_cast(sa))->sin6_addr), to_ret, INET6_ADDRSTRLEN); return to_ret; - } - else if (AF_INET == sa->sa_family) - { - inet_ntop(AF_INET, &(((sockaddr_in*) sa)->sin_addr), to_ret, INET_ADDRSTRLEN); + } else if (AF_INET == sa->sa_family) { + inet_ntop(AF_INET, &((reinterpret_cast(sa))->sin_addr), to_ret, INET_ADDRSTRLEN); return to_ret; - } - else - { + } else { throw std::invalid_argument("IP family must be either AF_INET or AF_INET6"); } } -unsigned short get_port(const struct sockaddr* sa) -{ +uint16_t get_port(const struct sockaddr* sa) { if (!sa) throw std::invalid_argument("socket pointer is null"); - if (sa->sa_family == AF_INET) - { - return ((struct sockaddr_in*) sa)->sin_port; - } - else if (sa->sa_family == AF_INET6) - { - return ((struct sockaddr_in6*) sa)->sin6_port; - } - else - { + if (sa->sa_family == AF_INET) { + return (reinterpret_cast(sa))->sin_port; + } else if (sa->sa_family == AF_INET6) { + return (reinterpret_cast(sa))->sin6_port; + } else { throw std::invalid_argument("IP family must be either AF_INET or AF_INET6"); } } -size_t http_unescape(std::string& val) -{ - if (val.empty()) return 0; +size_t http_unescape(std::string* val) { + if (val->empty()) return 0; unsigned int rpos = 0; unsigned int wpos = 0; unsigned int num; - unsigned int size = val.size(); + unsigned int size = val->size(); - while (rpos < size && val[rpos] != '\0') - { - switch (val[rpos]) - { + while (rpos < size && (*val)[rpos] != '\0') { + switch ((*val)[rpos]) { case '+': - val[wpos] = ' '; + (*val)[wpos] = ' '; wpos++; rpos++; break; case '%': - if (size > rpos + 2 && ((1 == sscanf (val.substr(rpos + 1, 2).c_str(), "%2x", &num)) || - (1 == sscanf (val.substr(rpos + 1, 2).c_str(), "%2X", &num))) - ) - { - val[wpos] = (unsigned char) num; + if (size > rpos + 2 && ((1 == sscanf(val->substr(rpos + 1, 2).c_str(), "%2x", &num)) || (1 == sscanf(val->substr(rpos + 1, 2).c_str(), "%2X", &num)))) { + (*val)[wpos] = (unsigned char) num; wpos++; rpos += 3; break; } - /* intentional fall through! */ + // intentional fall through! default: - val[wpos] = val[rpos]; + (*val)[wpos] = (*val)[rpos]; wpos++; rpos++; } } - val[wpos] = '\0'; /* add 0-terminator */ - val.resize(wpos); - return wpos; /* = strlen(val) */ + (*val)[wpos] = '\0'; // add 0-terminator + val->resize(wpos); + return wpos; // = strlen(val) } -ip_representation::ip_representation(const struct sockaddr* ip) -{ +ip_representation::ip_representation(const struct sockaddr* ip) { std::fill(pieces, pieces + 16, 0); - if(ip->sa_family == AF_INET) - { + if (ip->sa_family == AF_INET) { ip_version = http_utils::IPV4; - for(int i=0;i<4;i++) - { - pieces[12+i]=((u_char*)&(((struct sockaddr_in *)ip)->sin_addr))[i]; + const in_addr* sin_addr_pt = &((reinterpret_cast(ip))->sin_addr); + for (int i = 0; i < 4; i++) { + pieces[12 + i] = (reinterpret_cast(sin_addr_pt))[i]; } - } - else - { + } else { ip_version = http_utils::IPV6; - for (int i = 0; i < 16; i++) - { - pieces[i] = ((u_char*)&(((struct sockaddr_in6 *)ip)->sin6_addr))[i]; + const in6_addr* sin_addr6_pt = &((reinterpret_cast(ip))->sin6_addr); + for (int i = 0; i < 16; i++) { + pieces[i] = (reinterpret_cast(sin_addr6_pt))[i]; } } mask = DEFAULT_MASK_VALUE; } -ip_representation::ip_representation(const std::string& ip) -{ +ip_representation::ip_representation(const std::string& ip) { std::vector parts; mask = DEFAULT_MASK_VALUE; std::fill(pieces, pieces + 16, 0); - if(ip.find(':') != std::string::npos) //IPV6 - { + if (ip.find(':') != std::string::npos) { // IPV6 ip_version = http_utils::IPV6; parts = string_utilities::string_split(ip, ':', false); - if (parts.size() > 8) - { + if (parts.size() > 8) { throw std::invalid_argument("IP is badly formatted. Max 8 parts in IPV6."); } unsigned int omitted = 8 - (parts.size() - 1); - if (omitted != 0) - { + if (omitted != 0) { int empty_count = 0; - for (unsigned int i = 0; i < parts.size(); i++) - { + for (unsigned int i = 0; i < parts.size(); i++) { if (parts[i].size() == 0) empty_count++; } - if (empty_count > 1) - { + if (empty_count > 1) { if (parts[parts.size() - 1].find(".") != std::string::npos) omitted -= 1; - if (empty_count == 2 && parts[0] == "" && parts[1] == "") - { + if (empty_count == 2 && parts[0] == "" && parts[1] == "") { omitted += 1; parts = std::vector(parts.begin() + 1, parts.end()); - } - else - { + } else { throw std::invalid_argument("IP is badly formatted. Cannot have more than one omitted segment in IPV6."); } } } int y = 0; - for (unsigned int i = 0; i < parts.size(); i++) - { - if (parts[i] != "*") - { - if (parts[i].size() == 0) - { - for (unsigned int omitted_idx = 0; omitted_idx < omitted; omitted_idx++) - { + for (unsigned int i = 0; i < parts.size(); i++) { + if (parts[i] != "*") { + if (parts[i].size() == 0) { + for (unsigned int omitted_idx = 0; omitted_idx < omitted; omitted_idx++) { pieces[y] = 0; pieces[y+1] = 0; y += 2; @@ -419,140 +338,96 @@ ip_representation::ip_representation(const std::string& ip) continue; } - if (parts[i].size() < 4) - { - stringstream ss; - ss << setfill('0') << setw(4) << parts[i]; + if (parts[i].size() < 4) { + std::stringstream ss; + ss << std::setfill('0') << std::setw(4) << parts[i]; parts[i] = ss.str(); } - if (parts[i].size() == 4) - { - pieces[y] = strtol((parts[i].substr(0,2)).c_str(),NULL,16); - pieces[y+1] = strtol( - (parts[i].substr(2,2)).c_str(), - NULL, - 16 - ); + if (parts[i].size() == 4) { + pieces[y] = strtol((parts[i].substr(0, 2)).c_str(), NULL, 16); + pieces[y+1] = strtol((parts[i].substr(2, 2)).c_str(), NULL, 16); y += 2; - } - else - { - if(parts[i].find('.') != std::string::npos) - { - if(y != 12) - { + } else { + if (parts[i].find('.') != std::string::npos) { + if (y != 12) { throw std::invalid_argument("IP is badly formatted. Missing parts before nested IPV4."); } - if (i != parts.size() - 1) - { + if (i != parts.size() - 1) { throw std::invalid_argument("IP is badly formatted. Nested IPV4 should be at the end"); } - vector subparts = string_utilities::string_split(parts[i], '.'); - if(subparts.size() == 4) - { - for (unsigned int k = 0; k < 10; k++) - { + std::vector subparts = string_utilities::string_split(parts[i], '.'); + if (subparts.size() == 4) { + for (unsigned int k = 0; k < 10; k++) { if (pieces[k] != 0) throw std::invalid_argument("IP is badly formatted. Nested IPV4 can be preceded only by 0 (and, optionally, two 255 octects)"); } - if ((pieces[10] != 0 && pieces[10] != 255) || (pieces[11] != 0 && pieces[11] != 255)) - { + if ((pieces[10] != 0 && pieces[10] != 255) || (pieces[11] != 0 && pieces[11] != 255)) { throw std::invalid_argument("IP is badly formatted. Nested IPV4 can be preceded only by 0 (and, optionally, two 255 octects)"); } - for(unsigned int ii = 0; ii < subparts.size(); ii++) - { - if(subparts[ii] != "*") - { - pieces[y+ii] = strtol( - subparts[ii].c_str(), - NULL, - 10 - ); + for (unsigned int ii = 0; ii < subparts.size(); ii++) { + if (subparts[ii] != "*") { + pieces[y+ii] = strtol(subparts[ii].c_str(), NULL, 10); if (pieces[y+ii] > 255) throw std::invalid_argument("IP is badly formatted. 255 is max value for ip part."); - } - else - { + } else { CLEAR_BIT(mask, y+ii); } } - } - else - { + } else { throw std::invalid_argument("IP is badly formatted. Nested IPV4 can have max 4 parts."); } - } - else - { + } else { throw std::invalid_argument("IP is badly formatted. IPV6 parts can have max 4 characters (or nest an IPV4)"); } } - } - else - { + } else { CLEAR_BIT(mask, y); CLEAR_BIT(mask, y+1); y+=2; } } - } - else //IPV4 - { + } else { // IPV4 ip_version = http_utils::IPV4; parts = string_utilities::string_split(ip, '.'); - if(parts.size() == 4) - { - for(unsigned int i = 0; i < parts.size(); i++) - { - if(parts[i] != "*") - { + if (parts.size() == 4) { + for (unsigned int i = 0; i < parts.size(); i++) { + if (parts[i] != "*") { pieces[12+i] = strtol(parts[i].c_str(), NULL, 10); if (pieces[12+i] > 255) throw std::invalid_argument("IP is badly formatted. 255 is max value for ip part."); - } - else - { + } else { CLEAR_BIT(mask, 12+i); } } - } - else - { + } else { throw std::invalid_argument("IP is badly formatted. Max 4 parts in IPV4."); } } } -bool ip_representation::operator <(const ip_representation& b) const -{ - long this_score = 0; - long b_score = 0; - for (int i = 0; i < 16; i++) - { +bool ip_representation::operator <(const ip_representation& b) const { + int64_t this_score = 0; + int64_t b_score = 0; + for (int i = 0; i < 16; i++) { if (i == 10 || i == 11) continue; - if (CHECK_BIT(mask, i) && CHECK_BIT(b.mask, i)) - { + if (CHECK_BIT(mask, i) && CHECK_BIT(b.mask, i)) { this_score += (16 - i) * pieces[i]; b_score += (16 - i) * b.pieces[i]; } } if (this_score == b_score && - ((pieces[10] == 0x00 || pieces[10] == 0xFF) && (b.pieces[10] == 0x00 || b.pieces[10] == 0xFF)) && - ((pieces[11] == 0x00 || pieces[11] == 0xFF) && (b.pieces[11] == 0x00 || b.pieces[11] == 0xFF)) - ) - { + ((pieces[10] == 0x00 || pieces[10] == 0xFF) && (b.pieces[10] == 0x00 || b.pieces[10] == 0xFF)) && + ((pieces[11] == 0x00 || pieces[11] == 0xFF) && (b.pieces[11] == 0x00 || b.pieces[11] == 0xFF))) { return false; } - for (int i = 10; i < 12; i++) - { - if (CHECK_BIT(mask, i) && CHECK_BIT(b.mask, i)) - { + for (int i = 10; i < 12; i++) { + if (CHECK_BIT(mask, i) && CHECK_BIT(b.mask, i)) { this_score += (16 - i) * pieces[i]; b_score += (16 - i) * b.pieces[i]; } @@ -561,11 +436,9 @@ bool ip_representation::operator <(const ip_representation& b) const return this_score < b_score; } -const std::string load_file (const std::string& filename) -{ - ifstream fp(filename.c_str(), ios::in | ios::binary | ios::ate); - if(fp.is_open()) - { +const std::string load_file(const std::string& filename) { + std::ifstream fp(filename.c_str(), std::ios::in | std::ios::binary | std::ios::ate); + if (fp.is_open()) { std::string content; fp.seekg(0, fp.end); @@ -574,18 +447,14 @@ const std::string load_file (const std::string& filename) content.assign((std::istreambuf_iterator(fp)), std::istreambuf_iterator()); return content; - } - else - { + } else { throw std::invalid_argument("Unable to open file"); } } -void dump_header_map(std::ostream &os, const std::string &prefix, - const std::map &map) -{ - std::map::const_iterator it = map.begin(); - std::map::const_iterator end = map.end(); +void dump_header_map(std::ostream &os, const std::string &prefix, const std::map &map) { + std::map::const_iterator it = map.begin(); + std::map::const_iterator end = map.end(); if (map.size()) { os << " " << prefix << " ["; @@ -596,11 +465,9 @@ void dump_header_map(std::ostream &os, const std::string &prefix, } } -void dump_arg_map(std::ostream &os, const std::string &prefix, - const std::map &map) -{ - std::map::const_iterator it = map.begin(); - std::map::const_iterator end = map.end(); +void dump_arg_map(std::ostream &os, const std::string &prefix, const std::map &map) { + std::map::const_iterator it = map.begin(); + std::map::const_iterator end = map.end(); if (map.size()) { os << " " << prefix << " ["; @@ -611,18 +478,16 @@ void dump_arg_map(std::ostream &os, const std::string &prefix, } } -size_t base_unescaper(std::string& s, unescaper_ptr unescaper) -{ - if(s[0] == 0) return 0; +size_t base_unescaper(std::string* s, unescaper_ptr unescaper) { + if ((*s)[0] == 0) return 0; - if(unescaper != 0x0) - { - unescaper(s); - return s.size(); + if (unescaper != 0x0) { + unescaper(*s); + return s->size(); } return http_unescape(s); } -}; -}; +} // namespace http +} // namespace httpserver diff --git a/src/httpserver.hpp b/src/httpserver.hpp index 772554af..04eb251a 100644 --- a/src/httpserver.hpp +++ b/src/httpserver.hpp @@ -18,8 +18,8 @@ USA */ -#ifndef _HTTPSERVER_HPP_ -#define _HTTPSERVER_HPP_ +#ifndef SRC_HTTPSERVER_HPP_ +#define SRC_HTTPSERVER_HPP_ #define _HTTPSERVER_HPP_INSIDE_ @@ -34,4 +34,4 @@ #include "httpserver/string_response.hpp" #include "httpserver/webserver.hpp" -#endif +#endif // SRC_HTTPSERVER_HPP_ diff --git a/src/httpserver/basic_auth_fail_response.hpp b/src/httpserver/basic_auth_fail_response.hpp index a28fa3d9..11cf6820 100644 --- a/src/httpserver/basic_auth_fail_response.hpp +++ b/src/httpserver/basic_auth_fail_response.hpp @@ -22,8 +22,8 @@ #error "Only or can be included directly." #endif -#ifndef _BASIC_AUTH_FAIL_RESPONSE_HPP_ -#define _BASIC_AUTH_FAIL_RESPONSE_HPP_ +#ifndef SRC_HTTPSERVER_BASIC_AUTH_FAIL_RESPONSE_HPP_ +#define SRC_HTTPSERVER_BASIC_AUTH_FAIL_RESPONSE_HPP_ #include #include "http_utils.hpp" @@ -32,37 +32,32 @@ struct MHD_Connection; struct MHD_Response; -namespace httpserver -{ +namespace httpserver { -class basic_auth_fail_response : public string_response -{ - public: - basic_auth_fail_response() = default; +class basic_auth_fail_response : public string_response { + public: + basic_auth_fail_response() = default; - explicit basic_auth_fail_response( + explicit basic_auth_fail_response( const std::string& content, const std::string& realm = "", int response_code = http::http_utils::http_ok, - const std::string& content_type = http::http_utils::text_plain - ): + const std::string& content_type = http::http_utils::text_plain): string_response(content, response_code, content_type), - realm(realm) - { - } + realm(realm) { } - basic_auth_fail_response(const basic_auth_fail_response& other) = default; - basic_auth_fail_response(basic_auth_fail_response&& other) noexcept = default; - basic_auth_fail_response& operator=(const basic_auth_fail_response& b) = default; - basic_auth_fail_response& operator=(basic_auth_fail_response&& b) = default; + basic_auth_fail_response(const basic_auth_fail_response& other) = default; + basic_auth_fail_response(basic_auth_fail_response&& other) noexcept = default; + basic_auth_fail_response& operator=(const basic_auth_fail_response& b) = default; + basic_auth_fail_response& operator=(basic_auth_fail_response&& b) = default; - ~basic_auth_fail_response() = default; + ~basic_auth_fail_response() = default; - int enqueue_response(MHD_Connection* connection, MHD_Response* response); + int enqueue_response(MHD_Connection* connection, MHD_Response* response); - private: - std::string realm = ""; + private: + std::string realm = ""; }; -} -#endif // _BASIC_AUTH_FAIL_RESPONSE_HPP_ +} // namespace httpserver +#endif // SRC_HTTPSERVER_BASIC_AUTH_FAIL_RESPONSE_HPP_ diff --git a/src/httpserver/create_webserver.hpp b/src/httpserver/create_webserver.hpp index 39cb3ecb..74509264 100644 --- a/src/httpserver/create_webserver.hpp +++ b/src/httpserver/create_webserver.hpp @@ -22,10 +22,12 @@ #error "Only or can be included directly." #endif -#ifndef _CREATE_WEBSERVER_HPP_ -#define _CREATE_WEBSERVER_HPP_ +#ifndef SRC_HTTPSERVER_CREATE_WEBSERVER_HPP_ +#define SRC_HTTPSERVER_CREATE_WEBSERVER_HPP_ #include +#include +#include #include "httpserver/http_response.hpp" #include "httpserver/http_utils.hpp" @@ -43,264 +45,331 @@ typedef bool(*validator_ptr)(const std::string&); typedef void(*log_access_ptr)(const std::string&); typedef void(*log_error_ptr)(const std::string&); -class create_webserver -{ - public: - create_webserver() = default; - create_webserver(const create_webserver& b) = default; - create_webserver(create_webserver&& b) noexcept = default; - create_webserver& operator=(const create_webserver& b) = default; - create_webserver& operator=(create_webserver&& b) = default; - - explicit create_webserver(uint16_t port): - _port(port) - { - } - - create_webserver& port(uint16_t port) { _port = port; return *this; } - create_webserver& start_method( - const http::http_utils::start_method_T& start_method - ) - { - _start_method = start_method; return *this; - } - create_webserver& max_threads(int max_threads) - { - _max_threads = max_threads; return *this; - } - create_webserver& max_connections(int max_connections) - { - _max_connections = max_connections; return *this; - } - create_webserver& memory_limit(int memory_limit) - { - _memory_limit = memory_limit; return *this; - } - create_webserver& content_size_limit(size_t content_size_limit) - { - _content_size_limit = content_size_limit; return *this; - } - create_webserver& connection_timeout(int connection_timeout) - { - _connection_timeout = connection_timeout; return *this; - } - create_webserver& per_IP_connection_limit(int per_IP_connection_limit) - { - _per_IP_connection_limit = per_IP_connection_limit; return *this; - } - create_webserver& log_access(log_access_ptr log_access) - { - _log_access = log_access; return *this; - } - create_webserver& log_error(log_error_ptr log_error) - { - _log_error = log_error; return *this; - } - create_webserver& validator(validator_ptr validator) - { - _validator = validator; return *this; - } - create_webserver& unescaper(unescaper_ptr unescaper) - { - _unescaper = unescaper; return *this; - } - create_webserver& bind_address(const struct sockaddr* bind_address) - { - _bind_address = bind_address; return *this; - } - create_webserver& bind_socket(int bind_socket) - { - _bind_socket = bind_socket; return *this; - } - create_webserver& max_thread_stack_size(int max_thread_stack_size) - { - _max_thread_stack_size = max_thread_stack_size; return *this; - } - create_webserver& use_ssl() { _use_ssl = true; return *this; } - create_webserver& no_ssl() { _use_ssl = false; return *this; } - create_webserver& use_ipv6() { _use_ipv6 = true; return *this; } - create_webserver& no_ipv6() { _use_ipv6 = false; return *this; } - create_webserver& use_dual_stack() { _use_dual_stack = true; return *this; } - create_webserver& no_dual_stack() { _use_dual_stack = false; return *this; } - create_webserver& debug() { _debug = true; return *this; } - create_webserver& no_debug() { _debug = false; return *this; } - create_webserver& pedantic() { _pedantic = true; return *this; } - create_webserver& no_pedantic() { _pedantic = false; return *this; } - create_webserver& https_mem_key(const std::string& https_mem_key) - { - _https_mem_key = http::load_file(https_mem_key); - return *this; - } - create_webserver& https_mem_cert(const std::string& https_mem_cert) - { - _https_mem_cert = http::load_file(https_mem_cert); - return *this; - } - create_webserver& https_mem_trust(const std::string& https_mem_trust) - { - _https_mem_trust = http::load_file(https_mem_trust); - return *this; - } - create_webserver& raw_https_mem_key(const std::string& https_mem_key) - { - _https_mem_key = https_mem_key; return *this; - } - create_webserver& raw_https_mem_cert(const std::string& https_mem_cert) - { - _https_mem_cert = https_mem_cert; return *this; - } - create_webserver& raw_https_mem_trust( - const std::string& https_mem_trust - ) - { - _https_mem_trust = https_mem_trust; return *this; - } - create_webserver& https_priorities(const std::string& https_priorities) - { - _https_priorities = https_priorities; return *this; - } - create_webserver& cred_type(const http::http_utils::cred_type_T& cred_type) - { - _cred_type = cred_type; return *this; - } - create_webserver& digest_auth_random( - const std::string& digest_auth_random - ) - { - _digest_auth_random = digest_auth_random; return *this; - } - create_webserver& nonce_nc_size(int nonce_nc_size) - { - _nonce_nc_size = nonce_nc_size; return *this; - } - create_webserver& default_policy( - const http::http_utils::policy_T& default_policy - ) - { - _default_policy = default_policy; return *this; - } - create_webserver& basic_auth() - { - _basic_auth_enabled = true; return *this; - } - create_webserver& no_basic_auth() - { - _basic_auth_enabled = false; return *this; - } - create_webserver& digest_auth() - { - _digest_auth_enabled = true; return *this; - } - create_webserver& no_digest_auth() - { - _digest_auth_enabled = false; return *this; - } - create_webserver& deferred() - { - _deferred_enabled = true; return *this; - } - create_webserver& no_deferred() - { - _deferred_enabled = false; return *this; - } - create_webserver& regex_checking() - { - _regex_checking = true; return *this; - } - create_webserver& no_regex_checking() - { - _regex_checking = false; return *this; - } - create_webserver& ban_system() - { - _ban_system_enabled = true; return *this; - } - create_webserver& no_ban_system() - { - _ban_system_enabled = false; return *this; - } - create_webserver& post_process() - { - _post_process_enabled = true; return *this; - } - create_webserver& no_post_process() - { - _post_process_enabled = false; return *this; - } - create_webserver& single_resource() - { - _single_resource = true; return *this; - } - create_webserver& no_single_resource() - { - _single_resource = false; return *this; - } - create_webserver& tcp_nodelay() - { - _tcp_nodelay = true; return *this; - } - create_webserver& not_found_resource(render_ptr not_found_resource) - { - _not_found_resource = not_found_resource; return *this; - } - create_webserver& method_not_allowed_resource( - render_ptr method_not_allowed_resource - ) - { - _method_not_allowed_resource = method_not_allowed_resource; - return *this; - } - create_webserver& internal_error_resource( - render_ptr internal_error_resource - ) - { - _internal_error_resource = internal_error_resource; return *this; - } - - private: - uint16_t _port = DEFAULT_WS_PORT; - http::http_utils::start_method_T _start_method = http::http_utils::INTERNAL_SELECT; - int _max_threads = 0; - int _max_connections = 0; - int _memory_limit = 0; - size_t _content_size_limit = static_cast(-1); - int _connection_timeout = DEFAULT_WS_TIMEOUT; - int _per_IP_connection_limit = 0; - log_access_ptr _log_access = 0x0; - log_error_ptr _log_error = 0x0; - validator_ptr _validator = 0x0; - unescaper_ptr _unescaper = 0x0; - const struct sockaddr* _bind_address = 0x0; - int _bind_socket = 0; - int _max_thread_stack_size = 0; - bool _use_ssl = false; - bool _use_ipv6 = false; - bool _use_dual_stack = false; - bool _debug = false; - bool _pedantic = false; - std::string _https_mem_key = ""; - std::string _https_mem_cert = ""; - std::string _https_mem_trust = ""; - std::string _https_priorities = ""; - http::http_utils::cred_type_T _cred_type = http::http_utils::NONE; - std::string _digest_auth_random = ""; - int _nonce_nc_size = 0; - http::http_utils::policy_T _default_policy = http::http_utils::ACCEPT; - bool _basic_auth_enabled = true; - bool _digest_auth_enabled = true; - bool _regex_checking = true; - bool _ban_system_enabled = true; - bool _post_process_enabled = true; - bool _deferred_enabled = false; - bool _single_resource = false; - bool _tcp_nodelay = false; - render_ptr _not_found_resource = 0x0; - render_ptr _method_not_allowed_resource = 0x0; - render_ptr _internal_error_resource = 0x0; - - friend class webserver; +class create_webserver { + public: + create_webserver() = default; + create_webserver(const create_webserver& b) = default; + create_webserver(create_webserver&& b) noexcept = default; + create_webserver& operator=(const create_webserver& b) = default; + create_webserver& operator=(create_webserver&& b) = default; + + explicit create_webserver(uint16_t port): + _port(port) { } + + create_webserver& port(uint16_t port) { + _port = port; + return *this; + } + + create_webserver& start_method(const http::http_utils::start_method_T& start_method) { + _start_method = start_method; + return *this; + } + + create_webserver& max_threads(int max_threads) { + _max_threads = max_threads; + return *this; + } + + create_webserver& max_connections(int max_connections) { + _max_connections = max_connections; + return *this; + } + + create_webserver& memory_limit(int memory_limit) { + _memory_limit = memory_limit; + return *this; + } + + create_webserver& content_size_limit(size_t content_size_limit) { + _content_size_limit = content_size_limit; + return *this; + } + + create_webserver& connection_timeout(int connection_timeout) { + _connection_timeout = connection_timeout; + return *this; + } + + create_webserver& per_IP_connection_limit(int per_IP_connection_limit) { + _per_IP_connection_limit = per_IP_connection_limit; + return *this; + } + + create_webserver& log_access(log_access_ptr log_access) { + _log_access = log_access; + return *this; + } + + create_webserver& log_error(log_error_ptr log_error) { + _log_error = log_error; + return *this; + } + + create_webserver& validator(validator_ptr validator) { + _validator = validator; + return *this; + } + + create_webserver& unescaper(unescaper_ptr unescaper) { + _unescaper = unescaper; + return *this; + } + + create_webserver& bind_address(const struct sockaddr* bind_address) { + _bind_address = bind_address; + return *this; + } + + create_webserver& bind_socket(int bind_socket) { + _bind_socket = bind_socket; + return *this; + } + + create_webserver& max_thread_stack_size(int max_thread_stack_size) { + _max_thread_stack_size = max_thread_stack_size; + return *this; + } + + create_webserver& use_ssl() { + _use_ssl = true; + return *this; + } + + create_webserver& no_ssl() { + _use_ssl = false; + return *this; + } + + create_webserver& use_ipv6() { + _use_ipv6 = true; + return *this; + } + + create_webserver& no_ipv6() { + _use_ipv6 = false; + return *this; + } + + create_webserver& use_dual_stack() { + _use_dual_stack = true; + return *this; + } + + create_webserver& no_dual_stack() { + _use_dual_stack = false; + return *this; + } + + create_webserver& debug() { + _debug = true; + return *this; + } + + create_webserver& no_debug() { + _debug = false; + return *this; + } + + create_webserver& pedantic() { + _pedantic = true; + return *this; + } + + create_webserver& no_pedantic() { + _pedantic = false; + return *this; + } + + create_webserver& https_mem_key(const std::string& https_mem_key) { + _https_mem_key = http::load_file(https_mem_key); + return *this; + } + + create_webserver& https_mem_cert(const std::string& https_mem_cert) { + _https_mem_cert = http::load_file(https_mem_cert); + return *this; + } + + create_webserver& https_mem_trust(const std::string& https_mem_trust) { + _https_mem_trust = http::load_file(https_mem_trust); + return *this; + } + + create_webserver& raw_https_mem_key(const std::string& https_mem_key) { + _https_mem_key = https_mem_key; + return *this; + } + + create_webserver& raw_https_mem_cert(const std::string& https_mem_cert) { + _https_mem_cert = https_mem_cert; + return *this; + } + + create_webserver& raw_https_mem_trust(const std::string& https_mem_trust) { + _https_mem_trust = https_mem_trust; + return *this; + } + + create_webserver& https_priorities(const std::string& https_priorities) { + _https_priorities = https_priorities; + return *this; + } + + create_webserver& cred_type(const http::http_utils::cred_type_T& cred_type) { + _cred_type = cred_type; + return *this; + } + + create_webserver& digest_auth_random(const std::string& digest_auth_random) { + _digest_auth_random = digest_auth_random; + return *this; + } + + create_webserver& nonce_nc_size(int nonce_nc_size) { + _nonce_nc_size = nonce_nc_size; + return *this; + } + + create_webserver& default_policy(const http::http_utils::policy_T& default_policy) { + _default_policy = default_policy; + return *this; + } + + create_webserver& basic_auth() { + _basic_auth_enabled = true; + return *this; + } + + create_webserver& no_basic_auth() { + _basic_auth_enabled = false; + return *this; + } + + create_webserver& digest_auth() { + _digest_auth_enabled = true; + return *this; + } + + create_webserver& no_digest_auth() { + _digest_auth_enabled = false; + return *this; + } + + create_webserver& deferred() { + _deferred_enabled = true; + return *this; + } + + create_webserver& no_deferred() { + _deferred_enabled = false; + return *this; + } + + create_webserver& regex_checking() { + _regex_checking = true; + return *this; + } + + create_webserver& no_regex_checking() { + _regex_checking = false; + return *this; + } + + create_webserver& ban_system() { + _ban_system_enabled = true; + return *this; + } + + create_webserver& no_ban_system() { + _ban_system_enabled = false; + return *this; + } + + create_webserver& post_process() { + _post_process_enabled = true; + return *this; + } + + create_webserver& no_post_process() { + _post_process_enabled = false; + return *this; + } + + create_webserver& single_resource() { + _single_resource = true; + return *this; + } + + create_webserver& no_single_resource() { + _single_resource = false; + return *this; + } + + create_webserver& tcp_nodelay() { + _tcp_nodelay = true; + return *this; + } + + create_webserver& not_found_resource(render_ptr not_found_resource) { + _not_found_resource = not_found_resource; + return *this; + } + + create_webserver& method_not_allowed_resource(render_ptr method_not_allowed_resource) { + _method_not_allowed_resource = method_not_allowed_resource; + return *this; + } + + create_webserver& internal_error_resource(render_ptr internal_error_resource) { + _internal_error_resource = internal_error_resource; + return *this; + } + + private: + uint16_t _port = DEFAULT_WS_PORT; + http::http_utils::start_method_T _start_method = http::http_utils::INTERNAL_SELECT; + int _max_threads = 0; + int _max_connections = 0; + int _memory_limit = 0; + size_t _content_size_limit = static_cast(-1); + int _connection_timeout = DEFAULT_WS_TIMEOUT; + int _per_IP_connection_limit = 0; + log_access_ptr _log_access = 0x0; + log_error_ptr _log_error = 0x0; + validator_ptr _validator = 0x0; + unescaper_ptr _unescaper = 0x0; + const struct sockaddr* _bind_address = 0x0; + int _bind_socket = 0; + int _max_thread_stack_size = 0; + bool _use_ssl = false; + bool _use_ipv6 = false; + bool _use_dual_stack = false; + bool _debug = false; + bool _pedantic = false; + std::string _https_mem_key = ""; + std::string _https_mem_cert = ""; + std::string _https_mem_trust = ""; + std::string _https_priorities = ""; + http::http_utils::cred_type_T _cred_type = http::http_utils::NONE; + std::string _digest_auth_random = ""; + int _nonce_nc_size = 0; + http::http_utils::policy_T _default_policy = http::http_utils::ACCEPT; + bool _basic_auth_enabled = true; + bool _digest_auth_enabled = true; + bool _regex_checking = true; + bool _ban_system_enabled = true; + bool _post_process_enabled = true; + bool _deferred_enabled = false; + bool _single_resource = false; + bool _tcp_nodelay = false; + render_ptr _not_found_resource = 0x0; + render_ptr _method_not_allowed_resource = 0x0; + render_ptr _internal_error_resource = 0x0; + + friend class webserver; }; -} //httpserver +} // namespace httpserver -#endif //_CREATE_WEBSERVER_HPP_ +#endif // SRC_HTTPSERVER_CREATE_WEBSERVER_HPP_ diff --git a/src/httpserver/deferred_response.hpp b/src/httpserver/deferred_response.hpp index 9e4601e2..5ad6071d 100644 --- a/src/httpserver/deferred_response.hpp +++ b/src/httpserver/deferred_response.hpp @@ -22,8 +22,8 @@ #error "Only or can be included directly." #endif -#ifndef _DEFERRED_RESPONSE_HPP_ -#define _DEFERRED_RESPONSE_HPP_ +#ifndef SRC_HTTPSERVER_DEFERRED_RESPONSE_HPP_ +#define SRC_HTTPSERVER_DEFERRED_RESPONSE_HPP_ #include #include @@ -35,53 +35,45 @@ struct MHD_Response; -namespace httpserver -{ +namespace httpserver { -namespace details -{ +namespace details { MHD_Response* get_raw_response_helper(void* cls, ssize_t (*cb)(void*, uint64_t, char*, size_t)); } template -class deferred_response : public string_response -{ - public: - explicit deferred_response( - ssize_t(*cycle_callback)(std::shared_ptr, char*, size_t), - std::shared_ptr closure_data, - const std::string& content = "", - int response_code = http::http_utils::http_ok, - const std::string& content_type = http::http_utils::text_plain - ): - string_response(content, response_code, content_type), - cycle_callback(cycle_callback), - closure_data(closure_data) - { - } - - deferred_response(const deferred_response& other) = default; - deferred_response(deferred_response&& other) noexcept = default; - deferred_response& operator=(const deferred_response& b) = default; - deferred_response& operator=(deferred_response&& b) = default; - - ~deferred_response() = default; - - MHD_Response* get_raw_response() - { - return details::get_raw_response_helper((void*) this, &cb); - } - - private: - ssize_t (*cycle_callback)(std::shared_ptr, char*, size_t); - std::shared_ptr closure_data; - - static ssize_t cb(void* cls, uint64_t, char* buf, size_t max) - { - deferred_response* dfr = static_cast*>(cls); - return dfr->cycle_callback(dfr->closure_data, buf, max); - } +class deferred_response : public string_response { + public: + explicit deferred_response( + ssize_t(*cycle_callback)(std::shared_ptr, char*, size_t), + std::shared_ptr closure_data, + const std::string& content = "", + int response_code = http::http_utils::http_ok, + const std::string& content_type = http::http_utils::text_plain): + string_response(content, response_code, content_type), + cycle_callback(cycle_callback), + closure_data(closure_data) { } + + deferred_response(const deferred_response& other) = default; + deferred_response(deferred_response&& other) noexcept = default; + deferred_response& operator=(const deferred_response& b) = default; + deferred_response& operator=(deferred_response&& b) = default; + + ~deferred_response() = default; + + MHD_Response* get_raw_response() { + return details::get_raw_response_helper(reinterpret_cast(this), &cb); + } + + private: + ssize_t (*cycle_callback)(std::shared_ptr, char*, size_t); + std::shared_ptr closure_data; + + static ssize_t cb(void* cls, uint64_t, char* buf, size_t max) { + deferred_response* dfr = static_cast*>(cls); + return dfr->cycle_callback(dfr->closure_data, buf, max); + } }; -} -#endif // _DEFERRED_RESPONSE_HPP_ +} // namespace httpserver +#endif // SRC_HTTPSERVER_DEFERRED_RESPONSE_HPP_ diff --git a/src/httpserver/details/http_endpoint.hpp b/src/httpserver/details/http_endpoint.hpp index 37fd0d8c..2fcfc81b 100644 --- a/src/httpserver/details/http_endpoint.hpp +++ b/src/httpserver/details/http_endpoint.hpp @@ -22,185 +22,175 @@ #error "Only or can be included directly." #endif -#ifndef _HTTP_ENDPOINT_HPP_ -#define _HTTP_ENDPOINT_HPP_ +#ifndef SRC_HTTPSERVER_DETAILS_HTTP_ENDPOINT_HPP_ +#define SRC_HTTPSERVER_DETAILS_HTTP_ENDPOINT_HPP_ -#include +// cpplint errors on regex because it is replaced (in Chromium) by re2 google library. +// We don't have that alternative here (and we are actively avoiding dependencies). +#include // NOLINT [build/c++11] #include #include #include #include -namespace httpserver -{ +namespace httpserver { -namespace details -{ +namespace details { class http_resource; /** * Class representing an Http Endpoint. It is an abstraction used by the APIs. **/ -class http_endpoint -{ - public: - /** - * Copy constructor. It is useful expecially to copy regex_t structure that contains dinamically allocated data. - * @param h The http_endpoint to copy - **/ - http_endpoint(const http_endpoint& h); - - /** - * Class Destructor - **/ - ~http_endpoint(); //if inlined it causes problems during ruby wrapper compiling - - /** - * Operator overload for "less than operator". It is used to order endpoints in maps. - * @param b The http_endpoint to compare to - * @return boolean indicating if this is less than b. - **/ - bool operator <(const http_endpoint& b) const; - - /** - * Operator overload for "assignment operator". It is used to copy endpoints to existing objects. - * Is is functional expecially to copy regex_t structure that contains dinamically allocated data. - * @param h The http_endpoint to copy - * @return a reference to the http_endpoint obtained - **/ - http_endpoint& operator =(const http_endpoint& h); - - /** - * Method indicating if this endpoint 'matches' with the one passed. A passed endpoint matches a registered endpoint if - * the regex represented by the registered endpoint matches the passed one. - * @param url The endpoint to match - * @return true if the passed endpoint matches this. - **/ - bool match(const http_endpoint& url) const; - - /** - * Method used to get the complete endpoint url - * @return a string representing the url - **/ - const std::string& get_url_complete() const - { - return url_complete; - } - - const std::string& get_url_normalized() const - { - return url_normalized; - } - - /** - * Method used to get all pars defined inside an url. - * @return a vector of strings representing all found pars. - **/ - const std::vector& get_url_pars() const - { - return url_pars; - } - - /** - * Method used to get all pieces of an url; considering an url splitted by '/'. - * @return a vector of strings representing all found pieces. - **/ - const std::vector& get_url_pieces() const - { - return url_pieces; - } - - /** - * Method used to get indexes of all parameters inside url - * @return a vector of int indicating all positions. - **/ - const std::vector& get_chunk_positions() const - { - return chunk_positions; - } - - bool is_family_url() const - { - return family_url; - } - - bool is_regex_compiled() const - { - return reg_compiled; - } - - /** - * Default constructor of the class. - **/ - http_endpoint(): - url_complete("/"), - url_normalized("/"), - re_url_normalized(std::regex("")), // initialize empty - family_url(false), - reg_compiled(false) - { - } - - /** - * Constructor of the class http_endpoint. It is used to initialize an http_endpoint starting from a string form URL. - * @param url The string representation of the endpoint. All endpoints are in the form "/path/to/resource". - * @param family boolean that indicates if the endpoint is a family endpoint. - * A family endpoint is an endpoint that identifies a root and all its child like the same resource. - * For example, if I identify "/path/" like a family endpoint and I associate to it the resource "A", also - * "/path/to/res/" is automatically associated to resource "A". Default is false. - * @param registration boolean that indicates to the system if this is an endpoint that need to be registered to a webserver - * or it is simply an endpoint to be used for comparisons. Default is false. - * @param use_regex boolean that indicates if regexes are checked or not. Default is true. - **/ - http_endpoint(const std::string& url, - bool family = false, - bool registration = false, - bool use_regex = false - ); - private: - /** - * The complete url extracted - **/ - std::string url_complete; - - /** - * The url standardized in order to use standard comparisons or regexes - **/ - std::string url_normalized; - - /** - * Vector containing parameters extracted from url - **/ - std::vector url_pars; - - /** - * Pieces the url can be splitted into (consider '/' as separator) - **/ - std::vector url_pieces; - - /** - * Position of url pieces representing parameters - **/ - std::vector chunk_positions; - - /** - * Regex used in comparisons - **/ - std::regex re_url_normalized; - - /** - * Boolean indicating wheter the endpoint represents a family - **/ - bool family_url; - - /** - * Boolean indicating if the regex is compiled - **/ - bool reg_compiled; +class http_endpoint { + public: + /** + * Copy constructor. It is useful expecially to copy regex_t structure that contains dinamically allocated data. + * @param h The http_endpoint to copy + **/ + http_endpoint(const http_endpoint& h); + + /** + * Class Destructor + **/ + ~http_endpoint(); // if inlined it causes problems during ruby wrapper compiling + + /** + * Operator overload for "less than operator". It is used to order endpoints in maps. + * @param b The http_endpoint to compare to + * @return boolean indicating if this is less than b. + **/ + bool operator <(const http_endpoint& b) const; + + /** + * Operator overload for "assignment operator". It is used to copy endpoints to existing objects. + * Is is functional expecially to copy regex_t structure that contains dinamically allocated data. + * @param h The http_endpoint to copy + * @return a reference to the http_endpoint obtained + **/ + http_endpoint& operator =(const http_endpoint& h); + + /** + * Method indicating if this endpoint 'matches' with the one passed. A passed endpoint matches a registered endpoint if + * the regex represented by the registered endpoint matches the passed one. + * @param url The endpoint to match + * @return true if the passed endpoint matches this. + **/ + bool match(const http_endpoint& url) const; + + /** + * Method used to get the complete endpoint url + * @return a string representing the url + **/ + const std::string& get_url_complete() const { + return url_complete; + } + + const std::string& get_url_normalized() const { + return url_normalized; + } + + /** + * Method used to get all pars defined inside an url. + * @return a vector of strings representing all found pars. + **/ + const std::vector& get_url_pars() const { + return url_pars; + } + + /** + * Method used to get all pieces of an url; considering an url splitted by '/'. + * @return a vector of strings representing all found pieces. + **/ + const std::vector& get_url_pieces() const { + return url_pieces; + } + + /** + * Method used to get indexes of all parameters inside url + * @return a vector of int indicating all positions. + **/ + const std::vector& get_chunk_positions() const { + return chunk_positions; + } + + bool is_family_url() const { + return family_url; + } + + bool is_regex_compiled() const { + return reg_compiled; + } + + /** + * Default constructor of the class. + **/ + http_endpoint(): + url_complete("/"), + url_normalized("/"), + re_url_normalized(std::regex("")), // initialize empty + family_url(false), + reg_compiled(false) { } + + /** + * Constructor of the class http_endpoint. It is used to initialize an http_endpoint starting from a string form URL. + * @param url The string representation of the endpoint. All endpoints are in the form "/path/to/resource". + * @param family boolean that indicates if the endpoint is a family endpoint. + * A family endpoint is an endpoint that identifies a root and all its child like the same resource. + * For example, if I identify "/path/" like a family endpoint and I associate to it the resource "A", also + * "/path/to/res/" is automatically associated to resource "A". Default is false. + * @param registration boolean that indicates to the system if this is an endpoint that need to be registered to a webserver + * or it is simply an endpoint to be used for comparisons. Default is false. + * @param use_regex boolean that indicates if regexes are checked or not. Default is true. + **/ + http_endpoint(const std::string& url, + bool family = false, + bool registration = false, + bool use_regex = false); + + private: + /** + * The complete url extracted + **/ + std::string url_complete; + + /** + * The url standardized in order to use standard comparisons or regexes + **/ + std::string url_normalized; + + /** + * Vector containing parameters extracted from url + **/ + std::vector url_pars; + + /** + * Pieces the url can be splitted into (consider '/' as separator) + **/ + std::vector url_pieces; + + /** + * Position of url pieces representing parameters + **/ + std::vector chunk_positions; + + /** + * Regex used in comparisons + **/ + std::regex re_url_normalized; + + /** + * Boolean indicating wheter the endpoint represents a family + **/ + bool family_url; + + /** + * Boolean indicating if the regex is compiled + **/ + bool reg_compiled; }; -}; +} // namespace details -}; -#endif +} // namespace httpserver +#endif // SRC_HTTPSERVER_DETAILS_HTTP_ENDPOINT_HPP_ diff --git a/src/httpserver/details/modded_request.hpp b/src/httpserver/details/modded_request.hpp index 1ebe5b12..a0c0086c 100644 --- a/src/httpserver/details/modded_request.hpp +++ b/src/httpserver/details/modded_request.hpp @@ -22,19 +22,19 @@ #error "Only or can be included directly." #endif -#ifndef _MODDED_REQUEST_HPP_ -#define _MODDED_REQUEST_HPP_ +#ifndef SRC_HTTPSERVER_DETAILS_MODDED_REQUEST_HPP_ +#define SRC_HTTPSERVER_DETAILS_MODDED_REQUEST_HPP_ + +#include +#include #include "httpserver/http_request.hpp" -namespace httpserver -{ +namespace httpserver { -namespace details -{ +namespace details { -struct modded_request -{ +struct modded_request { struct MHD_PostProcessor *pp = 0x0; std::string* complete_uri = 0x0; std::string* standardized_url = 0x0; @@ -55,22 +55,20 @@ struct modded_request modded_request& operator=(const modded_request& b) = default; modded_request& operator=(modded_request&& b) = default; - ~modded_request() - { - if (NULL != pp) - { - MHD_destroy_post_processor (pp); + ~modded_request() { + if (NULL != pp) { + MHD_destroy_post_processor(pp); + } + if (second) { + delete dhr; } - if(second) - delete dhr; //TODO: verify. It could be an error delete complete_uri; delete standardized_url; } - }; -} //details +} // namespace details -} //httpserver +} // namespace httpserver -#endif //_MODDED_REQUEST_HPP_ +#endif // SRC_HTTPSERVER_DETAILS_MODDED_REQUEST_HPP_ diff --git a/src/httpserver/digest_auth_fail_response.hpp b/src/httpserver/digest_auth_fail_response.hpp index 50abcee2..0cd22f85 100644 --- a/src/httpserver/digest_auth_fail_response.hpp +++ b/src/httpserver/digest_auth_fail_response.hpp @@ -22,8 +22,8 @@ #error "Only or can be included directly." #endif -#ifndef _DIGEST_AUTH_FAIL_RESPONSE_HPP_ -#define _DIGEST_AUTH_FAIL_RESPONSE_HPP_ +#ifndef SRC_HTTPSERVER_DIGEST_AUTH_FAIL_RESPONSE_HPP_ +#define SRC_HTTPSERVER_DIGEST_AUTH_FAIL_RESPONSE_HPP_ #include #include "http_utils.hpp" @@ -32,44 +32,38 @@ struct MHD_Connection; struct MHD_Response; -namespace httpserver -{ - -class digest_auth_fail_response : public string_response -{ - public: - digest_auth_fail_response() = default; - - digest_auth_fail_response( - const std::string& content, - const std::string& realm = "", - const std::string& opaque = "", - bool reload_nonce = false, - int response_code = http::http_utils::http_ok, - const std::string& content_type = http::http_utils::text_plain - ): - string_response(content, response_code, content_type), - realm(realm), - opaque(opaque), - reload_nonce(reload_nonce) - { - } - - digest_auth_fail_response(const digest_auth_fail_response& other) = default; - digest_auth_fail_response(digest_auth_fail_response&& other) noexcept = default; - digest_auth_fail_response& operator=(const digest_auth_fail_response& b) = default; - digest_auth_fail_response& operator=(digest_auth_fail_response&& b) = default; - - ~digest_auth_fail_response() = default; - - int enqueue_response(MHD_Connection* connection, MHD_Response* response); - - private: - std::string realm = ""; - std::string opaque = ""; - bool reload_nonce = false; +namespace httpserver { + +class digest_auth_fail_response : public string_response { + public: + digest_auth_fail_response() = default; + + digest_auth_fail_response(const std::string& content, + const std::string& realm = "", + const std::string& opaque = "", + bool reload_nonce = false, + int response_code = http::http_utils::http_ok, + const std::string& content_type = http::http_utils::text_plain): + string_response(content, response_code, content_type), + realm(realm), + opaque(opaque), + reload_nonce(reload_nonce) { } + + digest_auth_fail_response(const digest_auth_fail_response& other) = default; + digest_auth_fail_response(digest_auth_fail_response&& other) noexcept = default; + digest_auth_fail_response& operator=(const digest_auth_fail_response& b) = default; + digest_auth_fail_response& operator=(digest_auth_fail_response&& b) = default; + + ~digest_auth_fail_response() = default; + + int enqueue_response(MHD_Connection* connection, MHD_Response* response); + + private: + std::string realm = ""; + std::string opaque = ""; + bool reload_nonce = false; }; -} +} // namespace httpserver -#endif // _DIGEST_AUTH_FAIL_RESPONSE_HPP_ +#endif // SRC_HTTPSERVER_DIGEST_AUTH_FAIL_RESPONSE_HPP_ diff --git a/src/httpserver/file_response.hpp b/src/httpserver/file_response.hpp index 0c9386fb..51e5103d 100644 --- a/src/httpserver/file_response.hpp +++ b/src/httpserver/file_response.hpp @@ -22,8 +22,8 @@ #error "Only or can be included directly." #endif -#ifndef _FILE_RESPONSE_HPP_ -#define _FILE_RESPONSE_HPP_ +#ifndef SRC_HTTPSERVER_FILE_RESPONSE_HPP_ +#define SRC_HTTPSERVER_FILE_RESPONSE_HPP_ #include #include "http_utils.hpp" @@ -31,37 +31,32 @@ struct MHD_Response; -namespace httpserver -{ +namespace httpserver { -class file_response : public http_response -{ - public: - file_response() = default; +class file_response : public http_response { + public: + file_response() = default; - explicit file_response( - const std::string& filename, - int response_code = http::http_utils::http_ok, - const std::string& content_type = http::http_utils::text_plain - ): - http_response(response_code, content_type), - filename(filename) - { - } + explicit file_response( + const std::string& filename, + int response_code = http::http_utils::http_ok, + const std::string& content_type = http::http_utils::text_plain): + http_response(response_code, content_type), + filename(filename) { } - file_response(const file_response& other) = default; - file_response(file_response&& other) noexcept = default; + file_response(const file_response& other) = default; + file_response(file_response&& other) noexcept = default; - file_response& operator=(const file_response& b) = default; - file_response& operator=(file_response&& b) = default; + file_response& operator=(const file_response& b) = default; + file_response& operator=(file_response&& b) = default; - ~file_response() = default; + ~file_response() = default; - MHD_Response* get_raw_response(); + MHD_Response* get_raw_response(); - private: - std::string filename = ""; + private: + std::string filename = ""; }; -} -#endif // _FILE_RESPONSE_HPP_ +} // namespace httpserver +#endif // SRC_HTTPSERVER_FILE_RESPONSE_HPP_ diff --git a/src/httpserver/http_request.hpp b/src/httpserver/http_request.hpp index 6aacbfe6..0f1708cf 100644 --- a/src/httpserver/http_request.hpp +++ b/src/httpserver/http_request.hpp @@ -22,8 +22,8 @@ #error "Only or can be included directly." #endif -#ifndef _HTTP_REQUEST_HPP_ -#define _HTTP_REQUEST_HPP_ +#ifndef SRC_HTTPSERVER_HTTP_REQUEST_HPP_ +#define SRC_HTTPSERVER_HTTP_REQUEST_HPP_ #include @@ -39,317 +39,290 @@ struct MHD_Connection; -namespace httpserver -{ +namespace httpserver { /** * Class representing an abstraction for an Http Request. It is used from classes using these apis to receive information through http protocol. **/ -class http_request -{ - public: - static const std::string EMPTY; - - /** - * Method used to get the username eventually passed through basic authentication. - * @return string representation of the username. - **/ - const std::string get_user() const; - - /** - * Method used to get the username extracted from a digest authentication - * @return the username - **/ - const std::string get_digested_user() const; - - /** - * Method used to get the password eventually passed through basic authentication. - * @return string representation of the password. - **/ - const std::string get_pass() const; - - /** - * Method used to get the path requested - * @return string representing the path requested. - **/ - const std::string& get_path() const - { - return path; - } - - /** - * Method used to get all pieces of the path requested; considering an url splitted by '/'. - * @return a vector of strings containing all pieces - **/ - const std::vector get_path_pieces() const - { - return http::http_utils::tokenize_url(path); - } - - /** - * Method used to obtain a specified piece of the path; considering an url splitted by '/'. - * @param index the index of the piece selected - * @return the selected piece in form of string - **/ - const std::string get_path_piece(int index) const - { - std::vector post_path = get_path_pieces(); - if(((int)(post_path.size())) > index) - return post_path[index]; - return EMPTY; - } - - /** - * Method used to get the METHOD used to make the request. - * @return string representing the method. - **/ - const std::string& get_method() const - { - return method; - } - - /** - * Method used to get all headers passed with the request. - * @param result a map > that will be filled with all headers - * @result the size of the map - **/ - const std::map get_headers() const; - - /** - * Method used to get all footers passed with the request. - * @param result a map > that will be filled with all footers - * @result the size of the map - **/ - const std::map get_footers() const; - - /** - * Method used to get all cookies passed with the request. - * @param result a map > that will be filled with all cookies - * @result the size of the map - **/ - const std::map get_cookies() const; - - /** - * Method used to get all args passed with the request. - * @param result a map > that will be filled with all args - * @result the size of the map - **/ - const std::map get_args() const; - - /** - * Method used to get a specific header passed with the request. - * @param key the specific header to get the value from - * @return the value of the header. - **/ - const std::string get_header(const std::string& key) const; - - const std::string get_cookie(const std::string& key) const; - - /** - * Method used to get a specific footer passed with the request. - * @param key the specific footer to get the value from - * @return the value of the footer. - **/ - const std::string get_footer(const std::string& key) const; - - /** - * Method used to get a specific argument passed with the request. - * @param ket the specific argument to get the value from - * @return the value of the arg. - **/ - const std::string get_arg(const std::string& key) const; - - /** - * Method used to get the content of the request. - * @return the content in string representation - **/ - const std::string& get_content() const - { - return content; - } - - /** - * Method to check whether the size of the content reached or exceeded content_size_limit. - * @return boolean - **/ - bool content_too_large() const - { - return content.size()>=content_size_limit; - } - /** - * Method used to get the content of the query string.. - * @return the query string in string representation - **/ - const std::string get_querystring() const; - - /** - * Method used to get the version of the request. - * @return the version in string representation - **/ - const std::string& get_version() const - { - return version; - } - - /** - * Method used to get the requestor. - * @return the requestor - **/ - const std::string get_requestor() const; - - /** - * Method used to get the requestor port used. - * @return the requestor port - **/ - unsigned short get_requestor_port() const; - - bool check_digest_auth(const std::string& realm, - const std::string& password, - int nonce_timeout, bool& reload_nonce - ) const; - - friend std::ostream &operator<< (std::ostream &os, http_request &r); - - private: - /** - * Default constructor of the class. It is a specific responsibility of apis to initialize this type of objects. - **/ - http_request() = default; - - http_request(MHD_Connection* underlying_connection, unescaper_ptr unescaper): - underlying_connection(underlying_connection), - unescaper(unescaper) - { - } - - /** - * Copy constructor. - * @param b http_request b to copy attributes from. - **/ - http_request(const http_request& b) = default; - http_request(http_request&& b) noexcept = default; - - http_request& operator=(const http_request& b) = default; - http_request& operator=(http_request&& b) = default; - - std::string path; - std::string method; - std::map args; - std::string content = ""; - size_t content_size_limit = static_cast(-1); - std::string version; - - struct MHD_Connection* underlying_connection = 0x0; - - unescaper_ptr unescaper = 0x0; - - static MHD_Result build_request_header(void *cls, enum MHD_ValueKind kind, - const char *key, const char *value - ); - - static MHD_Result build_request_args(void *cls, enum MHD_ValueKind kind, - const char *key, const char *value - ); - - static MHD_Result build_request_querystring(void *cls, enum MHD_ValueKind kind, - const char *key, const char *value - ); - - /** - * Method used to set an argument value by key. - * @param key The name identifying the argument - * @param value The value assumed by the argument - **/ - void set_arg(const std::string& key, const std::string& value) - { - args[key] = value.substr(0,content_size_limit); - } - - /** - * Method used to set an argument value by key. - * @param key The name identifying the argument - * @param value The value assumed by the argument - * @param size The size in number of char of the value parameter. - **/ - void set_arg(const char* key, const char* value, size_t size) - { - args[key] = std::string(value, std::min(size, content_size_limit)); - } - - /** - * Method used to set the content of the request - * @param content The content to set. - **/ - void set_content(const std::string& content) - { - this->content = content.substr(0,content_size_limit); - } - - /** - * Method used to set the maximum size of the content - * @param content_size_limit The limit on the maximum size of the content and arg's. - **/ - void set_content_size_limit(size_t content_size_limit) - { - this->content_size_limit = content_size_limit; - } - - /** - * Method used to append content to the request preserving the previous inserted content - * @param content The content to append. - * @param size The size of the data to append. - **/ - void grow_content(const char* content, size_t size) - { - this->content.append(content, size); - if (this->content.size() > content_size_limit) - { - this->content.resize (content_size_limit); - } - } - - /** - * Method used to set the path requested. - * @param path The path searched by the request. - **/ - void set_path(const std::string& path) - { - this->path = path; - } - - /** - * Method used to set the request METHOD - * @param method The method to set for the request - **/ - void set_method(const std::string& method); - - /** - * Method used to set the request http version (ie http 1.1) - * @param version The version to set in form of string - **/ - void set_version(const std::string& version) - { - this->version = version; - } - - /** - * Method used to set all arguments of the request. - * @param args The args key-value map to set for the request. - **/ - void set_args(const std::map& args) - { - std::map::const_iterator it; - for(it = args.begin(); it != args.end(); ++it) - this->args[it->first] = it->second.substr(0,content_size_limit); - } - - const std::string get_connection_value(const std::string& key, enum MHD_ValueKind kind) const; - const std::map get_headerlike_values(enum MHD_ValueKind kind) const; - - friend class webserver; +class http_request { + public: + static const char EMPTY[]; + + /** + * Method used to get the username eventually passed through basic authentication. + * @return string representation of the username. + **/ + const std::string get_user() const; + + /** + * Method used to get the username extracted from a digest authentication + * @return the username + **/ + const std::string get_digested_user() const; + + /** + * Method used to get the password eventually passed through basic authentication. + * @return string representation of the password. + **/ + const std::string get_pass() const; + + /** + * Method used to get the path requested + * @return string representing the path requested. + **/ + const std::string& get_path() const { + return path; + } + + /** + * Method used to get all pieces of the path requested; considering an url splitted by '/'. + * @return a vector of strings containing all pieces + **/ + const std::vector get_path_pieces() const { + return http::http_utils::tokenize_url(path); + } + + /** + * Method used to obtain a specified piece of the path; considering an url splitted by '/'. + * @param index the index of the piece selected + * @return the selected piece in form of string + **/ + const std::string get_path_piece(int index) const { + std::vector post_path = get_path_pieces(); + if ((static_cast((post_path.size()))) > index) { + return post_path[index]; + } + return EMPTY; + } + + /** + * Method used to get the METHOD used to make the request. + * @return string representing the method. + **/ + const std::string& get_method() const { + return method; + } + + /** + * Method used to get all headers passed with the request. + * @param result a map > that will be filled with all headers + * @result the size of the map + **/ + const std::map get_headers() const; + + /** + * Method used to get all footers passed with the request. + * @param result a map > that will be filled with all footers + * @result the size of the map + **/ + const std::map get_footers() const; + + /** + * Method used to get all cookies passed with the request. + * @param result a map > that will be filled with all cookies + * @result the size of the map + **/ + const std::map get_cookies() const; + + /** + * Method used to get all args passed with the request. + * @param result a map > that will be filled with all args + * @result the size of the map + **/ + const std::map get_args() const; + + /** + * Method used to get a specific header passed with the request. + * @param key the specific header to get the value from + * @return the value of the header. + **/ + const std::string get_header(const std::string& key) const; + + const std::string get_cookie(const std::string& key) const; + + /** + * Method used to get a specific footer passed with the request. + * @param key the specific footer to get the value from + * @return the value of the footer. + **/ + const std::string get_footer(const std::string& key) const; + + /** + * Method used to get a specific argument passed with the request. + * @param ket the specific argument to get the value from + * @return the value of the arg. + **/ + const std::string get_arg(const std::string& key) const; + + /** + * Method used to get the content of the request. + * @return the content in string representation + **/ + const std::string& get_content() const { + return content; + } + + /** + * Method to check whether the size of the content reached or exceeded content_size_limit. + * @return boolean + **/ + bool content_too_large() const { + return content.size() >= content_size_limit; + } + /** + * Method used to get the content of the query string.. + * @return the query string in string representation + **/ + const std::string get_querystring() const; + + /** + * Method used to get the version of the request. + * @return the version in string representation + **/ + const std::string& get_version() const { + return version; + } + + /** + * Method used to get the requestor. + * @return the requestor + **/ + const std::string get_requestor() const; + + /** + * Method used to get the requestor port used. + * @return the requestor port + **/ + uint16_t get_requestor_port() const; + + bool check_digest_auth(const std::string& realm, const std::string& password, int nonce_timeout, bool* reload_nonce) const; + + friend std::ostream &operator<< (std::ostream &os, http_request &r); + + private: + /** + * Default constructor of the class. It is a specific responsibility of apis to initialize this type of objects. + **/ + http_request() = default; + + http_request(MHD_Connection* underlying_connection, unescaper_ptr unescaper): + underlying_connection(underlying_connection), + unescaper(unescaper) { } + + /** + * Copy constructor. + * @param b http_request b to copy attributes from. + **/ + http_request(const http_request& b) = default; + http_request(http_request&& b) noexcept = default; + + http_request& operator=(const http_request& b) = default; + http_request& operator=(http_request&& b) = default; + + std::string path; + std::string method; + std::map args; + std::string content = ""; + size_t content_size_limit = static_cast(-1); + std::string version; + + struct MHD_Connection* underlying_connection = 0x0; + + unescaper_ptr unescaper = 0x0; + + static MHD_Result build_request_header(void *cls, enum MHD_ValueKind kind, const char *key, const char *value); + + static MHD_Result build_request_args(void *cls, enum MHD_ValueKind kind, const char *key, const char *value); + + static MHD_Result build_request_querystring(void *cls, enum MHD_ValueKind kind, const char *key, const char *value); + + /** + * Method used to set an argument value by key. + * @param key The name identifying the argument + * @param value The value assumed by the argument + **/ + void set_arg(const std::string& key, const std::string& value) { + args[key] = value.substr(0, content_size_limit); + } + + /** + * Method used to set an argument value by key. + * @param key The name identifying the argument + * @param value The value assumed by the argument + * @param size The size in number of char of the value parameter. + **/ + void set_arg(const char* key, const char* value, size_t size) { + args[key] = std::string(value, std::min(size, content_size_limit)); + } + + /** + * Method used to set the content of the request + * @param content The content to set. + **/ + void set_content(const std::string& content) { + this->content = content.substr(0, content_size_limit); + } + + /** + * Method used to set the maximum size of the content + * @param content_size_limit The limit on the maximum size of the content and arg's. + **/ + void set_content_size_limit(size_t content_size_limit) { + this->content_size_limit = content_size_limit; + } + + /** + * Method used to append content to the request preserving the previous inserted content + * @param content The content to append. + * @param size The size of the data to append. + **/ + void grow_content(const char* content, size_t size) { + this->content.append(content, size); + if (this->content.size() > content_size_limit) { + this->content.resize(content_size_limit); + } + } + + /** + * Method used to set the path requested. + * @param path The path searched by the request. + **/ + void set_path(const std::string& path) { + this->path = path; + } + + /** + * Method used to set the request METHOD + * @param method The method to set for the request + **/ + void set_method(const std::string& method); + + /** + * Method used to set the request http version (ie http 1.1) + * @param version The version to set in form of string + **/ + void set_version(const std::string& version) { + this->version = version; + } + + /** + * Method used to set all arguments of the request. + * @param args The args key-value map to set for the request. + **/ + void set_args(const std::map& args) { + std::map::const_iterator it; + for (it = args.begin(); it != args.end(); ++it) { + this->args[it->first] = it->second.substr(0, content_size_limit); + } + } + + const std::string get_connection_value(const std::string& key, enum MHD_ValueKind kind) const; + const std::map get_headerlike_values(enum MHD_ValueKind kind) const; + + friend class webserver; }; std::ostream &operator<< (std::ostream &os, const http_request &r); -}; -#endif +} // namespace httpserver +#endif // SRC_HTTPSERVER_HTTP_REQUEST_HPP_ diff --git a/src/httpserver/http_resource.hpp b/src/httpserver/http_resource.hpp index 04f67cb8..0def56d7 100644 --- a/src/httpserver/http_resource.hpp +++ b/src/httpserver/http_resource.hpp @@ -22,8 +22,8 @@ #error "Only or can be included directly." #endif -#ifndef _http_resource_hpp_ -#define _http_resource_hpp_ +#ifndef SRC_HTTPSERVER_HTTP_RESOURCE_HPP_ +#define SRC_HTTPSERVER_HTTP_RESOURCE_HPP_ #ifdef DEBUG #include @@ -39,187 +39,181 @@ namespace httpserver { class http_response; } namespace httpserver { -namespace details { std::shared_ptr empty_render(const http_request& r); }; +namespace details { std::shared_ptr empty_render(const http_request& r); } + +void resource_init(std::map* res); /** * Class representing a callable http resource. **/ - -void resource_init(std::map& res); - -class http_resource -{ - public: - /** - * Class destructor - **/ - virtual ~http_resource() = default; - - /** - * Method used to answer to a generic request - * @param req Request passed through http - * @return A http_response object - **/ - virtual const std::shared_ptr render(const http_request& req) - { - return details::empty_render(req); - } - /** - * Method used to answer to a GET request - * @param req Request passed through http - * @return A http_response object - **/ - virtual const std::shared_ptr render_GET(const http_request& req) - { - return render(req); - } - /** - * Method used to answer to a POST request - * @param req Request passed through http - * @return A http_response object - **/ - virtual const std::shared_ptr render_POST(const http_request& req) - { - return render(req); - } - /** - * Method used to answer to a PUT request - * @param req Request passed through http - * @return A http_response object - **/ - virtual const std::shared_ptr render_PUT(const http_request& req) - { - return render(req); - } - /** - * Method used to answer to a HEAD request - * @param req Request passed through http - * @return A http_response object - **/ - virtual const std::shared_ptr render_HEAD(const http_request& req) - { - return render(req); - } - /** - * Method used to answer to a DELETE request - * @param req Request passed through http - * @return A http_response object - **/ - virtual const std::shared_ptr render_DELETE(const http_request& req) - { - return render(req); - } - /** - * Method used to answer to a TRACE request - * @param req Request passed through http - * @return A http_response object - **/ - virtual const std::shared_ptr render_TRACE(const http_request& req) - { - return render(req); - } - /** - * Method used to answer to a OPTIONS request - * @param req Request passed through http - * @return A http_response object - **/ - virtual const std::shared_ptr render_OPTIONS(const http_request& req) - { - return render(req); - } - /** - * Method used to answer to a PATCH request - * @param req Request passed through http - * @return A http_response object - **/ - virtual const std::shared_ptr render_PATCH(const http_request& req) - { - return render(req); - } - /** - * Method used to answer to a CONNECT request - * @param req Request passed through http - * @return A http_response object - **/ - virtual const std::shared_ptr render_CONNECT(const http_request& req) - { - return render(req); - } - /** - * Method used to set if a specific method is allowed or not on this request - * @param method method to set permission on - * @param allowed boolean indicating if the method is allowed or not - **/ - void set_allowing(const std::string& method, bool allowed) - { - if(allowed_methods.count(method)) - { - allowed_methods[method] = allowed; - } - } - /** - * Method used to implicitly allow all methods - **/ - void allow_all() - { - std::map::iterator it; - for ( it=allowed_methods.begin() ; it != allowed_methods.end(); ++it ) - allowed_methods[(*it).first] = true; - } - /** - * Method used to implicitly disallow all methods - **/ - void disallow_all() - { - std::map::iterator it; - for ( it=allowed_methods.begin() ; it != allowed_methods.end(); ++it ) - allowed_methods[(*it).first] = false; - } - /** - * Method used to discover if an http method is allowed or not for this resource - * @param method Method to discover allowings - * @return true if the method is allowed - **/ - bool is_allowed(const std::string& method) - { - if(allowed_methods.count(method)) - { - return allowed_methods[method]; - } - else - { +class http_resource { + public: + /** + * Class destructor + **/ + virtual ~http_resource() = default; + + /** + * Method used to answer to a generic request + * @param req Request passed through http + * @return A http_response object + **/ + virtual const std::shared_ptr render(const http_request& req) { + return details::empty_render(req); + } + + /** + * Method used to answer to a GET request + * @param req Request passed through http + * @return A http_response object + **/ + virtual const std::shared_ptr render_GET(const http_request& req) { + return render(req); + } + + /** + * Method used to answer to a POST request + * @param req Request passed through http + * @return A http_response object + **/ + virtual const std::shared_ptr render_POST(const http_request& req) { + return render(req); + } + + /** + * Method used to answer to a PUT request + * @param req Request passed through http + * @return A http_response object + **/ + virtual const std::shared_ptr render_PUT(const http_request& req) { + return render(req); + } + + /** + * Method used to answer to a HEAD request + * @param req Request passed through http + * @return A http_response object + **/ + virtual const std::shared_ptr render_HEAD(const http_request& req) { + return render(req); + } + + /** + * Method used to answer to a DELETE request + * @param req Request passed through http + * @return A http_response object + **/ + virtual const std::shared_ptr render_DELETE(const http_request& req) { + return render(req); + } + + /** + * Method used to answer to a TRACE request + * @param req Request passed through http + * @return A http_response object + **/ + virtual const std::shared_ptr render_TRACE(const http_request& req) { + return render(req); + } + + /** + * Method used to answer to a OPTIONS request + * @param req Request passed through http + * @return A http_response object + **/ + virtual const std::shared_ptr render_OPTIONS(const http_request& req) { + return render(req); + } + + /** + * Method used to answer to a PATCH request + * @param req Request passed through http + * @return A http_response object + **/ + virtual const std::shared_ptr render_PATCH(const http_request& req) { + return render(req); + } + + /** + * Method used to answer to a CONNECT request + * @param req Request passed through http + * @return A http_response object + **/ + virtual const std::shared_ptr render_CONNECT(const http_request& req) { + return render(req); + } + + /** + * Method used to set if a specific method is allowed or not on this request + * @param method method to set permission on + * @param allowed boolean indicating if the method is allowed or not + **/ + void set_allowing(const std::string& method, bool allowed) { + if (allowed_methods.count(method)) { + allowed_methods[method] = allowed; + } + } + + /** + * Method used to implicitly allow all methods + **/ + void allow_all() { + std::map::iterator it; + for (it=allowed_methods.begin(); it != allowed_methods.end(); ++it) { + allowed_methods[(*it).first] = true; + } + } + + /** + * Method used to implicitly disallow all methods + **/ + void disallow_all() { + std::map::iterator it; + for (it=allowed_methods.begin(); it != allowed_methods.end(); ++it) { + allowed_methods[(*it).first] = false; + } + } + + /** + * Method used to discover if an http method is allowed or not for this resource + * @param method Method to discover allowings + * @return true if the method is allowed + **/ + bool is_allowed(const std::string& method) { + if (allowed_methods.count(method)) { + return allowed_methods[method]; + } else { #ifdef DEBUG - std::map::iterator it; - for(it = allowed_methods.begin(); it != allowed_methods.end(); ++it) - { - std::cout << (*it).first << " -> " << (*it).second << std::endl; - } -#endif //DEBUG - return false; - } - } - protected: - /** - * Constructor of the class - **/ - http_resource() - { - resource_init(allowed_methods); - } - - /** - * Copy constructor - **/ - http_resource(const http_resource& b) = default; - http_resource(http_resource&& b) noexcept = default; - http_resource& operator=(const http_resource& b) = default; - http_resource& operator=(http_resource&& b) = default; - - private: - friend class webserver; - friend void resource_init(std::map& res); - std::map allowed_methods; + std::map::iterator it; + for (it = allowed_methods.begin(); it != allowed_methods.end(); ++it) { + std::cout << (*it).first << " -> " << (*it).second << std::endl; + } +#endif // DEBUG + return false; + } + } + + protected: + /** + * Constructor of the class + **/ + http_resource() { + resource_init(&allowed_methods); + } + + /** + * Copy constructor + **/ + http_resource(const http_resource& b) = default; + http_resource(http_resource&& b) noexcept = default; + http_resource& operator=(const http_resource& b) = default; + http_resource& operator=(http_resource&& b) = default; + + private: + friend class webserver; + friend void resource_init(std::map* res); + std::map allowed_methods; }; -} -#endif +} // namespace httpserver +#endif // SRC_HTTPSERVER_HTTP_RESOURCE_HPP_ diff --git a/src/httpserver/http_response.hpp b/src/httpserver/http_response.hpp index 1f3f0971..0ab1ec87 100644 --- a/src/httpserver/http_response.hpp +++ b/src/httpserver/http_response.hpp @@ -22,8 +22,8 @@ #error "Only or can be included directly." #endif -#ifndef _HTTP_RESPONSE_HPP_ -#define _HTTP_RESPONSE_HPP_ +#ifndef SRC_HTTPSERVER_HTTP_RESPONSE_HPP_ +#define SRC_HTTPSERVER_HTTP_RESPONSE_HPP_ #include #include @@ -33,125 +33,112 @@ struct MHD_Connection; struct MHD_Response; -namespace httpserver -{ +namespace httpserver { /** * Class representing an abstraction for an Http Response. It is used from classes using these apis to send information through http protocol. **/ -class http_response -{ - public: - http_response() = default; - - explicit http_response(int response_code, const std::string& content_type): - response_code(response_code) - { - headers[http::http_utils::http_header_content_type] = content_type; - } - - /** - * Copy constructor - * @param b The http_response object to copy attributes value from. - **/ - http_response(const http_response& b) = default; - http_response(http_response&& b) noexcept = default; - - http_response& operator=(const http_response& b) = default; - http_response& operator=(http_response&& b) noexcept = default; - - virtual ~http_response() = default; - - /** - * Method used to get a specified header defined for the response - * @param key The header identification - * @return a string representing the value assumed by the header - **/ - const std::string& get_header(const std::string& key) - { - return headers[key]; - } - - /** - * Method used to get a specified footer defined for the response - * @param key The footer identification - * @return a string representing the value assumed by the footer - **/ - const std::string& get_footer(const std::string& key) - { - return footers[key]; - } - - const std::string& get_cookie(const std::string& key) - { - return cookies[key]; - } - - /** - * Method used to get all headers passed with the request. - * @return a map containing all headers. - **/ - const std::map& get_headers() const - { - return headers; - } - - /** - * Method used to get all footers passed with the request. - * @return a map containing all footers. - **/ - const std::map& get_footers() const - { - return footers; - } - - const std::map& get_cookies() const - { - return cookies; - } - - /** - * Method used to get the response code from the response - * @return The response code - **/ - int get_response_code() const - { - return response_code; - } - - void with_header(const std::string& key, const std::string& value) - { - headers[key] = value; - } - - void with_footer(const std::string& key, const std::string& value) - { - footers[key] = value; - } - - void with_cookie(const std::string& key, const std::string& value) - { - cookies[key] = value; - } - - void shoutCAST(); - - virtual MHD_Response* get_raw_response(); - virtual void decorate_response(MHD_Response* response); - virtual int enqueue_response(MHD_Connection* connection, MHD_Response* response); - - private: - int response_code = -1; - - std::map headers; - std::map footers; - std::map cookies; - - protected: - friend std::ostream &operator<< (std::ostream &os, const http_response &r); +class http_response { + public: + http_response() = default; + + explicit http_response(int response_code, const std::string& content_type): + response_code(response_code) { + headers[http::http_utils::http_header_content_type] = content_type; + } + + /** + * Copy constructor + * @param b The http_response object to copy attributes value from. + **/ + http_response(const http_response& b) = default; + http_response(http_response&& b) noexcept = default; + + http_response& operator=(const http_response& b) = default; + http_response& operator=(http_response&& b) noexcept = default; + + virtual ~http_response() = default; + + /** + * Method used to get a specified header defined for the response + * @param key The header identification + * @return a string representing the value assumed by the header + **/ + const std::string& get_header(const std::string& key) { + return headers[key]; + } + + /** + * Method used to get a specified footer defined for the response + * @param key The footer identification + * @return a string representing the value assumed by the footer + **/ + const std::string& get_footer(const std::string& key) { + return footers[key]; + } + + const std::string& get_cookie(const std::string& key) { + return cookies[key]; + } + + /** + * Method used to get all headers passed with the request. + * @return a map containing all headers. + **/ + const std::map& get_headers() const { + return headers; + } + + /** + * Method used to get all footers passed with the request. + * @return a map containing all footers. + **/ + const std::map& get_footers() const { + return footers; + } + + const std::map& get_cookies() const { + return cookies; + } + + /** + * Method used to get the response code from the response + * @return The response code + **/ + int get_response_code() const { + return response_code; + } + + void with_header(const std::string& key, const std::string& value) { + headers[key] = value; + } + + void with_footer(const std::string& key, const std::string& value) { + footers[key] = value; + } + + void with_cookie(const std::string& key, const std::string& value) { + cookies[key] = value; + } + + void shoutCAST(); + + virtual MHD_Response* get_raw_response(); + virtual void decorate_response(MHD_Response* response); + virtual int enqueue_response(MHD_Connection* connection, MHD_Response* response); + + private: + int response_code = -1; + + std::map headers; + std::map footers; + std::map cookies; + + protected: + friend std::ostream &operator<< (std::ostream &os, const http_response &r); }; -std::ostream &operator<< (std::ostream &os, const http_response &r); +std::ostream &operator<<(std::ostream &os, const http_response &r); -}; -#endif +} // namespace httpserver +#endif // SRC_HTTPSERVER_HTTP_RESPONSE_HPP_ diff --git a/src/httpserver/http_utils.hpp b/src/httpserver/http_utils.hpp index 338b3f70..566955fb 100644 --- a/src/httpserver/http_utils.hpp +++ b/src/httpserver/http_utils.hpp @@ -22,8 +22,8 @@ #error "Only or can be included directly." #endif -#ifndef _HTTPUTILS_H_ -#define _HTTPUTILS_H_ +#ifndef SRC_HTTPSERVER_HTTP_UTILS_HPP_ +#define SRC_HTTPSERVER_HTTP_UTILS_HPP_ #ifdef HAVE_GNUTLS #include @@ -68,211 +68,201 @@ typedef void(*unescaper_ptr)(std::string&); namespace http { -class http_utils -{ - public: - - enum cred_type_T - { - NONE = -1 +class http_utils { + public: + enum cred_type_T { + NONE = -1 #ifdef HAVE_GNUTLS - ,CERTIFICATE = GNUTLS_CRD_CERTIFICATE, - ANON = GNUTLS_CRD_ANON, - SRP = GNUTLS_CRD_SRP, - PSK = GNUTLS_CRD_PSK, - IA = GNUTLS_CRD_IA + , CERTIFICATE = GNUTLS_CRD_CERTIFICATE, + ANON = GNUTLS_CRD_ANON, + SRP = GNUTLS_CRD_SRP, + PSK = GNUTLS_CRD_PSK, + IA = GNUTLS_CRD_IA #endif - }; - - enum start_method_T - { - INTERNAL_SELECT = MHD_USE_SELECT_INTERNALLY | MHD_USE_AUTO, - THREAD_PER_CONNECTION = MHD_USE_THREAD_PER_CONNECTION | MHD_USE_AUTO - }; - - enum policy_T - { - ACCEPT, - REJECT - }; - - enum IP_version_T - { - IPV4 = 4, IPV6 = 16 - }; - - static const short http_method_connect_code; - static const short http_method_delete_code; - static const short http_method_get_code; - static const short http_method_head_code; - static const short http_method_options_code; - static const short http_method_post_code; - static const short http_method_put_code; - static const short http_method_trace_code; - static const short http_method_patch_code; - static const short http_method_unknown_code; - - static const int http_continue; - static const int http_switching_protocol; - static const int http_processing; - - static const int http_ok; - static const int http_created; - static const int http_accepted; - static const int http_non_authoritative_information; - static const int http_no_content; - static const int http_reset_content; - static const int http_partial_content; - static const int http_multi_status; - - static const int http_multiple_choices; - static const int http_moved_permanently; - static const int http_found; - static const int http_see_other; - static const int http_not_modified; - static const int http_use_proxy; - static const int http_switch_proxy; - static const int http_temporary_redirect; - - static const int http_bad_request; - static const int http_unauthorized; - static const int http_payment_required; - static const int http_forbidden; - static const int http_not_found; - static const int http_method_not_allowed; - static const int http_method_not_acceptable; - static const int http_proxy_authentication_required; - static const int http_request_timeout; - static const int http_conflict; - static const int http_gone; - static const int http_length_required; - static const int http_precondition_failed; - static const int http_request_entity_too_large; - static const int http_request_uri_too_long; - static const int http_unsupported_media_type; - static const int http_requested_range_not_satisfiable; - static const int http_expectation_failed; - static const int http_unprocessable_entity; - static const int http_locked; - static const int http_failed_dependency; - static const int http_unordered_collection; - static const int http_upgrade_required; - static const int http_retry_with; - - static const int http_internal_server_error; - static const int http_not_implemented; - static const int http_bad_gateway; - static const int http_service_unavailable; - static const int http_gateway_timeout; - static const int http_version_not_supported; - static const int http_variant_also_negotiated; - static const int http_insufficient_storage; - static const int http_bandwidth_limit_exceeded; - static const int http_not_extended; - - static const int shoutcast_response; - - /* See also: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html */ - static const std::string http_header_accept; - static const std::string http_header_accept_charset; - static const std::string http_header_accept_encoding; - static const std::string http_header_accept_language; - static const std::string http_header_accept_ranges; - static const std::string http_header_age; - static const std::string http_header_allow; - static const std::string http_header_authorization; - static const std::string http_header_cache_control; - static const std::string http_header_connection; - static const std::string http_header_content_encoding; - static const std::string http_header_content_language; - static const std::string http_header_content_length; - static const std::string http_header_content_location; - static const std::string http_header_content_md5; - static const std::string http_header_content_range; - static const std::string http_header_content_type; - static const std::string http_header_date; - static const std::string http_header_etag; - static const std::string http_header_expect; - static const std::string http_header_expires; - static const std::string http_header_from; - static const std::string http_header_host; - static const std::string http_header_if_match; - static const std::string http_header_if_modified_since; - static const std::string http_header_if_none_match; - static const std::string http_header_if_range; - static const std::string http_header_if_unmodified_since; - static const std::string http_header_last_modified; - static const std::string http_header_location; - static const std::string http_header_max_forwards; - static const std::string http_header_pragma; - static const std::string http_header_proxy_authenticate; - static const std::string http_header_proxy_authentication; - static const std::string http_header_range; - static const std::string http_header_referer; - static const std::string http_header_retry_after; - static const std::string http_header_server; - static const std::string http_header_te; - static const std::string http_header_trailer; - static const std::string http_header_transfer_encoding; - static const std::string http_header_upgrade; - static const std::string http_header_user_agent; - static const std::string http_header_vary; - static const std::string http_header_via; - static const std::string http_header_warning; - static const std::string http_header_www_authenticate; - - static const std::string http_version_1_0; - static const std::string http_version_1_1; - - static const std::string http_method_connect; - static const std::string http_method_delete; - static const std::string http_method_head; - static const std::string http_method_get; - static const std::string http_method_options; - static const std::string http_method_post; - static const std::string http_method_put; - static const std::string http_method_trace; - static const std::string http_method_patch; - - static const std::string http_post_encoding_form_urlencoded; - static const std::string http_post_encoding_multipart_formdata; - - static const std::string text_plain; - - static std::vector tokenize_url(const std::string&, - const char separator = '/' - ); - static std::string standardize_url(const std::string&); + }; + + enum start_method_T { + INTERNAL_SELECT = MHD_USE_SELECT_INTERNALLY | MHD_USE_AUTO, + THREAD_PER_CONNECTION = MHD_USE_THREAD_PER_CONNECTION | MHD_USE_AUTO + }; + + enum policy_T { + ACCEPT, + REJECT + }; + + enum IP_version_T { + IPV4 = 4, + IPV6 = 16 + }; + + static const uint16_t http_method_connect_code; + static const uint16_t http_method_delete_code; + static const uint16_t http_method_get_code; + static const uint16_t http_method_head_code; + static const uint16_t http_method_options_code; + static const uint16_t http_method_post_code; + static const uint16_t http_method_put_code; + static const uint16_t http_method_trace_code; + static const uint16_t http_method_patch_code; + static const uint16_t http_method_unknown_code; + + static const int http_continue; + static const int http_switching_protocol; + static const int http_processing; + + static const int http_ok; + static const int http_created; + static const int http_accepted; + static const int http_non_authoritative_information; + static const int http_no_content; + static const int http_reset_content; + static const int http_partial_content; + static const int http_multi_status; + + static const int http_multiple_choices; + static const int http_moved_permanently; + static const int http_found; + static const int http_see_other; + static const int http_not_modified; + static const int http_use_proxy; + static const int http_switch_proxy; + static const int http_temporary_redirect; + + static const int http_bad_request; + static const int http_unauthorized; + static const int http_payment_required; + static const int http_forbidden; + static const int http_not_found; + static const int http_method_not_allowed; + static const int http_method_not_acceptable; + static const int http_proxy_authentication_required; + static const int http_request_timeout; + static const int http_conflict; + static const int http_gone; + static const int http_length_required; + static const int http_precondition_failed; + static const int http_request_entity_too_large; + static const int http_request_uri_too_long; + static const int http_unsupported_media_type; + static const int http_requested_range_not_satisfiable; + static const int http_expectation_failed; + static const int http_unprocessable_entity; + static const int http_locked; + static const int http_failed_dependency; + static const int http_unordered_collection; + static const int http_upgrade_required; + static const int http_retry_with; + + static const int http_internal_server_error; + static const int http_not_implemented; + static const int http_bad_gateway; + static const int http_service_unavailable; + static const int http_gateway_timeout; + static const int http_version_not_supported; + static const int http_variant_also_negotiated; + static const int http_insufficient_storage; + static const int http_bandwidth_limit_exceeded; + static const int http_not_extended; + + static const int shoutcast_response; + + // See also: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html + static const char* http_header_accept; + static const char* http_header_accept_charset; + static const char* http_header_accept_encoding; + static const char* http_header_accept_language; + static const char* http_header_accept_ranges; + static const char* http_header_age; + static const char* http_header_allow; + static const char* http_header_authorization; + static const char* http_header_cache_control; + static const char* http_header_connection; + static const char* http_header_content_encoding; + static const char* http_header_content_language; + static const char* http_header_content_length; + static const char* http_header_content_location; + static const char* http_header_content_md5; + static const char* http_header_content_range; + static const char* http_header_content_type; + static const char* http_header_date; + static const char* http_header_etag; + static const char* http_header_expect; + static const char* http_header_expires; + static const char* http_header_from; + static const char* http_header_host; + static const char* http_header_if_match; + static const char* http_header_if_modified_since; + static const char* http_header_if_none_match; + static const char* http_header_if_range; + static const char* http_header_if_unmodified_since; + static const char* http_header_last_modified; + static const char* http_header_location; + static const char* http_header_max_forwards; + static const char* http_header_pragma; + static const char* http_header_proxy_authenticate; + static const char* http_header_proxy_authentication; + static const char* http_header_range; + static const char* http_header_referer; + static const char* http_header_retry_after; + static const char* http_header_server; + static const char* http_header_te; + static const char* http_header_trailer; + static const char* http_header_transfer_encoding; + static const char* http_header_upgrade; + static const char* http_header_user_agent; + static const char* http_header_vary; + static const char* http_header_via; + static const char* http_header_warning; + static const char* http_header_www_authenticate; + + static const char* http_version_1_0; + static const char* http_version_1_1; + + static const char* http_method_connect; + static const char* http_method_delete; + static const char* http_method_head; + static const char* http_method_get; + static const char* http_method_options; + static const char* http_method_post; + static const char* http_method_put; + static const char* http_method_trace; + static const char* http_method_patch; + + static const char* http_post_encoding_form_urlencoded; + static const char* http_post_encoding_multipart_formdata; + + static const char* text_plain; + + static std::vector tokenize_url(const std::string&, const char separator = '/'); + static std::string standardize_url(const std::string&); }; -#define COMPARATOR(x, y, op) \ - { \ - size_t l1 = (x).size();\ - size_t l2 = (y).size();\ - if (l1 < l2) return true;\ - if (l1 > l2) return false;\ - \ - for (size_t n = 0; n < l1; n++)\ - {\ - int xc = op((x)[n]);\ - int yc = op((y)[n]);\ - if (xc < yc) return true;\ - if (xc > yc) return false;\ - }\ - return false;\ - } +#define COMPARATOR(x, y, op) { \ + size_t l1 = (x).size(); \ + size_t l2 = (y).size(); \ + if (l1 < l2) return true; \ + if (l1 > l2) return false; \ + \ + for (size_t n = 0; n < l1; n++) { \ + int xc = op((x)[n]); \ + int yc = op((y)[n]); \ + if (xc < yc) return true; \ + if (xc > yc) return false; \ + } \ + return false; \ +} class header_comparator { - public: - /** - * Operator used to compare strings. - * @param first string - * @param second string - **/ - bool operator()(const std::string& x,const std::string& y) const - { - COMPARATOR(x, y, std::toupper); - } + public: + /** + * Operator used to compare strings. + * @param first string + * @param second string + **/ + bool operator()(const std::string& x, const std::string& y) const { + COMPARATOR(x, y, std::toupper); + } }; /** @@ -281,43 +271,40 @@ class header_comparator { * compilation phase the flag CASE_INSENSITIVE to the preprocessor. **/ class arg_comparator { - public: - /** - * Operator used to compare strings. - * @param first string - * @param second string - **/ - bool operator()(const std::string& x,const std::string& y) const - { + public: + /** + * Operator used to compare strings. + * @param first string + * @param second string + **/ + bool operator()(const std::string& x, const std::string& y) const { #ifdef CASE_INSENSITIVE - COMPARATOR(x, y, std::toupper); + COMPARATOR(x, y, std::toupper); #else - COMPARATOR(x, y, ); + COMPARATOR(x, y,); // NOLINT(whitespace/comma) #endif - } + } }; -struct ip_representation -{ +struct ip_representation { http_utils::IP_version_T ip_version; - unsigned short pieces[16]; - unsigned short mask; - - ip_representation(http_utils::IP_version_T ip_version) : - ip_version(ip_version) - { - mask = DEFAULT_MASK_VALUE; - std::fill(pieces, pieces + 16, 0); + uint16_t pieces[16]; + uint16_t mask; + + explicit ip_representation(http_utils::IP_version_T ip_version) : + ip_version(ip_version) { + mask = DEFAULT_MASK_VALUE; + std::fill(pieces, pieces + 16, 0); } - ip_representation(const std::string& ip); - ip_representation(const struct sockaddr* ip); + explicit ip_representation(const std::string& ip); + explicit ip_representation(const struct sockaddr* ip); + + bool operator<(const ip_representation& b) const; - bool operator <(const ip_representation& b) const; - int weight() const - { - //variable-precision SWAR algorithm - unsigned short x = mask; + int weight() const { + // variable-precision SWAR algorithm + uint16_t x = mask; x = x - ((x >> 1) & 0x55555555); x = (x & 0x33333333) + ((x >> 2) & 0x33333333); return (((x + (x >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24; @@ -330,15 +317,14 @@ struct ip_representation * @param maxlen Maxlen of the address (automatically discovered if not passed) * @return string containing the ip address **/ -std::string get_ip_str(const struct sockaddr *sa, socklen_t maxlen = 0); +std::string get_ip_str(const struct sockaddr *sa); -std::string get_ip_str_new(const struct sockaddr* sa, socklen_t maxlen = 0); /** * Method used to get a port from a sockaddr * @param sa The sockaddr object to find the port from * @return short representing the port **/ -unsigned short get_port(const struct sockaddr* sa); +uint16_t get_port(const struct sockaddr* sa); /** * Method to output the contents of a headers map to a std::ostream @@ -346,8 +332,7 @@ unsigned short get_port(const struct sockaddr* sa); * @param prefix Prefix to identify the map * @param map **/ -void dump_header_map(std::ostream &os, const std::string &prefix, - const std::map &map); +void dump_header_map(std::ostream &os, const std::string &prefix, const std::map &map); /** * Method to output the contents of an arguments map to a std::ostream @@ -355,8 +340,7 @@ void dump_header_map(std::ostream &os, const std::string &prefix, * @param prefix Prefix to identify the map * @param map **/ -void dump_arg_map(std::ostream &os, const std::string &prefix, - const std::map &map); +void dump_arg_map(std::ostream &os, const std::string &prefix, const std::map &map); /** * Process escape sequences ('+'=space, %HH) Updates val in place; the @@ -367,13 +351,13 @@ void dump_arg_map(std::ostream &os, const std::string &prefix, * @return length of the resulting val (strlen(val) maybe * shorter afterwards due to elimination of escape sequences) */ -size_t http_unescape (std::string& val); +size_t http_unescape(std::string* val); -const std::string load_file (const std::string& filename); +const std::string load_file(const std::string& filename); -size_t base_unescaper(std::string&, unescaper_ptr unescaper); +size_t base_unescaper(std::string*, unescaper_ptr unescaper); -}; -}; -#endif +} // namespace http +} // namespace httpserver +#endif // SRC_HTTPSERVER_HTTP_UTILS_HPP_ diff --git a/src/httpserver/string_response.hpp b/src/httpserver/string_response.hpp index 43e7580d..d50eb417 100644 --- a/src/httpserver/string_response.hpp +++ b/src/httpserver/string_response.hpp @@ -22,8 +22,8 @@ #error "Only or can be included directly." #endif -#ifndef _STRING_RESPONSE_HPP_ -#define _STRING_RESPONSE_HPP_ +#ifndef SRC_HTTPSERVER_STRING_RESPONSE_HPP_ +#define SRC_HTTPSERVER_STRING_RESPONSE_HPP_ #include #include @@ -32,37 +32,32 @@ struct MHD_Response; -namespace httpserver -{ +namespace httpserver { -class string_response : public http_response -{ - public: - string_response() = default; +class string_response : public http_response { + public: + string_response() = default; - explicit string_response( - std::string content, - int response_code = http::http_utils::http_ok, - const std::string& content_type = http::http_utils::text_plain - ): - http_response(response_code, content_type), - content(std::move(content)) - { - } + explicit string_response( + std::string content, + int response_code = http::http_utils::http_ok, + const std::string& content_type = http::http_utils::text_plain): + http_response(response_code, content_type), + content(std::move(content)) { } - string_response(const string_response& other) = default; - string_response(string_response&& other) noexcept = default; + string_response(const string_response& other) = default; + string_response(string_response&& other) noexcept = default; - string_response& operator=(const string_response& b) = default; - string_response& operator=(string_response&& b) = default; + string_response& operator=(const string_response& b) = default; + string_response& operator=(string_response&& b) = default; - ~string_response() = default; + ~string_response() = default; - MHD_Response* get_raw_response(); + MHD_Response* get_raw_response(); - private: - std::string content = ""; + private: + std::string content = ""; }; -} -#endif // _STRING_RESPONSE_HPP_ +} // namespace httpserver +#endif // SRC_HTTPSERVER_STRING_RESPONSE_HPP_ diff --git a/src/httpserver/string_utilities.hpp b/src/httpserver/string_utilities.hpp index e61762ab..69fb4929 100644 --- a/src/httpserver/string_utilities.hpp +++ b/src/httpserver/string_utilities.hpp @@ -22,16 +22,15 @@ #error "Only or can be included directly." #endif -#ifndef _STRING_UTILITIES_H_ -#define _STRING_UTILITIES_H_ +#ifndef SRC_HTTPSERVER_STRING_UTILITIES_HPP_ +#define SRC_HTTPSERVER_STRING_UTILITIES_HPP_ #include #include -namespace httpserver -{ -namespace string_utilities -{ +namespace httpserver { + +namespace string_utilities { /** * Function used to convert a string to its uppercase version. @@ -41,11 +40,10 @@ namespace string_utilities **/ const std::string to_upper_copy(const std::string& str); const std::string to_lower_copy(const std::string& str); -const std::vector string_split(const std::string& s, - char sep = ' ', bool collapse = true -); -void to_upper(std::string& str); -}; -}; +const std::vector string_split(const std::string& s, char sep = ' ', bool collapse = true); -#endif +} // namespace string_utilities + +} // namespace httpserver + +#endif // SRC_HTTPSERVER_STRING_UTILITIES_HPP_ diff --git a/src/httpserver/webserver.hpp b/src/httpserver/webserver.hpp index 02d626c5..763ba86d 100644 --- a/src/httpserver/webserver.hpp +++ b/src/httpserver/webserver.hpp @@ -22,8 +22,8 @@ #error "Only or can be included directly." #endif -#ifndef _FRAMEWORK_WEBSERVER_HPP_ -#define _FRAMEWORK_WEBSERVER_HPP_ +#ifndef SRC_HTTPSERVER_WEBSERVER_HPP_ +#define SRC_HTTPSERVER_WEBSERVER_HPP_ #define NOT_FOUND_ERROR "Not Found" #define METHOD_ERROR "Method not Allowed" @@ -60,192 +60,159 @@ namespace httpserver { /** * Class representing the webserver. Main class of the apis. **/ -class webserver -{ - public: - webserver(const create_webserver& params); - /** - * Destructor of the class - **/ - ~webserver(); - /** - * Method used to start the webserver. - * This method can be blocking or not. - * @param blocking param indicating if the method is blocking or not - * @return a boolean indicating if the webserver is running or not. - **/ - bool start(bool blocking = false); - /** - * Method used to stop the webserver. - * @return true if the webserver is stopped. - **/ - bool stop(); - /** - * Method used to evaluate if the server is running or not. - * @return true if the webserver is running - **/ - bool is_running(); - /** - * Method used to register a resource with the webserver. - * @param resource The url pointing to the resource. This url could be also parametrized in the form /path/to/url/{par1}/and/{par2} - * or a regular expression. - * @param http_resource http_resource pointer to register. - * @param family boolean indicating whether the resource is registered for the endpoint and its child or not. - * @return true if the resource was registered - **/ - bool register_resource(const std::string& resource, - http_resource* res, bool family = false - ); - - void unregister_resource(const std::string& resource); - void ban_ip(const std::string& ip); - void allow_ip(const std::string& ip); - void unban_ip(const std::string& ip); - void disallow_ip(const std::string& ip); - - log_access_ptr get_access_logger() const - { - return log_access; - } - - log_error_ptr get_error_logger() const - { - return log_error; - } - - validator_ptr get_request_validator() const - { - return validator; - } - - unescaper_ptr get_unescaper() const - { - return unescaper; - } - - /** - * Method used to kill the webserver waiting for it to terminate - **/ - void sweet_kill(); - - protected: - webserver& operator=(const webserver& other); - - private: - const uint16_t port; - http::http_utils::start_method_T start_method; - const int max_threads; - const int max_connections; - const int memory_limit; - const size_t content_size_limit; - const int connection_timeout; - const int per_IP_connection_limit; - log_access_ptr log_access; - log_error_ptr log_error; - validator_ptr validator; - unescaper_ptr unescaper; - const struct sockaddr* bind_address; - /* Changed type to MHD_socket because this type will always reflect the - platform's actual socket type (e.g. SOCKET on windows, int on unixes)*/ - MHD_socket bind_socket; - const int max_thread_stack_size; - const bool use_ssl; - const bool use_ipv6; - const bool use_dual_stack; - const bool debug; - const bool pedantic; - const std::string https_mem_key; - const std::string https_mem_cert; - const std::string https_mem_trust; - const std::string https_priorities; - const http::http_utils::cred_type_T cred_type; - const std::string digest_auth_random; - const int nonce_nc_size; - bool running; - const http::http_utils::policy_T default_policy; - const bool basic_auth_enabled; - const bool digest_auth_enabled; - const bool regex_checking; - const bool ban_system_enabled; - const bool post_process_enabled; - const bool deferred_enabled; - bool single_resource; - bool tcp_nodelay; - pthread_mutex_t mutexwait; - pthread_cond_t mutexcond; - render_ptr not_found_resource; - render_ptr method_not_allowed_resource; - render_ptr internal_error_resource; - std::map registered_resources; - std::map registered_resources_str; - - std::set bans; - std::set allowances; - - struct MHD_Daemon* daemon; - - const std::shared_ptr method_not_allowed_page(details::modded_request* mr) const; - const std::shared_ptr internal_error_page(details::modded_request* mr, bool force_our = false) const; - const std::shared_ptr not_found_page(details::modded_request* mr) const; - - static void request_completed(void *cls, - struct MHD_Connection *connection, void **con_cls, - enum MHD_RequestTerminationCode toe - ); - - static MHD_Result answer_to_connection - ( - void* cls, MHD_Connection* connection, - const char* url, const char* method, - const char* version, const char* upload_data, - size_t* upload_data_size, void** con_cls - ); - static MHD_Result post_iterator - ( - void *cls, - enum MHD_ValueKind kind, - const char *key, - const char *filename, - const char *content_type, - const char *transfer_encoding, - const char *data, uint64_t off, size_t size - ); - static void upgrade_handler - ( - void *cls, - struct MHD_Connection* connection, - void **con_cls, int upgrade_socket - ); - - MHD_Result requests_answer_first_step(MHD_Connection* connection, - struct details::modded_request* mr - ); - - MHD_Result requests_answer_second_step(MHD_Connection* connection, - const char* method, const char* version, const char* upload_data, - size_t* upload_data_size, struct details::modded_request* mr - ); - - MHD_Result finalize_answer(MHD_Connection* connection, - struct details::modded_request* mr, const char* method - ); - - MHD_Result complete_request(MHD_Connection* connection, - struct details::modded_request* mr, - const char* version, const char* method - ); - - friend MHD_Result policy_callback (void *cls, - const struct sockaddr* addr, socklen_t addrlen - ); - friend void error_log(void* cls, const char* fmt, va_list ap); - friend void access_log(webserver* cls, std::string uri); - friend void* uri_log(void* cls, const char* uri); - friend size_t unescaper_func(void * cls, - struct MHD_Connection *c, char *s - ); - friend class http_response; +class webserver { + public: + // Keeping this non explicit on purpose to easy construction through builder class. + webserver(const create_webserver& params); // NOLINT(runtime/explicit) + /** + * Destructor of the class + **/ + ~webserver(); + /** + * Method used to start the webserver. + * This method can be blocking or not. + * @param blocking param indicating if the method is blocking or not + * @return a boolean indicating if the webserver is running or not. + **/ + bool start(bool blocking = false); + /** + * Method used to stop the webserver. + * @return true if the webserver is stopped. + **/ + bool stop(); + /** + * Method used to evaluate if the server is running or not. + * @return true if the webserver is running + **/ + bool is_running(); + /** + * Method used to register a resource with the webserver. + * @param resource The url pointing to the resource. This url could be also parametrized in the form /path/to/url/{par1}/and/{par2} + * or a regular expression. + * @param http_resource http_resource pointer to register. + * @param family boolean indicating whether the resource is registered for the endpoint and its child or not. + * @return true if the resource was registered + **/ + bool register_resource(const std::string& resource, http_resource* res, bool family = false); + + void unregister_resource(const std::string& resource); + void ban_ip(const std::string& ip); + void allow_ip(const std::string& ip); + void unban_ip(const std::string& ip); + void disallow_ip(const std::string& ip); + + log_access_ptr get_access_logger() const { + return log_access; + } + + log_error_ptr get_error_logger() const { + return log_error; + } + + validator_ptr get_request_validator() const { + return validator; + } + + unescaper_ptr get_unescaper() const { + return unescaper; + } + + /** + * Method used to kill the webserver waiting for it to terminate + **/ + void sweet_kill(); + + protected: + webserver& operator=(const webserver& other); + + private: + const uint16_t port; + http::http_utils::start_method_T start_method; + const int max_threads; + const int max_connections; + const int memory_limit; + const size_t content_size_limit; + const int connection_timeout; + const int per_IP_connection_limit; + log_access_ptr log_access; + log_error_ptr log_error; + validator_ptr validator; + unescaper_ptr unescaper; + const struct sockaddr* bind_address; + /* Changed type to MHD_socket because this type will always reflect the + platform's actual socket type (e.g. SOCKET on windows, int on unixes)*/ + MHD_socket bind_socket; + const int max_thread_stack_size; + const bool use_ssl; + const bool use_ipv6; + const bool use_dual_stack; + const bool debug; + const bool pedantic; + const std::string https_mem_key; + const std::string https_mem_cert; + const std::string https_mem_trust; + const std::string https_priorities; + const http::http_utils::cred_type_T cred_type; + const std::string digest_auth_random; + const int nonce_nc_size; + bool running; + const http::http_utils::policy_T default_policy; + const bool basic_auth_enabled; + const bool digest_auth_enabled; + const bool regex_checking; + const bool ban_system_enabled; + const bool post_process_enabled; + const bool deferred_enabled; + bool single_resource; + bool tcp_nodelay; + pthread_mutex_t mutexwait; + pthread_cond_t mutexcond; + render_ptr not_found_resource; + render_ptr method_not_allowed_resource; + render_ptr internal_error_resource; + std::map registered_resources; + std::map registered_resources_str; + + std::set bans; + std::set allowances; + + struct MHD_Daemon* daemon; + + const std::shared_ptr method_not_allowed_page(details::modded_request* mr) const; + const std::shared_ptr internal_error_page(details::modded_request* mr, bool force_our = false) const; + const std::shared_ptr not_found_page(details::modded_request* mr) const; + + static void request_completed(void *cls, + struct MHD_Connection *connection, void **con_cls, + enum MHD_RequestTerminationCode toe); + + static MHD_Result answer_to_connection(void* cls, MHD_Connection* connection, const char* url, + const char* method, const char* version, const char* upload_data, + size_t* upload_data_size, void** con_cls); + + static MHD_Result post_iterator(void *cls, enum MHD_ValueKind kind, const char *key, + const char *filename, const char *content_type, const char *transfer_encoding, + const char *data, uint64_t off, size_t size); + + static void upgrade_handler(void *cls, struct MHD_Connection* connection, void **con_cls, int upgrade_socket); + + MHD_Result requests_answer_first_step(MHD_Connection* connection, struct details::modded_request* mr); + + MHD_Result requests_answer_second_step(MHD_Connection* connection, + const char* method, const char* version, const char* upload_data, + size_t* upload_data_size, struct details::modded_request* mr); + + MHD_Result finalize_answer(MHD_Connection* connection, struct details::modded_request* mr, const char* method); + + MHD_Result complete_request(MHD_Connection* connection, struct details::modded_request* mr, const char* version, const char* method); + + friend MHD_Result policy_callback(void *cls, const struct sockaddr* addr, socklen_t addrlen); + friend void error_log(void* cls, const char* fmt, va_list ap); + friend void access_log(webserver* cls, std::string uri); + friend void* uri_log(void* cls, const char* uri); + friend size_t unescaper_func(void * cls, struct MHD_Connection *c, char *s); + friend class http_response; }; -}; -#endif //_FRAMEWORK_WEBSERVER_HPP__ +} // namespace httpserver +#endif // SRC_HTTPSERVER_WEBSERVER_HPP_ diff --git a/src/string_response.cpp b/src/string_response.cpp index dec3c66a..df611fda 100644 --- a/src/string_response.cpp +++ b/src/string_response.cpp @@ -25,19 +25,12 @@ struct MHD_Response; -using namespace std; +namespace httpserver { -namespace httpserver -{ - -MHD_Response* string_response::get_raw_response() -{ +MHD_Response* string_response::get_raw_response() { size_t size = &(*content.end()) - &(*content.begin()); - return MHD_create_response_from_buffer( - size, - (void*) content.c_str(), - MHD_RESPMEM_PERSISTENT - ); + // Need to use a const cast here to satisfy MHD interface that requires a void* + return MHD_create_response_from_buffer(size, reinterpret_cast(const_cast(content.c_str())), MHD_RESPMEM_PERSISTENT); } -} +} // namespace httpserver diff --git a/src/string_utilities.cpp b/src/string_utilities.cpp index a3937f2e..e06234a7 100644 --- a/src/string_utilities.cpp +++ b/src/string_utilities.cpp @@ -26,60 +26,34 @@ #include #include -namespace httpserver -{ -namespace string_utilities -{ +namespace httpserver { +namespace string_utilities { -const std::string to_upper_copy(const std::string& str) -{ +const std::string to_upper_copy(const std::string& str) { std::string result = str; - std::transform(result.begin(), - result.end(), - result.begin(), - (int(*)(int)) std::toupper - ); + std::transform(result.begin(), result.end(), result.begin(), (int(*)(int)) std::toupper); return result; } -void to_upper(std::string& str) -{ - std::transform(str.begin(), - str.end(), - str.begin(), - (int(*)(int)) std::toupper - ); -} - -const std::string to_lower_copy(const std::string& str) -{ +const std::string to_lower_copy(const std::string& str) { std::string result = str; - std::transform(result.begin(), - result.end(), - result.begin(), - (int(*)(int)) std::tolower - ); + std::transform(result.begin(), result.end(), result.begin(), (int(*)(int)) std::tolower); return result; } -const std::vector string_split( - const std::string& s, - char sep, - bool collapse -) -{ +const std::vector string_split(const std::string& s, char sep, bool collapse) { std::vector result; std::istringstream buf(s); - for(std::string token; getline(buf, token, sep); ) - { - if((collapse && token != "") || !collapse) + for (std::string token; getline(buf, token, sep); ) { + if ((collapse && token != "") || !collapse) { result.push_back(token); + } } return result; } -}; -}; +} // namespace string_utilities +} // namespace httpserver diff --git a/src/webserver.cpp b/src/webserver.cpp index 8de4a9e7..7d50fe00 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -20,14 +20,11 @@ #include "httpserver/webserver.hpp" -#if defined(_WIN32) && ! defined(__CYGWIN__) +#if defined(_WIN32) && !defined(__CYGWIN__) #include #include #define _WINDOWS #else -#if defined(__FreeBSD__) -#include -#endif #if defined(__CYGWIN__) #include #endif @@ -51,7 +48,6 @@ #include #include -#include "gettext.h" #include "httpserver/create_webserver.hpp" #include "httpserver/details/http_endpoint.hpp" #include "httpserver/details/modded_request.hpp" @@ -73,58 +69,54 @@ struct MHD_Connection; typedef int MHD_Result; #endif -using namespace std; +using std::string; +using std::pair; +using std::vector; +using std::map; +using std::set; -namespace httpserver -{ +using httpserver::http::http_utils; +using httpserver::http::ip_representation; +using httpserver::http::base_unescaper; -using namespace http; +namespace httpserver { -MHD_Result policy_callback (void *, const struct sockaddr*, socklen_t); +MHD_Result policy_callback(void *, const struct sockaddr*, socklen_t); void error_log(void*, const char*, va_list); void* uri_log(void*, const char*); void access_log(webserver*, string); size_t unescaper_func(void*, struct MHD_Connection*, char*); -struct compare_value -{ - bool operator() (const std::pair& left, - const std::pair& right - ) const - { +struct compare_value { + bool operator() (const std::pair& left, const std::pair& right) const { return left.second < right.second; } }; #if !defined(_WIN32) && !defined(__MINGW32__) && !defined(__CYGWIN__) -static void catcher (int sig) -{ -} +static void catcher(int) { } #endif -static void ignore_sigpipe () -{ -//Mingw doesn't implement SIGPIPE +static void ignore_sigpipe() { +// Mingw doesn't implement SIGPIPE #if !defined(_WIN32) && !defined(__MINGW32__) && !defined(__CYGWIN__) struct sigaction oldsig; struct sigaction sig; sig.sa_handler = &catcher; - sigemptyset (&sig.sa_mask); + sigemptyset(&sig.sa_mask); #ifdef SA_INTERRUPT sig.sa_flags = SA_INTERRUPT; /* SunOS */ -#else //SA_INTERRUPT +#else // SA_INTERRUPT sig.sa_flags = SA_RESTART; -#endif //SA_INTERRUPTT - if (0 != sigaction (SIGPIPE, &sig, &oldsig)) - fprintf (stderr, - gettext("Failed to install SIGPIPE handler: %s\n"), - strerror (errno) - ); +#endif // SA_INTERRUPTT + if (0 != sigaction(SIGPIPE, &sig, &oldsig)) { + fprintf(stderr, "Failed to install SIGPIPE handler: %s\n", strerror(errno)); + } #endif } -//WEBSERVER +// WEBSERVER webserver::webserver(const create_webserver& params): port(params._port), start_method(params._start_method), @@ -165,32 +157,28 @@ webserver::webserver(const create_webserver& params): tcp_nodelay(params._tcp_nodelay), not_found_resource(params._not_found_resource), method_not_allowed_resource(params._method_not_allowed_resource), - internal_error_resource(params._internal_error_resource) -{ - ignore_sigpipe(); - pthread_mutex_init(&mutexwait, NULL); - pthread_cond_init(&mutexcond, NULL); + internal_error_resource(params._internal_error_resource) { + ignore_sigpipe(); + pthread_mutex_init(&mutexwait, NULL); + pthread_cond_init(&mutexcond, NULL); } -webserver::~webserver() -{ +webserver::~webserver() { stop(); pthread_mutex_destroy(&mutexwait); pthread_cond_destroy(&mutexcond); } -void webserver::sweet_kill() -{ +void webserver::sweet_kill() { stop(); } -void webserver::request_completed ( - void *cls, - struct MHD_Connection *connection, - void **con_cls, - enum MHD_RequestTerminationCode toe -) -{ +void webserver::request_completed(void *cls, struct MHD_Connection *connection, void **con_cls, enum MHD_RequestTerminationCode toe) { + // These parameters are passed to respect the MHD interface, but are not needed here. + std::ignore = cls; + std::ignore = connection; + std::ignore = toe; + details::modded_request* mr = static_cast(*con_cls); if (mr == 0x0) return; @@ -198,146 +186,142 @@ void webserver::request_completed ( mr = 0x0; } -bool webserver::register_resource(const std::string& resource, http_resource* hrm, bool family) -{ - if (single_resource && ((resource != "" && resource != "/") || !family)) - { +bool webserver::register_resource(const std::string& resource, http_resource* hrm, bool family) { + if (single_resource && ((resource != "" && resource != "/") || !family)) { throw std::invalid_argument("The resource should be '' or '/' and be marked as family when using a single_resource server"); } details::http_endpoint idx(resource, family, true, regex_checking); - pair::iterator, bool> result = registered_resources.insert( - map::value_type(idx, hrm) - ); + pair::iterator, bool> result = registered_resources.insert(map::value_type(idx, hrm)); - if(result.second) - { - registered_resources_str.insert( - pair(idx.get_url_complete(), result.first->second) - ); + if (result.second) { + registered_resources_str.insert(pair(idx.get_url_complete(), result.first->second)); } return result.second; } -bool webserver::start(bool blocking) -{ - +bool webserver::start(bool blocking) { struct { - MHD_OptionItem operator ()( - enum MHD_OPTION opt, - intptr_t val, - void *ptr = 0 - ) - { + MHD_OptionItem operator ()(enum MHD_OPTION opt, intptr_t val, void *ptr = 0) { MHD_OptionItem x = {opt, val, ptr}; return x; } } gen; vector iov; - iov.push_back(gen(MHD_OPTION_NOTIFY_COMPLETED, - (intptr_t) &request_completed, - NULL - )); + iov.push_back(gen(MHD_OPTION_NOTIFY_COMPLETED, (intptr_t) &request_completed, NULL)); iov.push_back(gen(MHD_OPTION_URI_LOG_CALLBACK, (intptr_t) &uri_log, this)); iov.push_back(gen(MHD_OPTION_EXTERNAL_LOGGER, (intptr_t) &error_log, this)); - iov.push_back(gen(MHD_OPTION_UNESCAPE_CALLBACK, - (intptr_t) &unescaper_func, - this) - ); + iov.push_back(gen(MHD_OPTION_UNESCAPE_CALLBACK, (intptr_t) &unescaper_func, this)); iov.push_back(gen(MHD_OPTION_CONNECTION_TIMEOUT, connection_timeout)); - if(bind_socket != 0) + if (bind_socket != 0) { iov.push_back(gen(MHD_OPTION_LISTEN_SOCKET, bind_socket)); - if(start_method == http_utils::THREAD_PER_CONNECTION && (max_threads != 0 || max_thread_stack_size != 0)) - { + } + + if (start_method == http_utils::THREAD_PER_CONNECTION && (max_threads != 0 || max_thread_stack_size != 0)) { throw std::invalid_argument("Cannot specify maximum number of threads when using a thread per connection"); } - if(max_threads != 0) + if (max_threads != 0) { iov.push_back(gen(MHD_OPTION_THREAD_POOL_SIZE, max_threads)); - if(max_connections != 0) + } + + if (max_connections != 0) { iov.push_back(gen(MHD_OPTION_CONNECTION_LIMIT, max_connections)); - if(memory_limit != 0) + } + + if (memory_limit != 0) { iov.push_back(gen(MHD_OPTION_CONNECTION_MEMORY_LIMIT, memory_limit)); - if(per_IP_connection_limit != 0) - iov.push_back(gen(MHD_OPTION_PER_IP_CONNECTION_LIMIT, - per_IP_connection_limit) - ); - if(max_thread_stack_size != 0) + } + + if (per_IP_connection_limit != 0) { + iov.push_back(gen(MHD_OPTION_PER_IP_CONNECTION_LIMIT, per_IP_connection_limit)); + } + + if (max_thread_stack_size != 0) { iov.push_back(gen(MHD_OPTION_THREAD_STACK_SIZE, max_thread_stack_size)); - if(nonce_nc_size != 0) + } + + if (nonce_nc_size != 0) { iov.push_back(gen(MHD_OPTION_NONCE_NC_SIZE, nonce_nc_size)); - if(use_ssl) - iov.push_back(gen(MHD_OPTION_HTTPS_MEM_KEY, - 0, - (void*)https_mem_key.c_str()) - ); - if(use_ssl) - iov.push_back(gen(MHD_OPTION_HTTPS_MEM_CERT, - 0, - (void*)https_mem_cert.c_str()) - ); - if(https_mem_trust != "" && use_ssl) - iov.push_back(gen(MHD_OPTION_HTTPS_MEM_TRUST, - 0, - (void*)https_mem_trust.c_str()) - ); - if(https_priorities != "" && use_ssl) - iov.push_back(gen(MHD_OPTION_HTTPS_PRIORITIES, - 0, - (void*)https_priorities.c_str()) - ); - if(digest_auth_random != "") - iov.push_back(gen(MHD_OPTION_DIGEST_AUTH_RANDOM, - digest_auth_random.size(), - (char*)digest_auth_random.c_str()) - ); + } + + if (use_ssl) { + // Need for const_cast to respect MHD interface that needs a void* + iov.push_back(gen(MHD_OPTION_HTTPS_MEM_KEY, 0, reinterpret_cast(const_cast(https_mem_key.c_str())))); + } + + if (use_ssl) { + // Need for const_cast to respect MHD interface that needs a void* + iov.push_back(gen(MHD_OPTION_HTTPS_MEM_CERT, 0, reinterpret_cast(const_cast(https_mem_cert.c_str())))); + } + + if (https_mem_trust != "" && use_ssl) { + // Need for const_cast to respect MHD interface that needs a void* + iov.push_back(gen(MHD_OPTION_HTTPS_MEM_TRUST, 0, reinterpret_cast(const_cast(https_mem_trust.c_str())))); + } + + if (https_priorities != "" && use_ssl) { + // Need for const_cast to respect MHD interface that needs a void* + iov.push_back(gen(MHD_OPTION_HTTPS_PRIORITIES, 0, reinterpret_cast(const_cast(https_priorities.c_str())))); + } + + if (digest_auth_random != "") { + // Need for const_cast to respect MHD interface that needs a char* + iov.push_back(gen(MHD_OPTION_DIGEST_AUTH_RANDOM, digest_auth_random.size(), const_cast(digest_auth_random.c_str()))); + } + #ifdef HAVE_GNUTLS - if(cred_type != http_utils::NONE) + if (cred_type != http_utils::NONE) { iov.push_back(gen(MHD_OPTION_HTTPS_CRED_TYPE, cred_type)); -#endif //HAVE_GNUTLS + } +#endif // HAVE_GNUTLS - iov.push_back(gen(MHD_OPTION_END, 0, NULL )); + iov.push_back(gen(MHD_OPTION_END, 0, NULL)); int start_conf = start_method; - if(use_ssl) + + if (use_ssl) { start_conf |= MHD_USE_SSL; - if(use_ipv6) + } + + if (use_ipv6) { start_conf |= MHD_USE_IPv6; - if(use_dual_stack) + } + + if (use_dual_stack) { start_conf |= MHD_USE_DUAL_STACK; - if(debug) + } + + if (debug) { start_conf |= MHD_USE_DEBUG; - if(pedantic) + } + if (pedantic) { start_conf |= MHD_USE_PEDANTIC_CHECKS; - if(deferred_enabled) + } + + if (deferred_enabled) { start_conf |= MHD_USE_SUSPEND_RESUME; + } #ifdef USE_FASTOPEN start_conf |= MHD_USE_TCP_FASTOPEN; #endif daemon = NULL; - if(bind_address == 0x0) { - daemon = MHD_start_daemon - ( - start_conf, port, &policy_callback, this, + if (bind_address == 0x0) { + daemon = MHD_start_daemon(start_conf, port, &policy_callback, this, &answer_to_connection, this, MHD_OPTION_ARRAY, - &iov[0], MHD_OPTION_END - ); + &iov[0], MHD_OPTION_END); } else { - daemon = MHD_start_daemon - ( - start_conf, 1, &policy_callback, this, + daemon = MHD_start_daemon(start_conf, 1, &policy_callback, this, &answer_to_connection, this, MHD_OPTION_ARRAY, - &iov[0], MHD_OPTION_SOCK_ADDR, bind_address, MHD_OPTION_END - ); + &iov[0], MHD_OPTION_SOCK_ADDR, bind_address, MHD_OPTION_END); } - if(daemon == NULL) - { + if (daemon == NULL) { throw std::invalid_argument("Unable to connect daemon to port: " + std::to_string(port)); } @@ -345,25 +329,23 @@ bool webserver::start(bool blocking) running = true; - if(blocking) - { + if (blocking) { pthread_mutex_lock(&mutexwait); - while(blocking && running) + while (blocking && running) { pthread_cond_wait(&mutexcond, &mutexwait); + } pthread_mutex_unlock(&mutexwait); value_onclose = true; } return value_onclose; } -bool webserver::is_running() -{ +bool webserver::is_running() { return running; } -bool webserver::stop() -{ - if(!running) return false; +bool webserver::stop() { + if (!running) return false; pthread_mutex_lock(&mutexwait); running = false; @@ -377,8 +359,7 @@ bool webserver::stop() return true; } -void webserver::unregister_resource(const string& resource) -{ +void webserver::unregister_resource(const string& resource) { // family does not matter - it just checks the url_normalized anyhow details::http_endpoint he(resource, false, true, regex_checking); registered_resources.erase(he); @@ -386,82 +367,81 @@ void webserver::unregister_resource(const string& resource) registered_resources_str.erase(he.get_url_complete()); } -void webserver::ban_ip(const string& ip) -{ +void webserver::ban_ip(const string& ip) { ip_representation t_ip(ip); set::iterator it = bans.find(t_ip); - if(it != bans.end() && (t_ip.weight() < (*it).weight())) - { + if (it != bans.end() && (t_ip.weight() < (*it).weight())) { bans.erase(it); bans.insert(t_ip); - } - else + } else { bans.insert(t_ip); + } } -void webserver::allow_ip(const string& ip) -{ +void webserver::allow_ip(const string& ip) { ip_representation t_ip(ip); set::iterator it = allowances.find(t_ip); - if(it != allowances.end() && (t_ip.weight() < (*it).weight())) - { + if (it != allowances.end() && (t_ip.weight() < (*it).weight())) { allowances.erase(it); allowances.insert(t_ip); - } - else + } else { allowances.insert(t_ip); + } } -void webserver::unban_ip(const string& ip) -{ - bans.erase(ip); +void webserver::unban_ip(const string& ip) { + bans.erase(ip_representation(ip)); } -void webserver::disallow_ip(const string& ip) -{ - allowances.erase(ip); +void webserver::disallow_ip(const string& ip) { + allowances.erase(ip_representation(ip)); } -MHD_Result policy_callback (void *cls, const struct sockaddr* addr, socklen_t addrlen) -{ - if(!(static_cast(cls))->ban_system_enabled) return MHD_YES; - - if((((static_cast(cls))->default_policy == http_utils::ACCEPT) && - ((static_cast(cls))->bans.count(addr)) && - (!(static_cast(cls))->allowances.count(addr)) - ) || - (((static_cast(cls))->default_policy == http_utils::REJECT) - && ((!(static_cast(cls))->allowances.count(addr)) || - ((static_cast(cls))->bans.count(addr))) - )) - { +MHD_Result policy_callback(void *cls, const struct sockaddr* addr, socklen_t addrlen) { + // Parameter needed to respect MHD interface, but not needed here. + std::ignore = addrlen; + + if (!(static_cast(cls))->ban_system_enabled) return MHD_YES; + + if ((((static_cast(cls))->default_policy == http_utils::ACCEPT) && + ((static_cast(cls))->bans.count(ip_representation(addr))) && + (!(static_cast(cls))->allowances.count(ip_representation(addr)))) || + (((static_cast(cls))->default_policy == http_utils::REJECT) && + ((!(static_cast(cls))->allowances.count(ip_representation(addr))) || + ((static_cast(cls))->bans.count(ip_representation(addr)))))) { return MHD_NO; } return MHD_YES; } -void* uri_log(void* cls, const char* uri) -{ +void* uri_log(void* cls, const char* uri) { + // Parameter needed to respect MHD interface, but not needed here. + std::ignore = cls; + struct details::modded_request* mr = new details::modded_request(); mr->complete_uri = new string(uri); mr->second = false; - return ((void*)mr); + return reinterpret_cast(mr); } -void error_log(void* cls, const char* fmt, va_list ap) -{ +void error_log(void* cls, const char* fmt, va_list ap) { + // Parameter needed to respect MHD interface, but not needed here. + std::ignore = ap; + webserver* dws = static_cast(cls); - if(dws->log_error != 0x0) dws->log_error(fmt); + if (dws->log_error != 0x0) dws->log_error(fmt); } -void access_log(webserver* dws, string uri) -{ - if(dws->log_access != 0x0) dws->log_access(uri); +void access_log(webserver* dws, string uri) { + if (dws->log_access != 0x0) dws->log_access(uri); } -size_t unescaper_func(void * cls, struct MHD_Connection *c, char *s) -{ +size_t unescaper_func(void * cls, struct MHD_Connection *c, char *s) { + // Parameter needed to respect MHD interface, but not needed here. + std::ignore = cls; + std::ignore = c; + // THIS IS USED TO AVOID AN UNESCAPING OF URL BEFORE THE ANSWER. // IT IS DUE TO A BOGUS ON libmicrohttpd (V0.99) THAT PRODUCING A // STRING CONTAINING '\0' AFTER AN UNESCAPING, IS UNABLE TO PARSE @@ -469,126 +449,84 @@ size_t unescaper_func(void * cls, struct MHD_Connection *c, char *s) return std::string(s).size(); } -MHD_Result webserver::post_iterator (void *cls, enum MHD_ValueKind kind, - const char *key, - const char *filename, - const char *content_type, - const char *transfer_encoding, - const char *data, uint64_t off, size_t size - ) -{ +MHD_Result webserver::post_iterator(void *cls, enum MHD_ValueKind kind, + const char *key, const char *filename, const char *content_type, + const char *transfer_encoding, const char *data, uint64_t off, size_t size) { + // Parameter needed to respect MHD interface, but not needed here. + std::ignore = kind; + std::ignore = filename; + std::ignore = content_type; + std::ignore = transfer_encoding; + std::ignore = off; + struct details::modded_request* mr = (struct details::modded_request*) cls; mr->dhr->set_arg(key, mr->dhr->get_arg(key) + std::string(data, size)); return MHD_YES; } -void webserver::upgrade_handler (void *cls, struct MHD_Connection* connection, - void **con_cls, int upgrade_socket) -{ +void webserver::upgrade_handler(void *cls, struct MHD_Connection* connection, void **con_cls, int upgrade_socket) { + std::ignore = cls; + std::ignore = connection; + std::ignore = con_cls; + std::ignore = upgrade_socket; } -const std::shared_ptr webserver::not_found_page(details::modded_request* mr) const -{ - if(not_found_resource != 0x0) - { +const std::shared_ptr webserver::not_found_page(details::modded_request* mr) const { + if (not_found_resource != 0x0) { return not_found_resource(*mr->dhr); - } - else - { + } else { return std::shared_ptr(new string_response(NOT_FOUND_ERROR, http_utils::http_not_found)); } } -const std::shared_ptr webserver::method_not_allowed_page(details::modded_request* mr) const -{ - if(method_not_allowed_resource != 0x0) - { +const std::shared_ptr webserver::method_not_allowed_page(details::modded_request* mr) const { + if (method_not_allowed_resource != 0x0) { return method_not_allowed_resource(*mr->dhr); - } - else - { + } else { return std::shared_ptr(new string_response(METHOD_ERROR, http_utils::http_method_not_allowed)); } } -const std::shared_ptr webserver::internal_error_page(details::modded_request* mr, bool force_our) const -{ - if(internal_error_resource != 0x0 && !force_our) - { +const std::shared_ptr webserver::internal_error_page(details::modded_request* mr, bool force_our) const { + if (internal_error_resource != 0x0 && !force_our) { return internal_error_resource(*mr->dhr); - } - else - { + } else { return std::shared_ptr(new string_response(GENERIC_ERROR, http_utils::http_internal_server_error, "text/plain")); } } -MHD_Result webserver::requests_answer_first_step( - MHD_Connection* connection, - struct details::modded_request* mr -) -{ +MHD_Result webserver::requests_answer_first_step(MHD_Connection* connection, struct details::modded_request* mr) { mr->second = true; mr->dhr = new http_request(connection, unescaper); - if (!mr->has_body) - { + if (!mr->has_body) { return MHD_YES; } mr->dhr->set_content_size_limit(content_size_limit); - const char *encoding = MHD_lookup_connection_value ( - connection, - MHD_HEADER_KIND, - http_utils::http_header_content_type.c_str() - ); - - if ( post_process_enabled && - ( - 0x0 != encoding && - ((0 == strncasecmp ( - http_utils::http_post_encoding_form_urlencoded.c_str(), - encoding, - http_utils::http_post_encoding_form_urlencoded.size() - ) - ) - || (0 == strncasecmp ( - http_utils::http_post_encoding_multipart_formdata.c_str(), - encoding, - http_utils::http_post_encoding_multipart_formdata.size() - ))) - ) - ) - { - const size_t post_memory_limit (32*1024); // Same as #MHD_POOL_SIZE_DEFAULT - mr->pp = MHD_create_post_processor ( - connection, - post_memory_limit, - &post_iterator, - mr - ); - } - else - { + const char *encoding = MHD_lookup_connection_value(connection, MHD_HEADER_KIND, http_utils::http_header_content_type); + + if (post_process_enabled && + (0x0 != encoding && + ((0 == strncasecmp(http_utils::http_post_encoding_form_urlencoded, encoding, strlen(http_utils::http_post_encoding_form_urlencoded))) || + (0 == strncasecmp(http_utils::http_post_encoding_multipart_formdata, encoding, strlen(http_utils::http_post_encoding_multipart_formdata)))))) { + const size_t post_memory_limit(32 * 1024); // Same as #MHD_POOL_SIZE_DEFAULT + mr->pp = MHD_create_post_processor(connection, post_memory_limit, &post_iterator, mr); + } else { mr->pp = NULL; } return MHD_YES; } -MHD_Result webserver::requests_answer_second_step( - MHD_Connection* connection, const char* method, - const char* version, const char* upload_data, - size_t* upload_data_size, struct details::modded_request* mr -) -{ +MHD_Result webserver::requests_answer_second_step(MHD_Connection* connection, const char* method, + const char* version, const char* upload_data, + size_t* upload_data_size, struct details::modded_request* mr) { if (0 == *upload_data_size) return complete_request(connection, mr, version, method); - if (mr->has_body) - { - + if (mr->has_body) { #ifdef DEBUG - cout << "Writing content: " << upload_data << endl; -#endif //DEBUG + std::cout << "Writing content: " << upload_data << std::endl; +#endif // DEBUG mr->dhr->grow_content(upload_data, *upload_data_size); if (mr->pp != NULL) MHD_post_process(mr->pp, upload_data, *upload_data_size); @@ -598,12 +536,7 @@ MHD_Result webserver::requests_answer_second_step( return MHD_YES; } -MHD_Result webserver::finalize_answer( - MHD_Connection* connection, - struct details::modded_request* mr, - const char* method -) -{ +MHD_Result webserver::finalize_answer(MHD_Connection* connection, struct details::modded_request* mr, const char* method) { int to_ret = MHD_NO; map::iterator fe; @@ -612,15 +545,11 @@ MHD_Result webserver::finalize_answer( bool found = false; struct MHD_Response* raw_response; - if(!single_resource) - { + if (!single_resource) { const char* st_url = mr->standardized_url->c_str(); fe = registered_resources_str.find(st_url); - if(fe == registered_resources_str.end()) - { - if(regex_checking) - { - + if (fe == registered_resources_str.end()) { + if (regex_checking) { map::iterator found_endpoint; details::http_endpoint endpoint(st_url, false, false, false); @@ -629,14 +558,11 @@ MHD_Result webserver::finalize_answer( size_t len = 0; size_t tot_len = 0; - for(it=registered_resources.begin(); it!=registered_resources.end(); ++it) - { + for (it = registered_resources.begin(); it != registered_resources.end(); ++it) { size_t endpoint_pieces_len = (*it).first.get_url_pieces().size(); size_t endpoint_tot_len = (*it).first.get_url_complete().size(); - if(!found || endpoint_pieces_len > len || (endpoint_pieces_len == len && endpoint_tot_len > tot_len)) - { - if((*it).first.match(endpoint)) - { + if (!found || endpoint_pieces_len > len || (endpoint_pieces_len == len && endpoint_tot_len > tot_len)) { + if ((*it).first.match(endpoint)) { found = true; len = endpoint_pieces_len; tot_len = endpoint_tot_len; @@ -644,88 +570,61 @@ MHD_Result webserver::finalize_answer( } } } - if(found) - { + + if (found) { vector url_pars = found_endpoint->first.get_url_pars(); vector url_pieces = endpoint.get_url_pieces(); vector chunks = found_endpoint->first.get_chunk_positions(); - for(unsigned int i = 0; i < url_pars.size(); i++) - { + for (unsigned int i = 0; i < url_pars.size(); i++) { mr->dhr->set_arg(url_pars[i], url_pieces[chunks[i]]); } hrm = found_endpoint->second; } } - } - else - { + } else { hrm = fe->second; found = true; } - } - else - { + } else { hrm = registered_resources.begin()->second; found = true; } - if(found) - { - try - { - if(hrm->is_allowed(method)) - { - mr->dhrs = ((hrm)->*(mr->callback))(*mr->dhr); //copy in memory (move in case) - if (mr->dhrs->get_response_code() == -1) - { + if (found) { + try { + if (hrm->is_allowed(method)) { + mr->dhrs = ((hrm)->*(mr->callback))(*mr->dhr); // copy in memory (move in case) + if (mr->dhrs->get_response_code() == -1) { mr->dhrs = internal_error_page(mr); } - } - else - { + } else { mr->dhrs = method_not_allowed_page(mr); } - } - catch(const std::exception& e) - { + } catch(const std::exception& e) { mr->dhrs = internal_error_page(mr); - } - catch(...) - { + } catch(...) { mr->dhrs = internal_error_page(mr); } - } - else - { + } else { mr->dhrs = not_found_page(mr); } - try - { - try - { + try { + try { raw_response = mr->dhrs->get_raw_response(); - } - catch(const std::invalid_argument& iae) - { + } catch(const std::invalid_argument& iae) { mr->dhrs = not_found_page(mr); raw_response = mr->dhrs->get_raw_response(); - } - catch(const std::exception& e) - { + } catch(const std::exception& e) { mr->dhrs = internal_error_page(mr); raw_response = mr->dhrs->get_raw_response(); - } - catch(...) - { + } catch(...) { mr->dhrs = internal_error_page(mr); raw_response = mr->dhrs->get_raw_response(); } - } - catch(...) // catches errors in internal error page - { + } catch(...) { // catches errors in internal error page mr->dhrs = internal_error_page(mr, true); raw_response = mr->dhrs->get_raw_response(); } @@ -735,13 +634,7 @@ MHD_Result webserver::finalize_answer( return (MHD_Result) to_ret; } -MHD_Result webserver::complete_request( - MHD_Connection* connection, - struct details::modded_request* mr, - const char* version, - const char* method -) -{ +MHD_Result webserver::complete_request(MHD_Connection* connection, struct details::modded_request* mr, const char* version, const char* method) { mr->ws = this; mr->dhr->set_path(mr->standardized_url->c_str()); @@ -751,93 +644,55 @@ MHD_Result webserver::complete_request( return finalize_answer(connection, mr, method); } -MHD_Result webserver::answer_to_connection(void* cls, MHD_Connection* connection, - const char* url, const char* method, - const char* version, const char* upload_data, - size_t* upload_data_size, void** con_cls - ) -{ - struct details::modded_request* mr = - static_cast(*con_cls); - - if(mr->second != false) - { - return static_cast(cls)-> - requests_answer_second_step( - connection, - method, - version, - upload_data, - upload_data_size, - mr - ); - } - - const MHD_ConnectionInfo * conninfo = MHD_get_connection_info( - connection, - MHD_CONNECTION_INFO_CONNECTION_FD - ); - - if (static_cast(cls)->tcp_nodelay) - { +MHD_Result webserver::answer_to_connection(void* cls, MHD_Connection* connection, const char* url, const char* method, + const char* version, const char* upload_data, size_t* upload_data_size, void** con_cls) { + struct details::modded_request* mr = static_cast(*con_cls); + + if (mr->second != false) { + return static_cast(cls)->requests_answer_second_step(connection, method, version, upload_data, upload_data_size, mr); + } + + const MHD_ConnectionInfo * conninfo = MHD_get_connection_info(connection, MHD_CONNECTION_INFO_CONNECTION_FD); + + if (static_cast(cls)->tcp_nodelay) { int yes = 1; - setsockopt(conninfo->connect_fd, IPPROTO_TCP, TCP_NODELAY, (char *) &yes, sizeof(int)); + setsockopt(conninfo->connect_fd, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast(&yes), sizeof(int)); } std::string t_url = url; - base_unescaper(t_url, static_cast(cls)->unescaper); + base_unescaper(&t_url, static_cast(cls)->unescaper); mr->standardized_url = new string(http_utils::standardize_url(t_url)); mr->has_body = false; - access_log( - static_cast(cls), - *(mr->complete_uri) + " METHOD: " + method - ); + access_log(static_cast(cls), *(mr->complete_uri) + " METHOD: " + method); - if( 0 == strcasecmp(method, http_utils::http_method_get.c_str())) - { + if (0 == strcasecmp(method, http_utils::http_method_get)) { mr->callback = &http_resource::render_GET; - } - else if (0 == strcmp(method, http_utils::http_method_post.c_str())) - { + } else if (0 == strcmp(method, http_utils::http_method_post)) { mr->callback = &http_resource::render_POST; mr->has_body = true; - } - else if (0 == strcasecmp(method, http_utils::http_method_put.c_str())) - { + } else if (0 == strcasecmp(method, http_utils::http_method_put)) { mr->callback = &http_resource::render_PUT; mr->has_body = true; - } - else if (0 == strcasecmp(method,http_utils::http_method_delete.c_str())) - { + } else if (0 == strcasecmp(method, http_utils::http_method_delete)) { mr->callback = &http_resource::render_DELETE; mr->has_body = true; - } - else if (0 == strcasecmp(method, http_utils::http_method_patch.c_str())) - { + } else if (0 == strcasecmp(method, http_utils::http_method_patch)) { mr->callback = &http_resource::render_PATCH; mr->has_body = true; - } - else if (0 == strcasecmp(method, http_utils::http_method_head.c_str())) - { + } else if (0 == strcasecmp(method, http_utils::http_method_head)) { mr->callback = &http_resource::render_HEAD; - } - else if (0 ==strcasecmp(method,http_utils::http_method_connect.c_str())) - { + } else if (0 ==strcasecmp(method, http_utils::http_method_connect)) { mr->callback = &http_resource::render_CONNECT; - } - else if (0 == strcasecmp(method, http_utils::http_method_trace.c_str())) - { + } else if (0 == strcasecmp(method, http_utils::http_method_trace)) { mr->callback = &http_resource::render_TRACE; - } - else if (0 ==strcasecmp(method,http_utils::http_method_options.c_str())) - { + } else if (0 ==strcasecmp(method, http_utils::http_method_options)) { mr->callback = &http_resource::render_OPTIONS; } return static_cast(cls)->requests_answer_first_step(connection, mr); } -}; +} // namespace httpserver diff --git a/test/CPPLINT.cfg b/test/CPPLINT.cfg new file mode 100644 index 00000000..175a3836 --- /dev/null +++ b/test/CPPLINT.cfg @@ -0,0 +1 @@ +exclude_files=littletest.hpp diff --git a/test/integ/authentication.cpp b/test/integ/authentication.cpp index da89f0a2..3ed4d993 100644 --- a/test/integ/authentication.cpp +++ b/test/integ/authentication.cpp @@ -18,7 +18,7 @@ USA */ -#if defined(_WIN32) && ! defined(__CYGWIN__) +#if defined(_WIN32) && !defined(__CYGWIN__) #define _WINDOWS #undef _WIN32_WINNT #define _WIN32_WINNT 0x600 @@ -37,55 +37,51 @@ #define MY_OPAQUE "11733b200778ce33060f31c9af70a870ba96ddd4" -using namespace std; -using namespace httpserver; - -size_t writefunc(void *ptr, size_t size, size_t nmemb, std::string *s) -{ - s->append((char*) ptr, size*nmemb); +using std::shared_ptr; +using httpserver::webserver; +using httpserver::create_webserver; +using httpserver::http_response; +using httpserver::basic_auth_fail_response; +using httpserver::digest_auth_fail_response; +using httpserver::string_response; +using httpserver::http_resource; +using httpserver::http_request; + +size_t writefunc(void *ptr, size_t size, size_t nmemb, std::string *s) { + s->append(reinterpret_cast(ptr), size*nmemb); return size*nmemb; } -class user_pass_resource : public httpserver::http_resource -{ - public: - const shared_ptr render_GET(const http_request& req) - { - if (req.get_user() != "myuser" || req.get_pass() != "mypass") - { - return shared_ptr(new basic_auth_fail_response("FAIL", "examplerealm")); - } - return shared_ptr(new string_response(req.get_user() + " " + req.get_pass(), 200, "text/plain")); - } +class user_pass_resource : public http_resource { + public: + const shared_ptr render_GET(const http_request& req) { + if (req.get_user() != "myuser" || req.get_pass() != "mypass") { + return shared_ptr(new basic_auth_fail_response("FAIL", "examplerealm")); + } + return shared_ptr(new string_response(req.get_user() + " " + req.get_pass(), 200, "text/plain")); + } }; -class digest_resource : public httpserver::http_resource -{ - public: - const shared_ptr render_GET(const http_request& req) - { - if (req.get_digested_user() == "") { - return shared_ptr(new digest_auth_fail_response("FAIL", "examplerealm", MY_OPAQUE, true)); - } - else - { - bool reload_nonce = false; - if(!req.check_digest_auth("examplerealm", "mypass", 300, reload_nonce)) - { - return shared_ptr(new digest_auth_fail_response("FAIL", "examplerealm", MY_OPAQUE, reload_nonce)); - } - } - return shared_ptr(new string_response("SUCCESS", 200, "text/plain")); - } +class digest_resource : public http_resource { + public: + const shared_ptr render_GET(const http_request& req) { + if (req.get_digested_user() == "") { + return shared_ptr(new digest_auth_fail_response("FAIL", "examplerealm", MY_OPAQUE, true)); + } else { + bool reload_nonce = false; + if (!req.check_digest_auth("examplerealm", "mypass", 300, &reload_nonce)) { + return shared_ptr(new digest_auth_fail_response("FAIL", "examplerealm", MY_OPAQUE, reload_nonce)); + } + } + return shared_ptr(new string_response("SUCCESS", 200, "text/plain")); + } }; LT_BEGIN_SUITE(authentication_suite) - void set_up() - { + void set_up() { } - void tear_down() - { + void tear_down() { } LT_END_SUITE(authentication_suite) @@ -139,9 +135,9 @@ LT_BEGIN_AUTO_TEST(authentication_suite, base_auth_fail) ws.stop(); LT_END_AUTO_TEST(base_auth_fail) -// do not run the digest auth tests on windows as curl -// appears to have problems with it. -// Will fix this separately +// do not run the digest auth tests on windows as curl +// appears to have problems with it. +// Will fix this separately #ifndef _WINDOWS LT_BEGIN_AUTO_TEST(authentication_suite, digest_auth) @@ -154,7 +150,7 @@ LT_BEGIN_AUTO_TEST(authentication_suite, digest_auth) ws.start(false); #if defined(_WINDOWS) - curl_global_init(CURL_GLOBAL_WIN32 ); + curl_global_init(CURL_GLOBAL_WIN32); #else curl_global_init(CURL_GLOBAL_ALL); #endif @@ -194,7 +190,7 @@ LT_BEGIN_AUTO_TEST(authentication_suite, digest_auth_wrong_pass) ws.start(false); #if defined(_WINDOWS) - curl_global_init(CURL_GLOBAL_WIN32 ); + curl_global_init(CURL_GLOBAL_WIN32); #else curl_global_init(CURL_GLOBAL_ALL); #endif diff --git a/test/integ/ban_system.cpp b/test/integ/ban_system.cpp index 07f3e8d8..a5a86a1a 100644 --- a/test/integ/ban_system.cpp +++ b/test/integ/ban_system.cpp @@ -26,32 +26,33 @@ #include "httpserver/http_utils.hpp" #include "littletest.hpp" -using namespace httpserver; -using namespace std; -using namespace http; - -size_t writefunc(void *ptr, size_t size, size_t nmemb, std::string *s) -{ - s->append((char*) ptr, size*nmemb); +using std::shared_ptr; + +using httpserver::webserver; +using httpserver::create_webserver; +using httpserver::http_resource; +using httpserver::http_response; +using httpserver::string_response; +using httpserver::http_request; +using httpserver::http::http_utils; + +size_t writefunc(void *ptr, size_t size, size_t nmemb, std::string *s) { + s->append(reinterpret_cast(ptr), size*nmemb); return size*nmemb; } -class ok_resource : public http_resource -{ - public: - const shared_ptr render_GET(const http_request& req) - { - return shared_ptr(new string_response("OK", 200, "text/plain")); - } +class ok_resource : public http_resource { + public: + const shared_ptr render_GET(const http_request&) { + return shared_ptr(new string_response("OK", 200, "text/plain")); + } }; LT_BEGIN_SUITE(ban_system_suite) - void set_up() - { + void set_up() { } - void tear_down() - { + void tear_down() { } LT_END_SUITE(ban_system_suite) diff --git a/test/integ/basic.cpp b/test/integ/basic.cpp index fdb503de..e340ff8a 100644 --- a/test/integ/basic.cpp +++ b/test/integ/basic.cpp @@ -27,277 +27,246 @@ #include "httpserver/string_utilities.hpp" #include "littletest.hpp" -using namespace httpserver; -using namespace std; - -std::string lorem_ipsum(" , unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam eaque ipsa, quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt, explicabo. Nemo enim ipsam voluptatem, quia voluptas sit, aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos, qui ratione voluptatem sequi nesciunt, neque porro quisquam est, qui dolorem ipsum, quia dolor sit, amet, consectetur, adipisci v'elit, sed quia non numquam eius modi tempora incidunt, ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit, qui in ea voluptate velit esse, quam nihil molestiae consequatur, vel illum, qui dolorem eum fugiat, quo voluptas nulla pariatur? [33] At vero eos et accusamus et iusto odio dignissimos ducimus, qui blanditiis praesentium voluptatum deleniti atque corrupti, quos dolores et quas molestias excepturi sint, obcaecati cupiditate non provident, similique sunt in culpa, qui officia deserunt mollitia animi, id est laborum et dolorum fuga. Et harum quidem rerum facilis est et expedita distinctio. Nam libero tempore, cum soluta nobis est eligendi optio, cumque nihil impedit, quo minus id, quod maxime placeat, facere possimus, omnis voluptas assumenda est, omnis dolor repellendus. Temporibus autem quibusdam et aut officiis debitis aut rerum necessitatibus saepe eveniet, ut et voluptates repudiandae sint et molestiae non recusandae. Itaque earum rerum hic tenetur a sapiente delectus, ut aut reiciendis voluptatibus maiores alias consequatur aut perferendis doloribus asperiores repellat. , unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam eaque ipsa, quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt, explicabo. Nemo enim ipsam voluptatem, quia voluptas sit, aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos, qui ratione voluptatem sequi nesciunt, neque porro quisquam est, qui dolorem ipsum, quia dolor sit, amet, consectetur, adipisci v'elit, sed quia non numquam eius modi tempora incidunt, ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit, qui in ea voluptate velit esse, quam nihil molestiae consequatur, vel illum, qui dolorem eum fugiat, quo voluptas nulla pariatur? [33] At vero eos et accusamus et iusto odio dignissimos ducimus, qui blanditiis praesentium voluptatum deleniti atque corrupti, quos dolores et quas molestias excepturi sint, obcaecati cupiditate non provident, similique sunt in culpa, qui officia deserunt mollitia animi, id est laborum et dolorum fuga. Et harum quidem rerum facilis est et expedita distinctio. Nam libero tempore, cum soluta nobis est eligendi optio, cumque nihil impedit, quo minus id, quod maxime placeat, facere possimus, omnis voluptas assumenda est, omnis dolor repellendus. Temporibus autem quibusdam et aut officiis debitis aut rerum necessitatibus saepe eveniet, ut et voluptates repudiandae sint et molestiae non recusandae. Itaque earum rerum hic tenetur a sapiente delectus, ut aut reiciendis voluptatibus maiores alias consequatur aut perferendis doloribus asperiores repellat. , unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam eaque ipsa, quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt, explicabo. Nemo enim ipsam voluptatem, quia voluptas sit, aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos, qui ratione voluptatem sequi nesciunt, neque porro quisquam est, qui dolorem ipsum, quia dolor sit, amet, consectetur, adipisci v'elit, sed quia non numquam eius modi tempora incidunt, ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit, qui in ea voluptate velit esse, quam nihil molestiae consequatur, vel illum, qui dolorem eum fugiat, quo voluptas nulla pariatur? [33] At vero eos et accusamus et iusto odio dignissimos ducimus, qui blanditiis praesentium voluptatum deleniti atque corrupti, quos dolores et quas molestias excepturi sint, obcaecati cupiditate non provident, similique sunt in culpa, qui officia deserunt mollitia animi, id est laborum et dolorum fuga. Et harum quidem rerum facilis est et expedita distinctio. Nam libero tempore, cum soluta nobis est eligendi optio, cumque nihil impedit, quo minus id, quod maxime placeat, facere possimus, omnis voluptas assumenda est, omnis dolor repellendus. Temporibus autem quibusdam et aut officiis debitis aut rerum necessitatibus saepe eveniet, ut et voluptates repudiandae sint et molestiae non recusandae. Itaque earum rerum hic tenetur a sapiente delectus, ut aut reiciendis voluptatibus maiores alias consequatur aut perferendis doloribus asperiores repellat. , unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam eaque ipsa, quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt, explicabo. Nemo enim ipsam voluptatem, quia voluptas sit, aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos, qui ratione voluptatem sequi nesciunt, neque porro quisquam est, qui dolorem ipsum, quia dolor sit, amet, consectetur, adipisci v'elit, sed quia non numquam eius modi tempora incidunt, ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit, qui in ea voluptate velit esse, quam nihil molestiae consequatur, vel illum, qui dolorem eum fugiat, quo voluptas nulla pariatur? [33] At vero eos et accusamus et iusto odio dignissimos ducimus, qui blanditiis praesentium voluptatum deleniti atque corrupti, quos dolores et quas molestias excepturi sint, obcaecati cupiditate non provident, similique sunt in culpa, qui officia deserunt mollitia animi, id est laborum et dolorum fuga. Et harum quidem rerum facilis est et expedita distinctio. Nam libero tempore, cum soluta nobis est eligendi optio, cumque nihil impedit, quo minus id, quod maxime placeat, facere possimus, omnis voluptas assumenda est, omnis dolor repellendus. Temporibus autem quibusdam et aut officiis debitis aut rerum necessitatibus saepe eveniet, ut et voluptates repudiandae sint et molestiae non recusandae. Itaque earum rerum hic tenetur a sapiente delectus, ut aut reiciendis voluptatibus maiores alias consequatur aut perferendis doloribus asperiores repellat. , unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam eaque ipsa, quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt, explicabo. Nemo enim ipsam voluptatem, quia voluptas sit, aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos, qui ratione voluptatem sequi nesciunt, neque porro quisquam est, qui dolorem ipsum, quia dolor sit, amet, consectetur, adipisci v'elit, sed quia non numquam eius modi tempora incidunt, ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit, qui in ea voluptate velit esse, quam nihil molestiae consequatur, vel illum, qui dolorem eum fugiat, quo voluptas nulla pariatur? [33] At vero eos et accusamus et iusto odio dignissimos ducimus, qui blanditiis praesentium voluptatum deleniti atque corrupti, quos dolores et quas molestias excepturi sint, obcaecati cupiditate non provident, similique sunt in culpa, qui officia deserunt mollitia animi, id est laborum et dolorum fuga. Et harum quidem rerum facilis est et expedita distinctio. Nam libero tempore, cum soluta nobis est eligendi optio, cumque nihil impedit, quo minus id, quod maxime placeat, facere possimus, omnis voluptas assumenda est, omnis dolor repellendus. Temporibus autem quibusdam et aut officiis debitis aut rerum necessitatibus saepe eveniet, ut et voluptates repudiandae sint et molestiae non recusandae. Itaque earum rerum hic tenetur a sapiente delectus, ut aut reiciendis voluptatibus maiores alias consequatur aut perferendis doloribus asperiores repellat."); - -size_t writefunc(void *ptr, size_t size, size_t nmemb, std::string *s) -{ - s->append((char*) ptr, size*nmemb); +using std::string; +using std::map; +using std::shared_ptr; +using std::vector; +using std::stringstream; + +using httpserver::http_resource; +using httpserver::http_request; +using httpserver::http_response; +using httpserver::string_response; +using httpserver::file_response; +using httpserver::webserver; +using httpserver::create_webserver; + +string lorem_ipsum(" , unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam eaque ipsa, quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt, explicabo. Nemo enim ipsam voluptatem, quia voluptas sit, aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos, qui ratione voluptatem sequi nesciunt, neque porro quisquam est, qui dolorem ipsum, quia dolor sit, amet, consectetur, adipisci v'elit, sed quia non numquam eius modi tempora incidunt, ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit, qui in ea voluptate velit esse, quam nihil molestiae consequatur, vel illum, qui dolorem eum fugiat, quo voluptas nulla pariatur? [33] At vero eos et accusamus et iusto odio dignissimos ducimus, qui blanditiis praesentium voluptatum deleniti atque corrupti, quos dolores et quas molestias excepturi sint, obcaecati cupiditate non provident, similique sunt in culpa, qui officia deserunt mollitia animi, id est laborum et dolorum fuga. Et harum quidem rerum facilis est et expedita distinctio. Nam libero tempore, cum soluta nobis est eligendi optio, cumque nihil impedit, quo minus id, quod maxime placeat, facere possimus, omnis voluptas assumenda est, omnis dolor repellendus. Temporibus autem quibusdam et aut officiis debitis aut rerum necessitatibus saepe eveniet, ut et voluptates repudiandae sint et molestiae non recusandae. Itaque earum rerum hic tenetur a sapiente delectus, ut aut reiciendis voluptatibus maiores alias consequatur aut perferendis doloribus asperiores repellat. , unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam eaque ipsa, quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt, explicabo. Nemo enim ipsam voluptatem, quia voluptas sit, aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos, qui ratione voluptatem sequi nesciunt, neque porro quisquam est, qui dolorem ipsum, quia dolor sit, amet, consectetur, adipisci v'elit, sed quia non numquam eius modi tempora incidunt, ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit, qui in ea voluptate velit esse, quam nihil molestiae consequatur, vel illum, qui dolorem eum fugiat, quo voluptas nulla pariatur? [33] At vero eos et accusamus et iusto odio dignissimos ducimus, qui blanditiis praesentium voluptatum deleniti atque corrupti, quos dolores et quas molestias excepturi sint, obcaecati cupiditate non provident, similique sunt in culpa, qui officia deserunt mollitia animi, id est laborum et dolorum fuga. Et harum quidem rerum facilis est et expedita distinctio. Nam libero tempore, cum soluta nobis est eligendi optio, cumque nihil impedit, quo minus id, quod maxime placeat, facere possimus, omnis voluptas assumenda est, omnis dolor repellendus. Temporibus autem quibusdam et aut officiis debitis aut rerum necessitatibus saepe eveniet, ut et voluptates repudiandae sint et molestiae non recusandae. Itaque earum rerum hic tenetur a sapiente delectus, ut aut reiciendis voluptatibus maiores alias consequatur aut perferendis doloribus asperiores repellat. , unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam eaque ipsa, quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt, explicabo. Nemo enim ipsam voluptatem, quia voluptas sit, aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos, qui ratione voluptatem sequi nesciunt, neque porro quisquam est, qui dolorem ipsum, quia dolor sit, amet, consectetur, adipisci v'elit, sed quia non numquam eius modi tempora incidunt, ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit, qui in ea voluptate velit esse, quam nihil molestiae consequatur, vel illum, qui dolorem eum fugiat, quo voluptas nulla pariatur? [33] At vero eos et accusamus et iusto odio dignissimos ducimus, qui blanditiis praesentium voluptatum deleniti atque corrupti, quos dolores et quas molestias excepturi sint, obcaecati cupiditate non provident, similique sunt in culpa, qui officia deserunt mollitia animi, id est laborum et dolorum fuga. Et harum quidem rerum facilis est et expedita distinctio. Nam libero tempore, cum soluta nobis est eligendi optio, cumque nihil impedit, quo minus id, quod maxime placeat, facere possimus, omnis voluptas assumenda est, omnis dolor repellendus. Temporibus autem quibusdam et aut officiis debitis aut rerum necessitatibus saepe eveniet, ut et voluptates repudiandae sint et molestiae non recusandae. Itaque earum rerum hic tenetur a sapiente delectus, ut aut reiciendis voluptatibus maiores alias consequatur aut perferendis doloribus asperiores repellat. , unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam eaque ipsa, quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt, explicabo. Nemo enim ipsam voluptatem, quia voluptas sit, aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos, qui ratione voluptatem sequi nesciunt, neque porro quisquam est, qui dolorem ipsum, quia dolor sit, amet, consectetur, adipisci v'elit, sed quia non numquam eius modi tempora incidunt, ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit, qui in ea voluptate velit esse, quam nihil molestiae consequatur, vel illum, qui dolorem eum fugiat, quo voluptas nulla pariatur? [33] At vero eos et accusamus et iusto odio dignissimos ducimus, qui blanditiis praesentium voluptatum deleniti atque corrupti, quos dolores et quas molestias excepturi sint, obcaecati cupiditate non provident, similique sunt in culpa, qui officia deserunt mollitia animi, id est laborum et dolorum fuga. Et harum quidem rerum facilis est et expedita distinctio. Nam libero tempore, cum soluta nobis est eligendi optio, cumque nihil impedit, quo minus id, quod maxime placeat, facere possimus, omnis voluptas assumenda est, omnis dolor repellendus. Temporibus autem quibusdam et aut officiis debitis aut rerum necessitatibus saepe eveniet, ut et voluptates repudiandae sint et molestiae non recusandae. Itaque earum rerum hic tenetur a sapiente delectus, ut aut reiciendis voluptatibus maiores alias consequatur aut perferendis doloribus asperiores repellat. , unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam eaque ipsa, quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt, explicabo. Nemo enim ipsam voluptatem, quia voluptas sit, aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos, qui ratione voluptatem sequi nesciunt, neque porro quisquam est, qui dolorem ipsum, quia dolor sit, amet, consectetur, adipisci v'elit, sed quia non numquam eius modi tempora incidunt, ut labore et dolore magnam aliquam quaerat voluptatem. Ut enim ad minima veniam, quis nostrum exercitationem ullam corporis suscipit laboriosam, nisi ut aliquid ex ea commodi consequatur? Quis autem vel eum iure reprehenderit, qui in ea voluptate velit esse, quam nihil molestiae consequatur, vel illum, qui dolorem eum fugiat, quo voluptas nulla pariatur? [33] At vero eos et accusamus et iusto odio dignissimos ducimus, qui blanditiis praesentium voluptatum deleniti atque corrupti, quos dolores et quas molestias excepturi sint, obcaecati cupiditate non provident, similique sunt in culpa, qui officia deserunt mollitia animi, id est laborum et dolorum fuga. Et harum quidem rerum facilis est et expedita distinctio. Nam libero tempore, cum soluta nobis est eligendi optio, cumque nihil impedit, quo minus id, quod maxime placeat, facere possimus, omnis voluptas assumenda est, omnis dolor repellendus. Temporibus autem quibusdam et aut officiis debitis aut rerum necessitatibus saepe eveniet, ut et voluptates repudiandae sint et molestiae non recusandae. Itaque earum rerum hic tenetur a sapiente delectus, ut aut reiciendis voluptatibus maiores alias consequatur aut perferendis doloribus asperiores repellat."); // NOLINT + +size_t writefunc(void *ptr, size_t size, size_t nmemb, string *s) { + s->append(reinterpret_cast(ptr), size*nmemb); return size*nmemb; } -size_t headerfunc(void *ptr, size_t size, size_t nmemb, map* ss) -{ - string s_ptr((char*)ptr, size*nmemb); +size_t headerfunc(void *ptr, size_t size, size_t nmemb, map* ss) { + string s_ptr(reinterpret_cast(ptr), size * nmemb); size_t pos = s_ptr.find(":"); - if(pos != string::npos) - (*ss)[s_ptr.substr(0, pos)] = - s_ptr.substr(pos + 2, s_ptr.size() - pos - 4); + if (pos != string::npos) { + (*ss)[s_ptr.substr(0, pos)] = s_ptr.substr(pos + 2, s_ptr.size() - pos - 4); + } return size*nmemb; } -class simple_resource : public http_resource -{ - public: - const shared_ptr render_GET(const http_request& req) - { - return shared_ptr(new string_response("OK", 200, "text/plain")); - } - const shared_ptr render_POST(const http_request& req) - { - return shared_ptr(new string_response(req.get_arg("arg1")+req.get_arg("arg2"), 200, "text/plain")); - } +class simple_resource : public http_resource { + public: + const shared_ptr render_GET(const http_request&) { + return shared_ptr(new string_response("OK", 200, "text/plain")); + } + const shared_ptr render_POST(const http_request& req) { + return shared_ptr(new string_response(req.get_arg("arg1")+req.get_arg("arg2"), 200, "text/plain")); + } }; -class args_resource : public http_resource -{ - public: - const shared_ptr render_GET(const http_request& req) - { - return shared_ptr(new string_response(req.get_arg("arg") + req.get_arg("arg2"), 200, "text/plain")); - } +class args_resource : public http_resource { + public: + const shared_ptr render_GET(const http_request& req) { + return shared_ptr(new string_response(req.get_arg("arg") + req.get_arg("arg2"), 200, "text/plain")); + } }; -class long_content_resource : public http_resource -{ - public: - const shared_ptr render_GET(const http_request& req) - { - return shared_ptr(new string_response(lorem_ipsum, 200, "text/plain")); - } +class long_content_resource : public http_resource { + public: + const shared_ptr render_GET(const http_request&) { + return shared_ptr(new string_response(lorem_ipsum, 200, "text/plain")); + } }; -class header_set_test_resource : public http_resource -{ - public: - const shared_ptr render_GET(const http_request& req) - { - shared_ptr hrb(new string_response("OK", 200, "text/plain")); - hrb->with_header("KEY", "VALUE"); - return hrb; - } +class header_set_test_resource : public http_resource { + public: + const shared_ptr render_GET(const http_request&) { + shared_ptr hrb(new string_response("OK", 200, "text/plain")); + hrb->with_header("KEY", "VALUE"); + return hrb; + } }; -class cookie_set_test_resource : public http_resource -{ - public: - const shared_ptr render_GET(const http_request& req) - { - shared_ptr hrb(new string_response("OK", 200, "text/plain")); - hrb->with_cookie("MyCookie", "CookieValue"); - return hrb; - } +class cookie_set_test_resource : public http_resource { + public: + const shared_ptr render_GET(const http_request&) { + shared_ptr hrb(new string_response("OK", 200, "text/plain")); + hrb->with_cookie("MyCookie", "CookieValue"); + return hrb; + } }; -class cookie_reading_resource : public http_resource -{ - public: - const shared_ptr render_GET(const http_request& req) - { - return shared_ptr(new string_response(req.get_cookie("name"), 200, "text/plain")); - } +class cookie_reading_resource : public http_resource { + public: + const shared_ptr render_GET(const http_request& req) { + return shared_ptr(new string_response(req.get_cookie("name"), 200, "text/plain")); + } }; -class header_reading_resource : public http_resource -{ - public: - const shared_ptr render_GET(const http_request& req) - { - return shared_ptr(new string_response(req.get_header("MyHeader"), 200, "text/plain")); - } +class header_reading_resource : public http_resource { + public: + const shared_ptr render_GET(const http_request& req) { + return shared_ptr(new string_response(req.get_header("MyHeader"), 200, "text/plain")); + } }; -class full_args_resource : public http_resource -{ - public: - const shared_ptr render_GET(const http_request& req) - { - return shared_ptr(new string_response(req.get_args().at("arg"), 200, "text/plain")); - } +class full_args_resource : public http_resource { + public: + const shared_ptr render_GET(const http_request& req) { + return shared_ptr(new string_response(req.get_args().at("arg"), 200, "text/plain")); + } }; -class querystring_resource : public http_resource -{ - public: - const shared_ptr render_GET(const http_request& req) - { - return shared_ptr(new string_response(req.get_querystring(), 200, "text/plain")); - } +class querystring_resource : public http_resource { + public: + const shared_ptr render_GET(const http_request& req) { + return shared_ptr(new string_response(req.get_querystring(), 200, "text/plain")); + } }; -class path_pieces_resource : public http_resource -{ - public: - const shared_ptr render_GET(const http_request& req) - { - std::stringstream ss; - for (unsigned int i = 0; i < req.get_path_pieces().size(); i++) - { - ss << req.get_path_piece(i) << ","; - } - return shared_ptr(new string_response(ss.str(), 200, "text/plain")); - } +class path_pieces_resource : public http_resource { + public: + const shared_ptr render_GET(const http_request& req) { + stringstream ss; + for (unsigned int i = 0; i < req.get_path_pieces().size(); i++) { + ss << req.get_path_piece(i) << ","; + } + return shared_ptr(new string_response(ss.str(), 200, "text/plain")); + } +}; + +class complete_test_resource : public http_resource { + public: + const shared_ptr render_GET(const http_request&) { + return shared_ptr(new string_response("OK", 200, "text/plain")); + } + + const shared_ptr render_POST(const http_request&) { + return shared_ptr(new string_response("OK", 200, "text/plain")); + } + + const shared_ptr render_PUT(const http_request&) { + return shared_ptr(new string_response("OK", 200, "text/plain")); + } + + const shared_ptr render_DELETE(const http_request&) { + return shared_ptr(new string_response("OK", 200, "text/plain")); + } + + const shared_ptr render_CONNECT(const http_request&) { + return shared_ptr(new string_response("OK", 200, "text/plain")); + } + + const shared_ptr render_PATCH(const http_request&) { + return shared_ptr(new string_response("OK", 200, "text/plain")); + } }; -class complete_test_resource : public http_resource -{ - public: - const shared_ptr render_GET(const http_request& req) - { - return shared_ptr(new string_response("OK", 200, "text/plain")); - } - const shared_ptr render_POST(const http_request& req) - { - return shared_ptr(new string_response("OK", 200, "text/plain")); - } - const shared_ptr render_PUT(const http_request& req) - { - return shared_ptr(new string_response("OK", 200, "text/plain")); - } - const shared_ptr render_DELETE(const http_request& req) - { - return shared_ptr(new string_response("OK", 200, "text/plain")); - } - const shared_ptr render_CONNECT(const http_request& req) - { - return shared_ptr(new string_response("OK", 200, "text/plain")); - } - const shared_ptr render_PATCH(const http_request& req) - { - return shared_ptr(new string_response("OK", 200, "text/plain")); - } +class only_render_resource : public http_resource { + public: + const shared_ptr render(const http_request&) { + return shared_ptr(new string_response("OK", 200, "text/plain")); + } }; -class only_render_resource : public http_resource -{ - public: - const shared_ptr render(const http_request& req) - { - return shared_ptr(new string_response("OK", 200, "text/plain")); - } +class ok_resource : public http_resource { + public: + const shared_ptr render_GET(const http_request&) { + return shared_ptr(new string_response("OK", 200, "text/plain")); + } }; -class ok_resource : public http_resource -{ - public: - const shared_ptr render_GET(const http_request& req) - { - return shared_ptr(new string_response("OK", 200, "text/plain")); - } +class nok_resource : public http_resource { + public: + const shared_ptr render_GET(const http_request&) { + return shared_ptr(new string_response("NOK", 200, "text/plain")); + } }; -class nok_resource : public http_resource -{ - public: - const shared_ptr render_GET(const http_request& req) - { - return shared_ptr(new string_response("NOK", 200, "text/plain")); - } +class no_response_resource : public http_resource { + public: + const shared_ptr render_GET(const http_request&) { + return shared_ptr(new http_response()); + } }; -class no_response_resource : public http_resource -{ - public: - const shared_ptr render_GET(const http_request& req) - { - return shared_ptr(new http_response()); - } +class file_response_resource : public http_resource { + public: + const shared_ptr render_GET(const http_request&) { + return shared_ptr(new file_response("test_content", 200, "text/plain")); + } }; -class file_response_resource : public http_resource -{ - public: - const shared_ptr render_GET(const http_request& req) - { - return shared_ptr(new file_response("test_content", 200, "text/plain")); - } +class file_response_resource_empty : public http_resource { + public: + const shared_ptr render_GET(const http_request&) { + return shared_ptr(new file_response("test_content_empty", 200, "text/plain")); + } }; -class exception_resource : public http_resource -{ - public: - const shared_ptr render_GET(const http_request& req) - { - throw std::domain_error("invalid"); - } +class exception_resource : public http_resource { + public: + const shared_ptr render_GET(const http_request&) { + throw std::domain_error("invalid"); + } }; -class error_resource : public http_resource -{ - public: - const shared_ptr render_GET(const http_request& req) - { - throw "invalid"; - } +class error_resource : public http_resource { + public: + const shared_ptr render_GET(const http_request&) { + throw "invalid"; + } }; -class print_request_resource : public http_resource -{ - public: - print_request_resource(std::stringstream* ss) - { - this->ss = ss; - } - - const shared_ptr render_GET(const http_request& req) - { - (*ss) << req; - return shared_ptr(new string_response("OK", 200, "text/plain")); - } - - private: - std::stringstream* ss; +class print_request_resource : public http_resource { + public: + explicit print_request_resource(stringstream* ss) { + this->ss = ss; + } + + const shared_ptr render_GET(const http_request& req) { + (*ss) << req; + return shared_ptr(new string_response("OK", 200, "text/plain")); + } + + private: + stringstream* ss; }; -class print_response_resource : public http_resource -{ - public: - print_response_resource(std::stringstream* ss) - { - this->ss = ss; - } +class print_response_resource : public http_resource { + public: + explicit print_response_resource(stringstream* ss) { + this->ss = ss; + } - const shared_ptr render_GET(const http_request& req) - { - shared_ptr hresp(new string_response("OK", 200, "text/plain")); + const shared_ptr render_GET(const http_request&) { + shared_ptr hresp(new string_response("OK", 200, "text/plain")); - hresp->with_header("MyResponseHeader", "MyResponseHeaderValue"); - hresp->with_footer("MyResponseFooter", "MyResponseFooterValue"); - hresp->with_cookie("MyResponseCookie", "MyResponseCookieValue"); + hresp->with_header("MyResponseHeader", "MyResponseHeaderValue"); + hresp->with_footer("MyResponseFooter", "MyResponseFooterValue"); + hresp->with_cookie("MyResponseCookie", "MyResponseCookieValue"); - (*ss) << *hresp; + (*ss) << *hresp; - return hresp; - } + return hresp; + } - private: - std::stringstream* ss; + private: + stringstream* ss; }; LT_BEGIN_SUITE(basic_suite) - webserver* ws; - void set_up() - { + void set_up() { ws = new webserver(create_webserver(8080)); ws->start(false); } - void tear_down() - { + void tear_down() { ws->stop(); delete ws; } @@ -314,7 +283,7 @@ LT_BEGIN_AUTO_TEST(basic_suite, two_endpoints) ws->register_resource("NOK", &nok); curl_global_init(CURL_GLOBAL_ALL); - std::string s; + string s; { CURL *curl = curl_easy_init(); CURLcode res; @@ -328,7 +297,7 @@ LT_BEGIN_AUTO_TEST(basic_suite, two_endpoints) curl_easy_cleanup(curl); } - std::string t; + string t; { CURL *curl = curl_easy_init(); CURLcode res; @@ -347,7 +316,7 @@ LT_BEGIN_AUTO_TEST(basic_suite, read_body) simple_resource resource; ws->register_resource("base", &resource); curl_global_init(CURL_GLOBAL_ALL); - std::string s; + string s; CURL *curl = curl_easy_init(); CURLcode res; curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); @@ -364,7 +333,7 @@ LT_BEGIN_AUTO_TEST(basic_suite, read_long_body) long_content_resource resource; ws->register_resource("base", &resource); curl_global_init(CURL_GLOBAL_ALL); - std::string s; + string s; CURL *curl = curl_easy_init(); CURLcode res; curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); @@ -381,7 +350,7 @@ LT_BEGIN_AUTO_TEST(basic_suite, resource_setting_header) header_set_test_resource resource; ws->register_resource("base", &resource); curl_global_init(CURL_GLOBAL_ALL); - std::string s; + string s; map ss; CURL *curl = curl_easy_init(); CURLcode res; @@ -402,7 +371,7 @@ LT_BEGIN_AUTO_TEST(basic_suite, resource_setting_cookie) cookie_set_test_resource resource; ws->register_resource("base", &resource); curl_global_init(CURL_GLOBAL_ALL); - std::string s; + string s; CURL *curl = curl_easy_init(); CURLcode res; curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); @@ -418,12 +387,12 @@ LT_BEGIN_AUTO_TEST(basic_suite, resource_setting_cookie) LT_ASSERT_EQ(res, 0); LT_CHECK_EQ(s, "OK"); - std::string read_cookie = ""; + string read_cookie = ""; read_cookie = cookies->data; curl_slist_free_all(cookies); - std::vector cookie_parts = string_utilities::string_split(read_cookie, '\t', false); + vector cookie_parts = httpserver::string_utilities::string_split(read_cookie, '\t', false); LT_CHECK_EQ(cookie_parts[5], "MyCookie"); LT_CHECK_EQ(cookie_parts[6], "CookieValue"); @@ -434,7 +403,7 @@ LT_BEGIN_AUTO_TEST(basic_suite, request_with_header) header_reading_resource resource; ws->register_resource("base", &resource); curl_global_init(CURL_GLOBAL_ALL); - std::string s; + string s; CURL *curl = curl_easy_init(); CURLcode res; curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); @@ -457,7 +426,7 @@ LT_BEGIN_AUTO_TEST(basic_suite, request_with_cookie) cookie_reading_resource resource; ws->register_resource("base", &resource); curl_global_init(CURL_GLOBAL_ALL); - std::string s; + string s; CURL *curl = curl_easy_init(); CURLcode res; curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); @@ -538,7 +507,7 @@ LT_BEGIN_AUTO_TEST(basic_suite, only_render) only_render_resource resource; ws->register_resource("base", &resource); curl_global_init(CURL_GLOBAL_ALL); - std::string s; + string s; CURL* curl; CURLcode res; @@ -617,7 +586,7 @@ LT_BEGIN_AUTO_TEST(basic_suite, postprocessor) simple_resource resource; ws->register_resource("base", &resource); curl_global_init(CURL_GLOBAL_ALL); - std::string s; + string s; CURL *curl = curl_easy_init(); CURLcode res; curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); @@ -654,8 +623,8 @@ LT_BEGIN_AUTO_TEST(basic_suite, no_response) curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); CURLcode res = curl_easy_perform(curl); LT_ASSERT_EQ(res, 0); - long http_code = 0; - curl_easy_getinfo (curl, CURLINFO_RESPONSE_CODE, &http_code); + int64_t http_code = 0; + curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code); LT_ASSERT_EQ(http_code, 500); curl_easy_cleanup(curl); LT_END_AUTO_TEST(no_response) @@ -665,7 +634,7 @@ LT_BEGIN_AUTO_TEST(basic_suite, regex_matching) ws->register_resource("regex/matching/number/[0-9]+", &resource); curl_global_init(CURL_GLOBAL_ALL); - std::string s; + string s; CURL *curl = curl_easy_init(); CURLcode res; curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/regex/matching/number/10"); @@ -683,7 +652,7 @@ LT_BEGIN_AUTO_TEST(basic_suite, regex_matching_arg) ws->register_resource("this/captures/{arg}/passed/in/input", &resource); curl_global_init(CURL_GLOBAL_ALL); - std::string s; + string s; CURL *curl = curl_easy_init(); CURLcode res; curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/this/captures/whatever/passed/in/input"); @@ -702,7 +671,7 @@ LT_BEGIN_AUTO_TEST(basic_suite, regex_matching_arg_custom) curl_global_init(CURL_GLOBAL_ALL); { - std::string s; + string s; CURL *curl = curl_easy_init(); CURLcode res; curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/this/captures/numeric/11/passed/in/input"); @@ -716,7 +685,7 @@ LT_BEGIN_AUTO_TEST(basic_suite, regex_matching_arg_custom) } { - std::string s; + string s; CURL *curl = curl_easy_init(); CURLcode res; curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/this/captures/numeric/text/passed/in/input"); @@ -726,8 +695,8 @@ LT_BEGIN_AUTO_TEST(basic_suite, regex_matching_arg_custom) res = curl_easy_perform(curl); LT_ASSERT_EQ(res, 0); LT_CHECK_EQ(s, "Not Found"); - long http_code = 0; - curl_easy_getinfo (curl, CURLINFO_RESPONSE_CODE, &http_code); + int64_t http_code = 0; + curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code); LT_ASSERT_EQ(http_code, 404); curl_easy_cleanup(curl); } @@ -738,7 +707,7 @@ LT_BEGIN_AUTO_TEST(basic_suite, querystring_processing) ws->register_resource("this/captures/args/passed/in/the/querystring", &resource); curl_global_init(CURL_GLOBAL_ALL); - std::string s; + string s; CURL *curl = curl_easy_init(); CURLcode res; curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/this/captures/args/passed/in/the/querystring?arg=first&arg2=second"); @@ -756,7 +725,7 @@ LT_BEGIN_AUTO_TEST(basic_suite, full_arguments_processing) ws->register_resource("this/captures/args/passed/in/the/querystring", &resource); curl_global_init(CURL_GLOBAL_ALL); - std::string s; + string s; CURL *curl = curl_easy_init(); CURLcode res; curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/this/captures/args/passed/in/the/querystring?arg=argument"); @@ -774,7 +743,7 @@ LT_BEGIN_AUTO_TEST(basic_suite, querystring_query_processing) ws->register_resource("this/captures/args/passed/in/the/querystring", &resource); curl_global_init(CURL_GLOBAL_ALL); - std::string s; + string s; CURL *curl = curl_easy_init(); CURLcode res; curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/this/captures/args/passed/in/the/querystring?arg1=value1&arg2=value2&arg3=value3"); @@ -793,7 +762,7 @@ LT_BEGIN_AUTO_TEST(basic_suite, register_unregister) curl_global_init(CURL_GLOBAL_ALL); { - std::string s; + string s; CURL *curl = curl_easy_init(); CURLcode res; curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); @@ -808,7 +777,7 @@ LT_BEGIN_AUTO_TEST(basic_suite, register_unregister) ws->unregister_resource("base"); { - std::string s; + string s; CURL *curl = curl_easy_init(); CURLcode res; curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); @@ -818,8 +787,8 @@ LT_BEGIN_AUTO_TEST(basic_suite, register_unregister) res = curl_easy_perform(curl); LT_ASSERT_EQ(res, 0); - long http_code = 0; - curl_easy_getinfo (curl, CURLINFO_RESPONSE_CODE, &http_code); + int64_t http_code = 0; + curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code); LT_ASSERT_EQ(http_code, 404); LT_CHECK_EQ(s, "Not Found"); @@ -829,7 +798,7 @@ LT_BEGIN_AUTO_TEST(basic_suite, register_unregister) ws->register_resource("base", &resource); { - std::string s; + string s; CURL *curl = curl_easy_init(); CURLcode res; curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); @@ -848,7 +817,7 @@ LT_BEGIN_AUTO_TEST(basic_suite, file_serving_resource) ws->register_resource("base", &resource); curl_global_init(CURL_GLOBAL_ALL); - std::string s; + string s; CURL *curl = curl_easy_init(); CURLcode res; curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); @@ -861,12 +830,30 @@ LT_BEGIN_AUTO_TEST(basic_suite, file_serving_resource) curl_easy_cleanup(curl); LT_END_AUTO_TEST(file_serving_resource) +LT_BEGIN_AUTO_TEST(basic_suite, file_serving_resource_empty) + file_response_resource_empty resource; + ws->register_resource("base", &resource); + curl_global_init(CURL_GLOBAL_ALL); + + string s; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, ""); + curl_easy_cleanup(curl); +LT_END_AUTO_TEST(file_serving_resource_empty) + LT_BEGIN_AUTO_TEST(basic_suite, exception_forces_500) exception_resource resource; ws->register_resource("base", &resource); curl_global_init(CURL_GLOBAL_ALL); - std::string s; + string s; CURL *curl = curl_easy_init(); CURLcode res; curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); @@ -877,8 +864,8 @@ LT_BEGIN_AUTO_TEST(basic_suite, exception_forces_500) LT_ASSERT_EQ(res, 0); LT_CHECK_EQ(s, "Internal Error"); - long http_code = 0; - curl_easy_getinfo (curl, CURLINFO_RESPONSE_CODE, &http_code); + int64_t http_code = 0; + curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code); LT_ASSERT_EQ(http_code, 500); curl_easy_cleanup(curl); @@ -889,7 +876,7 @@ LT_BEGIN_AUTO_TEST(basic_suite, untyped_error_forces_500) ws->register_resource("base", &resource); curl_global_init(CURL_GLOBAL_ALL); - std::string s; + string s; CURL *curl = curl_easy_init(); CURLcode res; curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); @@ -900,20 +887,20 @@ LT_BEGIN_AUTO_TEST(basic_suite, untyped_error_forces_500) LT_ASSERT_EQ(res, 0); LT_CHECK_EQ(s, "Internal Error"); - long http_code = 0; - curl_easy_getinfo (curl, CURLINFO_RESPONSE_CODE, &http_code); + int64_t http_code = 0; + curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code); LT_ASSERT_EQ(http_code, 500); curl_easy_cleanup(curl); LT_END_AUTO_TEST(untyped_error_forces_500) LT_BEGIN_AUTO_TEST(basic_suite, request_is_printable) - std::stringstream ss; + stringstream ss; print_request_resource resource(&ss); ws->register_resource("base", &resource); curl_global_init(CURL_GLOBAL_ALL); - std::string s; + string s; CURL *curl = curl_easy_init(); CURLcode res; curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); @@ -931,7 +918,7 @@ LT_BEGIN_AUTO_TEST(basic_suite, request_is_printable) LT_ASSERT_EQ(res, 0); LT_CHECK_EQ(s, "OK"); - std::string actual = ss.str(); + string actual = ss.str(); LT_CHECK_EQ(actual.find("GET Request") != string::npos, true); LT_CHECK_EQ(actual.find("Headers [") != string::npos, true); LT_CHECK_EQ(actual.find("Host") != string::npos, true); @@ -943,12 +930,12 @@ LT_BEGIN_AUTO_TEST(basic_suite, request_is_printable) LT_END_AUTO_TEST(request_is_printable) LT_BEGIN_AUTO_TEST(basic_suite, response_is_printable) - std::stringstream ss; + stringstream ss; print_response_resource resource(&ss); ws->register_resource("base", &resource); curl_global_init(CURL_GLOBAL_ALL); - std::string s; + string s; CURL *curl = curl_easy_init(); CURLcode res; curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); @@ -966,7 +953,7 @@ LT_BEGIN_AUTO_TEST(basic_suite, response_is_printable) LT_ASSERT_EQ(res, 0); LT_CHECK_EQ(s, "OK"); - std::string actual = ss.str(); + string actual = ss.str(); LT_CHECK_EQ(actual.find("Response [response_code:200]") != string::npos, true); LT_CHECK_EQ(actual.find("Headers [Content-Type:\"text/plain\" MyResponseHeader:\"MyResponseHeaderValue\" ]") != string::npos, true); LT_CHECK_EQ(actual.find("Footers [MyResponseFooter:\"MyResponseFooterValue\" ]") != string::npos, true); @@ -980,7 +967,7 @@ LT_BEGIN_AUTO_TEST(basic_suite, long_path_pieces) ws->register_resource("/settings", &resource, true); curl_global_init(CURL_GLOBAL_ALL); - std::string s; + string s; CURL *curl = curl_easy_init(); CURLcode res; curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/settings/somestringthatisreallylong/with_really_a_lot_of_content/and_underscores_and_looooooooooooooooooong_stuff"); @@ -998,7 +985,7 @@ LT_BEGIN_AUTO_TEST(basic_suite, url_with_regex_like_pieces) ws->register_resource("/settings", &resource, true); curl_global_init(CURL_GLOBAL_ALL); - std::string s; + string s; CURL *curl = curl_easy_init(); CURLcode res; curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/settings/{}"); diff --git a/test/integ/deferred.cpp b/test/integ/deferred.cpp index 571c36a5..9c093adf 100644 --- a/test/integ/deferred.cpp +++ b/test/integ/deferred.cpp @@ -18,7 +18,7 @@ USA */ -#if defined(_WIN32) && ! defined(__CYGWIN__) +#if defined(_WIN32) && !defined(__CYGWIN__) #define _WINDOWS #undef _WIN32_WINNT #define _WIN32_WINNT 0x600 @@ -37,47 +37,46 @@ #include "httpserver.hpp" #include "littletest.hpp" -using namespace std; -using namespace httpserver; +using std::shared_ptr; +using std::string; -size_t writefunc(void *ptr, size_t size, size_t nmemb, std::string *s) -{ - s->append((char*) ptr, size*nmemb); +using httpserver::webserver; +using httpserver::create_webserver; +using httpserver::http_response; +using httpserver::http_request; +using httpserver::http_resource; +using httpserver::deferred_response; + +size_t writefunc(void *ptr, size_t size, size_t nmemb, string *s) { + s->append(reinterpret_cast(ptr), size*nmemb); return size*nmemb; } static int counter = 0; -struct test_data -{ +struct test_data { int value; }; -ssize_t test_callback(std::shared_ptr closure_data, char* buf, size_t max) -{ - if (counter == 2) - { +ssize_t test_callback(shared_ptr closure_data, char* buf, size_t max) { + std::ignore = closure_data; + + if (counter == 2) { return -1; - } - else - { + } else { memset(buf, 0, max); - strcat(buf, "test"); + snprintf(buf, max, "%s", "test"); counter++; - return std::string(buf).size(); + return string(buf).size(); } } -ssize_t test_callback_with_data(std::shared_ptr closure_data, char* buf, size_t max) -{ - if (counter == 2) - { +ssize_t test_callback_with_data(shared_ptr closure_data, char* buf, size_t max) { + if (counter == 2) { return -1; - } - else - { + } else { memset(buf, 0, max); - strcat(buf, ("test" + std::to_string(closure_data->value)).c_str()); + snprintf(buf, max, "%s%s", "test", std::to_string(closure_data->value).c_str()); closure_data->value = 84; @@ -86,37 +85,31 @@ ssize_t test_callback_with_data(std::shared_ptr closure_data, char* b } } -class deferred_resource : public http_resource -{ - public: - const shared_ptr render_GET(const http_request& req) - { - return shared_ptr>(new deferred_response(test_callback, nullptr, "cycle callback response")); - } +class deferred_resource : public http_resource { + public: + const shared_ptr render_GET(const http_request&) { + return shared_ptr>(new deferred_response(test_callback, nullptr, "cycle callback response")); + } }; -class deferred_resource_with_data : public http_resource -{ - public: - const shared_ptr render_GET(const http_request& req) - { - std::shared_ptr internal_info(new test_data); - internal_info->value = 42; - return shared_ptr>(new deferred_response(test_callback_with_data, internal_info, "cycle callback response")); - } +class deferred_resource_with_data : public http_resource { + public: + const shared_ptr render_GET(const http_request&) { + shared_ptr internal_info(new test_data); + internal_info->value = 42; + return shared_ptr>(new deferred_response(test_callback_with_data, internal_info, "cycle callback response")); + } }; LT_BEGIN_SUITE(deferred_suite) webserver* ws; - void set_up() - { + void set_up() { ws = new webserver(create_webserver(8080)); ws->start(false); } - void tear_down() - { + void tear_down() { counter = 0; ws->stop(); @@ -124,7 +117,7 @@ LT_BEGIN_SUITE(deferred_suite) } LT_END_SUITE(deferred_suite) -LT_BEGIN_AUTO_TEST(deferred_suite, deferred_response) +LT_BEGIN_AUTO_TEST(deferred_suite, deferred_response_suite) deferred_resource resource; ws->register_resource("base", &resource); curl_global_init(CURL_GLOBAL_ALL); @@ -140,7 +133,7 @@ LT_BEGIN_AUTO_TEST(deferred_suite, deferred_response) LT_ASSERT_EQ(res, 0); LT_CHECK_EQ(s, "testtest"); curl_easy_cleanup(curl); -LT_END_AUTO_TEST(deferred_response) +LT_END_AUTO_TEST(deferred_response_suite) LT_BEGIN_AUTO_TEST(deferred_suite, deferred_response_with_data) deferred_resource_with_data resource; diff --git a/test/integ/nodelay.cpp b/test/integ/nodelay.cpp index 0284a3f9..21aa74b9 100644 --- a/test/integ/nodelay.cpp +++ b/test/integ/nodelay.cpp @@ -25,30 +25,32 @@ #include "httpserver.hpp" #include "littletest.hpp" -using namespace httpserver; -using namespace std; +using std::shared_ptr; -class ok_resource : public http_resource -{ - public: - const shared_ptr render_GET(const http_request& req) - { - return shared_ptr(new string_response("OK", 200, "text/plain")); - } +using httpserver::http_resource; +using httpserver::http_response; +using httpserver::string_response; +using httpserver::http_request; +using httpserver::http_resource; +using httpserver::webserver; +using httpserver::create_webserver; + +class ok_resource : public http_resource { + public: + const shared_ptr render_GET(const http_request&) { + return shared_ptr(new string_response("OK", 200, "text/plain")); + } }; LT_BEGIN_SUITE(threaded_suite) - webserver* ws; - void set_up() - { + void set_up() { ws = new webserver(create_webserver(8080).tcp_nodelay()); ws->start(false); } - void tear_down() - { + void tear_down() { ws->stop(); delete ws; } diff --git a/test/integ/threaded.cpp b/test/integ/threaded.cpp index 3e1576c5..08abe2f7 100644 --- a/test/integ/threaded.cpp +++ b/test/integ/threaded.cpp @@ -18,7 +18,7 @@ USA */ -#if defined(_WIN32) && ! defined(__CYGWIN__) +#if defined(_WIN32) && !defined(__CYGWIN__) #define _WINDOWS #endif @@ -29,16 +29,20 @@ #include "httpserver.hpp" #include "littletest.hpp" -using namespace httpserver; -using namespace std; +using std::shared_ptr; -class ok_resource : public http_resource -{ - public: - const shared_ptr render_GET(const http_request& req) - { - return shared_ptr(new string_response("OK", 200, "text/plain")); - } +using httpserver::http_resource; +using httpserver::http_request; +using httpserver::http_response; +using httpserver::string_response; +using httpserver::webserver; +using httpserver::create_webserver; + +class ok_resource : public http_resource { + public: + const shared_ptr render_GET(const http_request&) { + return shared_ptr(new string_response("OK", 200, "text/plain")); + } }; LT_BEGIN_SUITE(threaded_suite) @@ -47,16 +51,14 @@ LT_BEGIN_SUITE(threaded_suite) webserver* ws; #endif - void set_up() - { + void set_up() { #ifndef _WINDOWS - ws = new webserver(create_webserver(8080).start_method(http::http_utils::INTERNAL_SELECT).max_threads(5)); + ws = new webserver(create_webserver(8080).start_method(httpserver::http::http_utils::INTERNAL_SELECT).max_threads(5)); ws->start(false); #endif } - void tear_down() - { + void tear_down() { #ifndef _WINDOWS ws->stop(); delete ws; diff --git a/test/integ/ws_start_stop.cpp b/test/integ/ws_start_stop.cpp index aca06486..89506afc 100644 --- a/test/integ/ws_start_stop.cpp +++ b/test/integ/ws_start_stop.cpp @@ -18,7 +18,7 @@ USA */ -#if defined(_WIN32) && ! defined(__CYGWIN__) +#if defined(_WIN32) && !defined(__CYGWIN__) #define _WINDOWS #undef _WIN32_WINNT #define _WIN32_WINNT 0x600 @@ -37,49 +37,41 @@ #include "httpserver.hpp" #include "littletest.hpp" -using namespace std; -using namespace httpserver; +using std::shared_ptr; -size_t writefunc(void *ptr, size_t size, size_t nmemb, std::string *s) -{ - s->append((char*) ptr, size*nmemb); +size_t writefunc(void *ptr, size_t size, size_t nmemb, std::string *s) { + s->append(reinterpret_cast(ptr), size*nmemb); return size*nmemb; } -class ok_resource : public http_resource -{ - public: - const shared_ptr render_GET(const http_request& req) - { - return shared_ptr(new string_response("OK", 200, "text/plain")); - } +class ok_resource : public httpserver::http_resource { + public: + const shared_ptr render_GET(const httpserver::http_request&) { + return shared_ptr(new httpserver::string_response("OK", 200, "text/plain")); + } }; -const shared_ptr not_found_custom(const http_request& req) -{ - return shared_ptr(new string_response("Not found custom", 404, "text/plain")); +const shared_ptr not_found_custom(const httpserver::http_request&) { + return shared_ptr(new httpserver::string_response("Not found custom", 404, "text/plain")); } -const shared_ptr not_allowed_custom(const http_request& req) -{ - return shared_ptr(new string_response("Not allowed custom", 405, "text/plain")); +const shared_ptr not_allowed_custom(const httpserver::http_request&) { + return shared_ptr(new httpserver::string_response("Not allowed custom", 405, "text/plain")); } LT_BEGIN_SUITE(ws_start_stop_suite) - void set_up() - { + void set_up() { } - void tear_down() - { + void tear_down() { } LT_END_SUITE(ws_start_stop_suite) #ifndef _WINDOWS LT_BEGIN_AUTO_TEST(ws_start_stop_suite, start_stop) - { - webserver ws = create_webserver(8080); + { // NOLINT (internal scope opening - not method start) + httpserver::webserver ws = httpserver::create_webserver(8080); ok_resource ok; ws.register_resource("base", &ok); ws.start(false); @@ -101,7 +93,7 @@ LT_BEGIN_AUTO_TEST(ws_start_stop_suite, start_stop) } { - webserver ws = create_webserver(8080).start_method(http::http_utils::INTERNAL_SELECT); + httpserver::webserver ws = httpserver::create_webserver(8080).start_method(httpserver::http::http_utils::INTERNAL_SELECT); ok_resource ok; ws.register_resource("base", &ok); ws.start(false); @@ -123,7 +115,7 @@ LT_BEGIN_AUTO_TEST(ws_start_stop_suite, start_stop) } { - webserver ws = create_webserver(8080).start_method(http::http_utils::THREAD_PER_CONNECTION); + httpserver::webserver ws = httpserver::create_webserver(8080).start_method(httpserver::http::http_utils::THREAD_PER_CONNECTION); ok_resource ok; ws.register_resource("base", &ok); ws.start(false); @@ -148,8 +140,8 @@ LT_END_AUTO_TEST(start_stop) #if defined(IPV6_TESTS_ENABLED) LT_BEGIN_AUTO_TEST(ws_start_stop_suite, ipv6) - { - webserver ws = create_webserver(8080).use_ipv6(); + { // NOLINT (internal scope opening - not method start) + httpserver::webserver ws = httpserver::create_webserver(8080).use_ipv6(); ok_resource ok; ws.register_resource("base", &ok); ws.start(false); @@ -173,8 +165,8 @@ LT_BEGIN_AUTO_TEST(ws_start_stop_suite, ipv6) LT_END_AUTO_TEST(ipv6) LT_BEGIN_AUTO_TEST(ws_start_stop_suite, dual_stack) - { - webserver ws = create_webserver(8080).use_dual_stack(); + { // NOLINT (internal scope opening - not method start) + httpserver::webserver ws = httpserver::create_webserver(8080).use_dual_stack(); ok_resource ok; ws.register_resource("base", &ok); ws.start(false); @@ -200,7 +192,7 @@ LT_END_AUTO_TEST(dual_stack) #endif LT_BEGIN_AUTO_TEST(ws_start_stop_suite, sweet_kill) - webserver ws = create_webserver(8080); + httpserver::webserver ws = httpserver::create_webserver(8080); ok_resource ok; ws.register_resource("base", &ok); ws.start(false); @@ -238,7 +230,7 @@ LT_BEGIN_AUTO_TEST(ws_start_stop_suite, sweet_kill) LT_END_AUTO_TEST(sweet_kill) LT_BEGIN_AUTO_TEST(ws_start_stop_suite, disable_options) - webserver ws = create_webserver(8080) + httpserver::webserver ws = httpserver::create_webserver(8080) .no_ssl() .no_ipv6() .no_debug() @@ -270,7 +262,7 @@ LT_BEGIN_AUTO_TEST(ws_start_stop_suite, disable_options) LT_END_AUTO_TEST(disable_options) LT_BEGIN_AUTO_TEST(ws_start_stop_suite, enable_options) - webserver ws = create_webserver(8080) + httpserver::webserver ws = httpserver::create_webserver(8080) .debug() .pedantic() .deferred() @@ -297,8 +289,8 @@ LT_BEGIN_AUTO_TEST(ws_start_stop_suite, enable_options) ws.stop(); LT_END_AUTO_TEST(enable_options) -LT_BEGIN_AUTO_TEST(ws_start_stop_suite, custom_socket) #ifndef DARWIN +LT_BEGIN_AUTO_TEST(ws_start_stop_suite, custom_socket) int fd = socket(AF_INET, SOCK_STREAM, 0); struct sockaddr_in address; @@ -308,7 +300,7 @@ LT_BEGIN_AUTO_TEST(ws_start_stop_suite, custom_socket) bind(fd, (struct sockaddr*) &address, sizeof(address)); listen(fd, 10000); - webserver ws = create_webserver(-1).bind_socket(fd); //whatever port here doesn't matter + httpserver::webserver ws = httpserver::create_webserver(-1).bind_socket(fd); // whatever port here doesn't matter ok_resource ok; ws.register_resource("base", &ok); ws.start(false); @@ -327,11 +319,11 @@ LT_BEGIN_AUTO_TEST(ws_start_stop_suite, custom_socket) curl_easy_cleanup(curl); ws.stop(); -#endif LT_END_AUTO_TEST(custom_socket) +#endif LT_BEGIN_AUTO_TEST(ws_start_stop_suite, single_resource) - webserver ws = create_webserver(8080).single_resource(); + httpserver::webserver ws = httpserver::create_webserver(8080).single_resource(); ok_resource ok; ws.register_resource("/", &ok, true); ws.start(false); @@ -353,7 +345,7 @@ LT_BEGIN_AUTO_TEST(ws_start_stop_suite, single_resource) LT_END_AUTO_TEST(single_resource) LT_BEGIN_AUTO_TEST(ws_start_stop_suite, single_resource_not_default_resource) - webserver ws = create_webserver(8080).single_resource(); + httpserver::webserver ws = httpserver::create_webserver(8080).single_resource(); ok_resource ok; LT_CHECK_THROW(ws.register_resource("/other", &ok, true)); LT_CHECK_THROW(ws.register_resource("/", &ok, false)); @@ -363,32 +355,31 @@ LT_BEGIN_AUTO_TEST(ws_start_stop_suite, single_resource_not_default_resource) LT_END_AUTO_TEST(single_resource_not_default_resource) LT_BEGIN_AUTO_TEST(ws_start_stop_suite, thread_per_connection_fails_with_max_threads) - { - webserver ws = create_webserver(8080) - .start_method(http::http_utils::THREAD_PER_CONNECTION) + { // NOLINT (internal scope opening - not method start) + httpserver::webserver ws = httpserver::create_webserver(8080) + .start_method(httpserver::http::http_utils::THREAD_PER_CONNECTION) .max_threads(5); LT_CHECK_THROW(ws.start(false)); } LT_END_AUTO_TEST(thread_per_connection_fails_with_max_threads) LT_BEGIN_AUTO_TEST(ws_start_stop_suite, thread_per_connection_fails_with_max_threads_stack_size) - { - webserver ws = create_webserver(8080) - .start_method(http::http_utils::THREAD_PER_CONNECTION) + { // NOLINT (internal scope opening - not method start) + httpserver::webserver ws = httpserver::create_webserver(8080) + .start_method(httpserver::http::http_utils::THREAD_PER_CONNECTION) .max_thread_stack_size(4*1024*1024); LT_CHECK_THROW(ws.start(false)); } LT_END_AUTO_TEST(thread_per_connection_fails_with_max_threads_stack_size) LT_BEGIN_AUTO_TEST(ws_start_stop_suite, tuning_options) - webserver ws = create_webserver(8080) + httpserver::webserver ws = httpserver::create_webserver(8080) .max_connections(10) .max_threads(10) .memory_limit(10000) .per_IP_connection_limit(10) .max_thread_stack_size(4*1024*1024) .nonce_nc_size(10); - ; ok_resource ok; ws.register_resource("base", &ok); @@ -411,7 +402,7 @@ LT_BEGIN_AUTO_TEST(ws_start_stop_suite, tuning_options) LT_END_AUTO_TEST(tuning_options) LT_BEGIN_AUTO_TEST(ws_start_stop_suite, ssl_base) - webserver ws = create_webserver(8080) + httpserver::webserver ws = httpserver::create_webserver(8080) .use_ssl() .https_mem_key("key.pem") .https_mem_cert("cert.pem"); @@ -424,8 +415,8 @@ LT_BEGIN_AUTO_TEST(ws_start_stop_suite, ssl_base) std::string s; CURL *curl = curl_easy_init(); CURLcode res; - curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); // avoid verifying ssl - curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); // avoid verifying ssl + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); // avoid verifying ssl + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); // avoid verifying ssl curl_easy_setopt(curl, CURLOPT_URL, "https://localhost:8080/base"); curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); @@ -440,7 +431,7 @@ LT_BEGIN_AUTO_TEST(ws_start_stop_suite, ssl_base) LT_END_AUTO_TEST(ssl_base) LT_BEGIN_AUTO_TEST(ws_start_stop_suite, ssl_with_protocol_priorities) - webserver ws = create_webserver(8080) + httpserver::webserver ws = httpserver::create_webserver(8080) .use_ssl() .https_mem_key("key.pem") .https_mem_cert("cert.pem") @@ -454,8 +445,8 @@ LT_BEGIN_AUTO_TEST(ws_start_stop_suite, ssl_with_protocol_priorities) std::string s; CURL *curl = curl_easy_init(); CURLcode res; - curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); // avoid verifying ssl - curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); // avoid verifying ssl + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); // avoid verifying ssl + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); // avoid verifying ssl curl_easy_setopt(curl, CURLOPT_URL, "https://localhost:8080/base"); curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); @@ -469,7 +460,7 @@ LT_BEGIN_AUTO_TEST(ws_start_stop_suite, ssl_with_protocol_priorities) LT_END_AUTO_TEST(ssl_with_protocol_priorities) LT_BEGIN_AUTO_TEST(ws_start_stop_suite, ssl_with_trust) - webserver ws = create_webserver(8080) + httpserver::webserver ws = httpserver::create_webserver(8080) .use_ssl() .https_mem_key("key.pem") .https_mem_cert("cert.pem") @@ -483,8 +474,8 @@ LT_BEGIN_AUTO_TEST(ws_start_stop_suite, ssl_with_trust) std::string s; CURL *curl = curl_easy_init(); CURLcode res; - curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); // avoid verifying ssl - curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); // avoid verifying ssl + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); // avoid verifying ssl + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); // avoid verifying ssl curl_easy_setopt(curl, CURLOPT_URL, "https://localhost:8080/base"); curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); @@ -497,9 +488,8 @@ LT_BEGIN_AUTO_TEST(ws_start_stop_suite, ssl_with_trust) ws.stop(); LT_END_AUTO_TEST(ssl_with_trust) -void* start_ws_blocking(void* par) -{ - webserver* ws = (webserver*) par; +void* start_ws_blocking(void* par) { + httpserver::webserver* ws = (httpserver::webserver*) par; ok_resource ok; ws->register_resource("base", &ok); ws->start(true); @@ -508,10 +498,10 @@ void* start_ws_blocking(void* par) } LT_BEGIN_AUTO_TEST(ws_start_stop_suite, blocking_server) - webserver ws = create_webserver(8080); + httpserver::webserver ws = httpserver::create_webserver(8080); pthread_t tid; - pthread_create(&tid, NULL, start_ws_blocking, (void *) &ws); + pthread_create(&tid, NULL, start_ws_blocking, reinterpret_cast(&ws)); sleep(1); @@ -531,12 +521,12 @@ LT_BEGIN_AUTO_TEST(ws_start_stop_suite, blocking_server) ws.stop(); char* b; - pthread_join(tid,(void**) &b); + pthread_join(tid, reinterpret_cast(&b)); free(b); LT_END_AUTO_TEST(blocking_server) LT_BEGIN_AUTO_TEST(ws_start_stop_suite, custom_error_resources) - webserver ws = create_webserver(8080) + httpserver::webserver ws = httpserver::create_webserver(8080) .not_found_resource(not_found_custom) .method_not_allowed_resource(not_allowed_custom); @@ -572,8 +562,8 @@ LT_BEGIN_AUTO_TEST(ws_start_stop_suite, custom_error_resources) LT_ASSERT_EQ(res, 0); LT_CHECK_EQ(s, "Not found custom"); - long http_code = 0; - curl_easy_getinfo (curl, CURLINFO_RESPONSE_CODE, &http_code); + int64_t http_code = 0; + curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code); LT_ASSERT_EQ(http_code, 404); curl_easy_cleanup(curl); @@ -594,8 +584,8 @@ LT_BEGIN_AUTO_TEST(ws_start_stop_suite, custom_error_resources) LT_ASSERT_EQ(res, 0); LT_CHECK_EQ(s, "Not allowed custom"); - long http_code = 0; - curl_easy_getinfo (curl, CURLINFO_RESPONSE_CODE, &http_code); + int64_t http_code = 0; + curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code); LT_ASSERT_EQ(http_code, 405); curl_easy_cleanup(curl); diff --git a/test/littletest.hpp b/test/littletest.hpp index bf9539fd..80aa7658 100644 --- a/test/littletest.hpp +++ b/test/littletest.hpp @@ -333,7 +333,7 @@ class suite } suite() { } - suite(const suite& s) { } + suite(const suite&) { } }; double calculate_duration(timeval* before, timeval* after) @@ -499,7 +499,7 @@ class test_base { public: const char* __lt_name__; - virtual void run_test(test_runner* tr) { } + virtual void run_test(test_runner*) { } virtual void operator()() { } }; @@ -581,11 +581,11 @@ class test : public test_base } protected: test() { } - test(const test& t) { } + test(const test&) { } friend class test_runner; }; -}; +} #endif //_LITTLETEST_HPP_ diff --git a/test/test_content_empty b/test/test_content_empty new file mode 100755 index 00000000..e69de29b diff --git a/test/unit/http_endpoint_test.cpp b/test/unit/http_endpoint_test.cpp index 677b0fa4..2199af0c 100644 --- a/test/unit/http_endpoint_test.cpp +++ b/test/unit/http_endpoint_test.cpp @@ -22,17 +22,15 @@ #include "littletest.hpp" -using namespace httpserver; -using namespace std; -using namespace details; +using httpserver::details::http_endpoint; +using std::string; +using std::vector; LT_BEGIN_SUITE(http_endpoint_suite) - void set_up() - { + void set_up() { } - void tear_down() - { + void tear_down() { } LT_END_SUITE(http_endpoint_suite) diff --git a/test/unit/http_utils_test.cpp b/test/unit/http_utils_test.cpp index 792ef59d..5a31bfb9 100644 --- a/test/unit/http_utils_test.cpp +++ b/test/unit/http_utils_test.cpp @@ -20,7 +20,7 @@ #include "httpserver/http_utils.hpp" -#if defined(_WIN32) && ! defined(__CYGWIN__) +#if defined(_WIN32) && !defined(__CYGWIN__) #define _WINDOWS #undef _WIN32_WINNT #define _WIN32_WINNT 0x600 @@ -34,27 +34,27 @@ #include "littletest.hpp" -using namespace httpserver; -using namespace std; +using std::string; +using std::vector; LT_BEGIN_SUITE(http_utils_suite) - void set_up() - { + void set_up() { } - void tear_down() - { + void tear_down() { } LT_END_SUITE(http_utils_suite) LT_BEGIN_AUTO_TEST(http_utils_suite, unescape) - char* with_plus = (char*) malloc(6 * sizeof(char)); - sprintf(with_plus, "%s", "A%20B"); + int with_plus_buff_len = 6; + char* with_plus = new char[with_plus_buff_len]; + snprintf(with_plus, with_plus_buff_len, "%s", "A%20B"); std::string string_with_plus = with_plus; - int expected_size = http::http_unescape(string_with_plus); + int expected_size = httpserver::http::http_unescape(&string_with_plus); - char* expected = (char*) malloc(4 * sizeof(char)); - sprintf(expected, "%s", "A B"); + int expected_buff_len = 4; + char* expected = new char[expected_buff_len]; + snprintf(expected, expected_buff_len, "%s", "A B"); std::cout << "|||||" << string_with_plus << "||||" << std::endl; std::cout << expected << std::endl; @@ -62,47 +62,50 @@ LT_BEGIN_AUTO_TEST(http_utils_suite, unescape) LT_CHECK_EQ(string_with_plus, string(expected)); LT_CHECK_EQ(expected_size, 3); - free(with_plus); - free(expected); + delete[] with_plus; + delete[] expected; LT_END_AUTO_TEST(unescape) LT_BEGIN_AUTO_TEST(http_utils_suite, unescape_plus) - char* with_plus = (char*) malloc(4 * sizeof(char)); - sprintf(with_plus, "%s", "A+B"); + int with_plus_buff_len = 4; + char* with_plus = new char[with_plus_buff_len]; + snprintf(with_plus, with_plus_buff_len, "%s", "A+B"); std::string string_with_plus = with_plus; - int expected_size = http::http_unescape(string_with_plus); + int expected_size = httpserver::http::http_unescape(&string_with_plus); - char* expected = (char*) malloc(4 * sizeof(char)); - sprintf(expected, "%s", "A B"); + int expected_buff_len = 4; + char* expected = new char[expected_buff_len]; + snprintf(expected, expected_buff_len, "%s", "A B"); LT_CHECK_EQ(string_with_plus, string(expected)); LT_CHECK_EQ(expected_size, 3); - free(with_plus); - free(expected); + delete[] with_plus; + delete[] expected; LT_END_AUTO_TEST(unescape_plus) LT_BEGIN_AUTO_TEST(http_utils_suite, unescape_partial_marker) - char* with_marker = (char*) malloc(6 * sizeof(char)); - sprintf(with_marker, "%s", "A+B%0"); + int size = 6; + char* with_marker = new char[size]; + snprintf(with_marker, size, "%s", "A+B%0"); std::string string_with_marker = with_marker; - int expected_size = http::http_unescape(string_with_marker); + int expected_size = httpserver::http::http_unescape(&string_with_marker); - char* expected = (char*) malloc(6 * sizeof(char)); - sprintf(expected, "%s", "A B%0"); + char* expected = new char[size]; + snprintf(expected, size, "%s", "A B%0"); LT_CHECK_EQ(string_with_marker, string(expected)); LT_CHECK_EQ(expected_size, 5); - free(with_marker); - free(expected); + delete[] with_marker; + delete[] expected; LT_END_AUTO_TEST(unescape_partial_marker) LT_BEGIN_AUTO_TEST(http_utils_suite, tokenize_url) string value = "test/this/url/here"; string expected_arr[] = { "test", "this", "url", "here" }; vector expected(expected_arr, expected_arr + sizeof(expected_arr) / sizeof(expected_arr[0])); - vector actual = http::http_utils::tokenize_url(value, '/'); + vector actual = httpserver::http::http_utils::tokenize_url(value, '/'); LT_CHECK_COLLECTIONS_EQ(expected.begin(), expected.end(), actual.begin()); LT_END_AUTO_TEST(tokenize_url) @@ -111,7 +114,7 @@ LT_BEGIN_AUTO_TEST(http_utils_suite, tokenize_url_multiple_spaces) string value = "test//this//url//here"; string expected_arr[] = { "test", "this", "url", "here" }; vector expected(expected_arr, expected_arr + sizeof(expected_arr) / sizeof(expected_arr[0])); - vector actual = http::http_utils::tokenize_url(value, '/'); + vector actual = httpserver::http::http_utils::tokenize_url(value, '/'); LT_CHECK_COLLECTIONS_EQ(expected.begin(), expected.end(), actual.begin()); LT_END_AUTO_TEST(tokenize_url_multiple_spaces) @@ -120,34 +123,34 @@ LT_BEGIN_AUTO_TEST(http_utils_suite, tokenize_url_end_slash) string value = "test/this/url/here/"; string expected_arr[] = { "test", "this", "url", "here" }; vector expected(expected_arr, expected_arr + sizeof(expected_arr) / sizeof(expected_arr[0])); - vector actual = http::http_utils::tokenize_url(value, '/'); + vector actual = httpserver::http::http_utils::tokenize_url(value, '/'); LT_CHECK_COLLECTIONS_EQ(expected.begin(), expected.end(), actual.begin()); LT_END_AUTO_TEST(tokenize_url_end_slash) LT_BEGIN_AUTO_TEST(http_utils_suite, standardize_url) string url = "/", result; - result = http::http_utils::standardize_url(url); + result = httpserver::http::http_utils::standardize_url(url); LT_CHECK_EQ(result, "/"); url = "/abc/", result = ""; - result = http::http_utils::standardize_url(url); + result = httpserver::http::http_utils::standardize_url(url); LT_CHECK_EQ(result, "/abc"); url = "/abc", result = ""; - result = http::http_utils::standardize_url(url); + result = httpserver::http::http_utils::standardize_url(url); LT_CHECK_EQ(result, "/abc"); url = "/abc/pqr/", result = ""; - result = http::http_utils::standardize_url(url); + result = httpserver::http::http_utils::standardize_url(url); LT_CHECK_EQ(result, "/abc/pqr"); url = "/abc/pqr", result = ""; - result = http::http_utils::standardize_url(url); + result = httpserver::http::http_utils::standardize_url(url); LT_CHECK_EQ(result, "/abc/pqr"); url = "/abc//pqr", result = ""; - result = http::http_utils::standardize_url(url); + result = httpserver::http::http_utils::standardize_url(url); LT_CHECK_EQ(result, "/abc/pqr"); LT_END_AUTO_TEST(standardize_url) @@ -158,8 +161,8 @@ LT_BEGIN_AUTO_TEST(http_utils_suite, ip_to_str) ip4addr.sin_port = htons(3490); ip4addr.sin_addr.s_addr = inet_addr("127.0.0.1"); - string result = http::get_ip_str((struct sockaddr*) &ip4addr); - unsigned short port = http::get_port((struct sockaddr*) &ip4addr); + string result = httpserver::http::get_ip_str((struct sockaddr*) &ip4addr); + unsigned short port = httpserver::http::get_port((struct sockaddr*) &ip4addr); LT_CHECK_EQ(result, "127.0.0.1"); LT_CHECK_EQ(port, htons(3490)); @@ -172,8 +175,8 @@ LT_BEGIN_AUTO_TEST(http_utils_suite, ip_to_str6) ip6addr.sin6_port = htons(3490); inet_pton(AF_INET6, "2001:db8:8714:3a90::12", &(ip6addr.sin6_addr)); - string result = http::get_ip_str((struct sockaddr *) &ip6addr); - unsigned short port = http::get_port((struct sockaddr*) &ip6addr); + string result = httpserver::http::get_ip_str((struct sockaddr *) &ip6addr); + unsigned short port = httpserver::http::get_port((struct sockaddr*) &ip6addr); LT_CHECK_EQ(result, "2001:db8:8714:3a90::12"); LT_CHECK_EQ(port, htons(3490)); @@ -186,11 +189,11 @@ LT_BEGIN_AUTO_TEST(http_utils_suite, ip_to_str_invalid_family) ip4addr.sin_port = htons(3490); ip4addr.sin_addr.s_addr = inet_addr("127.0.0.1"); - LT_CHECK_THROW(http::get_ip_str((struct sockaddr*) &ip4addr)); + LT_CHECK_THROW(httpserver::http::get_ip_str((struct sockaddr*) &ip4addr)); LT_END_AUTO_TEST(ip_to_str_invalid_family) LT_BEGIN_AUTO_TEST(http_utils_suite, ip_to_str_null) - LT_CHECK_THROW(http::get_ip_str((struct sockaddr*) 0x0)); + LT_CHECK_THROW(httpserver::http::get_ip_str((struct sockaddr*) 0x0)); LT_END_AUTO_TEST(ip_to_str_null) LT_BEGIN_AUTO_TEST(http_utils_suite, get_port_invalid_family) @@ -200,17 +203,17 @@ LT_BEGIN_AUTO_TEST(http_utils_suite, get_port_invalid_family) ip4addr.sin_port = htons(3490); ip4addr.sin_addr.s_addr = inet_addr("127.0.0.1"); - LT_CHECK_THROW(http::get_port((struct sockaddr*) &ip4addr)); + LT_CHECK_THROW(httpserver::http::get_port((struct sockaddr*) &ip4addr)); LT_END_AUTO_TEST(get_port_invalid_family) LT_BEGIN_AUTO_TEST(http_utils_suite, get_port_null) - LT_CHECK_THROW(http::get_port((struct sockaddr*) 0x0)); + LT_CHECK_THROW(httpserver::http::get_port((struct sockaddr*) 0x0)); LT_END_AUTO_TEST(get_port_null) LT_BEGIN_AUTO_TEST(http_utils_suite, ip_representation4_str) - http::ip_representation test_ip("192.168.5.5"); + httpserver::http::ip_representation test_ip("192.168.5.5"); - LT_CHECK_EQ(test_ip.ip_version, http::http_utils::IPV4); + LT_CHECK_EQ(test_ip.ip_version, httpserver::http::http_utils::IPV4); for (int i = 0; i < 12; i++) { LT_CHECK_EQ(test_ip.pieces[i], 0); @@ -225,9 +228,9 @@ LT_BEGIN_AUTO_TEST(http_utils_suite, ip_representation4_str) LT_END_AUTO_TEST(ip_representation4_str) LT_BEGIN_AUTO_TEST(http_utils_suite, ip_representation4_str_mask) - http::ip_representation test_ip("192.168.*.*"); + httpserver::http::ip_representation test_ip("192.168.*.*"); - LT_CHECK_EQ(test_ip.ip_version, http::http_utils::IPV4); + LT_CHECK_EQ(test_ip.ip_version, httpserver::http::http_utils::IPV4); for (int i = 0; i < 12; i++) { LT_CHECK_EQ(test_ip.pieces[i], 0); @@ -242,17 +245,17 @@ LT_BEGIN_AUTO_TEST(http_utils_suite, ip_representation4_str_mask) LT_END_AUTO_TEST(ip_representation4_str_mask) LT_BEGIN_AUTO_TEST(http_utils_suite, ip_representation4_str_invalid) - LT_CHECK_THROW(http::ip_representation("192.168.5.5.5")); + LT_CHECK_THROW(httpserver::http::ip_representation("192.168.5.5.5")); LT_END_AUTO_TEST(ip_representation4_str_invalid) LT_BEGIN_AUTO_TEST(http_utils_suite, ip_representation4_str_beyond255) - LT_CHECK_THROW(http::ip_representation("192.168.256.5")); + LT_CHECK_THROW(httpserver::http::ip_representation("192.168.256.5")); LT_END_AUTO_TEST(ip_representation4_str_beyond255) LT_BEGIN_AUTO_TEST(http_utils_suite, ip_representation6_str) - http::ip_representation test_ip("2001:db8:8714:3a90::12"); + httpserver::http::ip_representation test_ip("2001:db8:8714:3a90::12"); - LT_CHECK_EQ(test_ip.ip_version, http::http_utils::IPV6); + LT_CHECK_EQ(test_ip.ip_version, httpserver::http::http_utils::IPV6); LT_CHECK_EQ(test_ip.pieces[0], 32); LT_CHECK_EQ(test_ip.pieces[1], 1); @@ -275,9 +278,9 @@ LT_BEGIN_AUTO_TEST(http_utils_suite, ip_representation6_str) LT_END_AUTO_TEST(ip_representation6_str) LT_BEGIN_AUTO_TEST(http_utils_suite, ip_representation6_str_mask) - http::ip_representation test_ip("2001:db8:8714:3a90:*:*"); + httpserver::http::ip_representation test_ip("2001:db8:8714:3a90:*:*"); - LT_CHECK_EQ(test_ip.ip_version, http::http_utils::IPV6); + LT_CHECK_EQ(test_ip.ip_version, httpserver::http::http_utils::IPV6); LT_CHECK_EQ(test_ip.pieces[0], 32); LT_CHECK_EQ(test_ip.pieces[1], 1); @@ -300,9 +303,9 @@ LT_BEGIN_AUTO_TEST(http_utils_suite, ip_representation6_str_mask) LT_END_AUTO_TEST(ip_representation6_str_mask) LT_BEGIN_AUTO_TEST(http_utils_suite, ip_representation6_str_nested) - http::ip_representation test_ip("::ffff:192.0.2.128"); + httpserver::http::ip_representation test_ip("::ffff:192.0.2.128"); - LT_CHECK_EQ(test_ip.ip_version, http::http_utils::IPV6); + LT_CHECK_EQ(test_ip.ip_version, httpserver::http::http_utils::IPV6); LT_CHECK_EQ(test_ip.pieces[0], 0); LT_CHECK_EQ(test_ip.pieces[1], 0); @@ -325,10 +328,10 @@ LT_BEGIN_AUTO_TEST(http_utils_suite, ip_representation6_str_nested) LT_END_AUTO_TEST(ip_representation6_str_nested) LT_BEGIN_AUTO_TEST(http_utils_suite, ip_representation6_str_nested_deprecated) - LT_CHECK_NOTHROW(http::ip_representation("::192.0.2.128")); - http::ip_representation test_ip("::192.0.2.128"); + LT_CHECK_NOTHROW(httpserver::http::ip_representation("::192.0.2.128")); + httpserver::http::ip_representation test_ip("::192.0.2.128"); - LT_CHECK_EQ(test_ip.ip_version, http::http_utils::IPV6); + LT_CHECK_EQ(test_ip.ip_version, httpserver::http::http_utils::IPV6); LT_CHECK_EQ(test_ip.pieces[0], 0); LT_CHECK_EQ(test_ip.pieces[1], 0); @@ -351,9 +354,9 @@ LT_BEGIN_AUTO_TEST(http_utils_suite, ip_representation6_str_nested_deprecated) LT_END_AUTO_TEST(ip_representation6_str_nested_deprecated) LT_BEGIN_AUTO_TEST(http_utils_suite, ip_representation6_str_ipv4_mask) - http::ip_representation test_ip("::ffff:192.0.*.*"); + httpserver::http::ip_representation test_ip("::ffff:192.0.*.*"); - LT_CHECK_EQ(test_ip.ip_version, http::http_utils::IPV6); + LT_CHECK_EQ(test_ip.ip_version, httpserver::http::http_utils::IPV6); LT_CHECK_EQ(test_ip.pieces[0], 0); LT_CHECK_EQ(test_ip.pieces[1], 0); @@ -376,9 +379,9 @@ LT_BEGIN_AUTO_TEST(http_utils_suite, ip_representation6_str_ipv4_mask) LT_END_AUTO_TEST(ip_representation6_str_ipv4_mask) LT_BEGIN_AUTO_TEST(http_utils_suite, ip_representation6_str_clustered_middle) - http::ip_representation test_ip("2001:db8::ff00:42:8329"); + httpserver::http::ip_representation test_ip("2001:db8::ff00:42:8329"); - LT_CHECK_EQ(test_ip.ip_version, http::http_utils::IPV6); + LT_CHECK_EQ(test_ip.ip_version, httpserver::http::http_utils::IPV6); LT_CHECK_EQ(test_ip.pieces[0], 32); LT_CHECK_EQ(test_ip.pieces[1], 1); @@ -401,9 +404,9 @@ LT_BEGIN_AUTO_TEST(http_utils_suite, ip_representation6_str_clustered_middle) LT_END_AUTO_TEST(ip_representation6_str_clustered_middle) LT_BEGIN_AUTO_TEST(http_utils_suite, ip_representation6_str_loopback) - http::ip_representation test_ip("::1"); + httpserver::http::ip_representation test_ip("::1"); - LT_CHECK_EQ(test_ip.ip_version, http::http_utils::IPV6); + LT_CHECK_EQ(test_ip.ip_version, httpserver::http::http_utils::IPV6); LT_CHECK_EQ(test_ip.pieces[0], 0); LT_CHECK_EQ(test_ip.pieces[1], 0); @@ -426,51 +429,51 @@ LT_BEGIN_AUTO_TEST(http_utils_suite, ip_representation6_str_loopback) LT_END_AUTO_TEST(ip_representation6_str_loopback) LT_BEGIN_AUTO_TEST(http_utils_suite, ip_representation_weight) - LT_CHECK_EQ(http::ip_representation("::1").weight(), 16); - LT_CHECK_EQ(http::ip_representation("192.168.0.1").weight(), 16); - LT_CHECK_EQ(http::ip_representation("192.168.*.*").weight(), 14); - LT_CHECK_EQ(http::ip_representation("::ffff:192.0.*.*").weight(), 14); - LT_CHECK_EQ(http::ip_representation("2001:db8:8714:3a90:*:*").weight(), 12); - LT_CHECK_EQ(http::ip_representation("2001:db8:8714:3a90:8714:2001:db8:3a90").weight(), 16); - LT_CHECK_EQ(http::ip_representation("2001:db8:8714:3a90:8714:2001:*:*").weight(), 12); - LT_CHECK_EQ(http::ip_representation("*:*:*:*:*:*:*:*").weight(), 0); + LT_CHECK_EQ(httpserver::http::ip_representation("::1").weight(), 16); + LT_CHECK_EQ(httpserver::http::ip_representation("192.168.0.1").weight(), 16); + LT_CHECK_EQ(httpserver::http::ip_representation("192.168.*.*").weight(), 14); + LT_CHECK_EQ(httpserver::http::ip_representation("::ffff:192.0.*.*").weight(), 14); + LT_CHECK_EQ(httpserver::http::ip_representation("2001:db8:8714:3a90:*:*").weight(), 12); + LT_CHECK_EQ(httpserver::http::ip_representation("2001:db8:8714:3a90:8714:2001:db8:3a90").weight(), 16); + LT_CHECK_EQ(httpserver::http::ip_representation("2001:db8:8714:3a90:8714:2001:*:*").weight(), 12); + LT_CHECK_EQ(httpserver::http::ip_representation("*:*:*:*:*:*:*:*").weight(), 0); LT_END_AUTO_TEST(ip_representation_weight) LT_BEGIN_AUTO_TEST(http_utils_suite, ip_representation6_str_invalid) - LT_CHECK_THROW(http::ip_representation("2001:db8:8714:3a90::12:4:4:4")); + LT_CHECK_THROW(httpserver::http::ip_representation("2001:db8:8714:3a90::12:4:4:4")); LT_END_AUTO_TEST(ip_representation6_str_invalid) LT_BEGIN_AUTO_TEST(http_utils_suite, ip_representation6_str_block_too_long) - LT_CHECK_THROW(http::ip_representation("2001:db8:87214:3a90::12:4:4")); + LT_CHECK_THROW(httpserver::http::ip_representation("2001:db8:87214:3a90::12:4:4")); LT_END_AUTO_TEST(ip_representation6_str_block_too_long) LT_BEGIN_AUTO_TEST(http_utils_suite, ip_representation6_str_invalid_multiple_clusters) - LT_CHECK_THROW(http::ip_representation("2001::3a90::12:4:4")); + LT_CHECK_THROW(httpserver::http::ip_representation("2001::3a90::12:4:4")); LT_END_AUTO_TEST(ip_representation6_str_invalid_multiple_clusters) LT_BEGIN_AUTO_TEST(http_utils_suite, ip_representation6_str_invalid_too_long_before_nested) - LT_CHECK_THROW(http::ip_representation("2001:db8:8714:3a90:13:12:13:192.0.2.128")); + LT_CHECK_THROW(httpserver::http::ip_representation("2001:db8:8714:3a90:13:12:13:192.0.2.128")); LT_END_AUTO_TEST(ip_representation6_str_invalid_too_long_before_nested) LT_BEGIN_AUTO_TEST(http_utils_suite, ip_representation6_str_invalid_nested_beyond255) - LT_CHECK_THROW(http::ip_representation("::ffff:192.0.256.128")); + LT_CHECK_THROW(httpserver::http::ip_representation("::ffff:192.0.256.128")); LT_END_AUTO_TEST(ip_representation6_str_invalid_nested_beyond255) LT_BEGIN_AUTO_TEST(http_utils_suite, ip_representation6_str_invalid_nested_more_than_4_parts) - LT_CHECK_THROW(http::ip_representation("::ffff:192.0.5.128.128")); + LT_CHECK_THROW(httpserver::http::ip_representation("::ffff:192.0.5.128.128")); LT_END_AUTO_TEST(ip_representation6_str_invalid_nested_more_than_4_parts) LT_BEGIN_AUTO_TEST(http_utils_suite, ip_representation6_str_invalid_nested_not_at_end) - LT_CHECK_THROW(http::ip_representation("::ffff:192.0.256.128:ffff")); + LT_CHECK_THROW(httpserver::http::ip_representation("::ffff:192.0.256.128:ffff")); LT_END_AUTO_TEST(ip_representation6_str_invalid_nested_not_at_end) LT_BEGIN_AUTO_TEST(http_utils_suite, ip_representation6_str_invalid_nested_starting_non_zero) - LT_CHECK_THROW(http::ip_representation("0:0:1::ffff:192.0.5.128")); + LT_CHECK_THROW(httpserver::http::ip_representation("0:0:1::ffff:192.0.5.128")); LT_END_AUTO_TEST(ip_representation6_str_invalid_nested_starting_non_zero) LT_BEGIN_AUTO_TEST(http_utils_suite, ip_representation6_str_invalid_nested_starting_wrong_prefix) - LT_CHECK_THROW(http::ip_representation("::ffcc:192.0.5.128")); - LT_CHECK_THROW(http::ip_representation("::ccff:192.0.5.128")); + LT_CHECK_THROW(httpserver::http::ip_representation("::ffcc:192.0.5.128")); + LT_CHECK_THROW(httpserver::http::ip_representation("::ccff:192.0.5.128")); LT_END_AUTO_TEST(ip_representation6_str_invalid_nested_starting_wrong_prefix) LT_BEGIN_AUTO_TEST(http_utils_suite, ip_representation4_sockaddr) @@ -480,9 +483,9 @@ LT_BEGIN_AUTO_TEST(http_utils_suite, ip_representation4_sockaddr) ip4addr.sin_port = htons(3490); ip4addr.sin_addr.s_addr = inet_addr("127.0.0.1"); - http::ip_representation test_ip((sockaddr*) &ip4addr); + httpserver::http::ip_representation test_ip(reinterpret_cast(&ip4addr)); - LT_CHECK_EQ(test_ip.ip_version, http::http_utils::IPV4); + LT_CHECK_EQ(test_ip.ip_version, httpserver::http::http_utils::IPV4); for (int i = 0; i < 12; i++) { LT_CHECK_EQ(test_ip.pieces[i], 0); @@ -503,9 +506,9 @@ LT_BEGIN_AUTO_TEST(http_utils_suite, ip_representation6_sockaddr) ip6addr.sin6_port = htons(3490); inet_pton(AF_INET6, "2001:db8:8714:3a90::12", &(ip6addr.sin6_addr)); - http::ip_representation test_ip((sockaddr*) &ip6addr); + httpserver::http::ip_representation test_ip(reinterpret_cast(&ip6addr)); - LT_CHECK_EQ(test_ip.ip_version, http::http_utils::IPV6); + LT_CHECK_EQ(test_ip.ip_version, httpserver::http::http_utils::IPV6); LT_CHECK_EQ(test_ip.pieces[0], 32); LT_CHECK_EQ(test_ip.pieces[1], 1); @@ -528,94 +531,94 @@ LT_BEGIN_AUTO_TEST(http_utils_suite, ip_representation6_sockaddr) LT_END_AUTO_TEST(ip_representation6_sockaddr) LT_BEGIN_AUTO_TEST(http_utils_suite, load_file) - LT_CHECK_EQ(http::load_file("test_content"), "test content of file\n"); + LT_CHECK_EQ(httpserver::http::load_file("test_content"), "test content of file\n"); LT_END_AUTO_TEST(load_file) LT_BEGIN_AUTO_TEST(http_utils_suite, load_file_invalid) - LT_CHECK_THROW(http::load_file("test_content_invalid")); + LT_CHECK_THROW(httpserver::http::load_file("test_content_invalid")); LT_END_AUTO_TEST(load_file_invalid) LT_BEGIN_AUTO_TEST(http_utils_suite, ip_representation_less_than) - LT_CHECK_EQ(http::ip_representation("127.0.0.1") < http::ip_representation("127.0.0.2"), true); - LT_CHECK_EQ(http::ip_representation("128.0.0.1") < http::ip_representation("127.0.0.2"), false); - LT_CHECK_EQ(http::ip_representation("127.0.0.2") < http::ip_representation("127.0.0.1"), false); - LT_CHECK_EQ(http::ip_representation("127.0.0.1") < http::ip_representation("127.0.0.1"), false); - LT_CHECK_EQ(http::ip_representation("127.0.0.1") < http::ip_representation("127.0.0.1"), false); - - LT_CHECK_EQ(http::ip_representation("2001:db8::ff00:42:8329") < http::ip_representation("2001:db8::ff00:42:8329"), false); - LT_CHECK_EQ(http::ip_representation("2001:db8::ff00:42:8330") < http::ip_representation("2001:db8::ff00:42:8329"), false); - LT_CHECK_EQ(http::ip_representation("2001:db8::ff00:42:8329") < http::ip_representation("2001:db8::ff00:42:8330"), true); - LT_CHECK_EQ(http::ip_representation("2002:db8::ff00:42:8329") < http::ip_representation("2001:db8::ff00:42:8330"), false); - - LT_CHECK_EQ(http::ip_representation("::192.0.2.128") < http::ip_representation("::192.0.2.128"), false); - LT_CHECK_EQ(http::ip_representation("::192.0.2.129") < http::ip_representation("::192.0.2.128"), false); - LT_CHECK_EQ(http::ip_representation("::192.0.2.128") < http::ip_representation("::192.0.2.129"), true); - - LT_CHECK_EQ(http::ip_representation("::ffff:192.0.2.128") < http::ip_representation("::ffff:192.0.2.128"), false); - LT_CHECK_EQ(http::ip_representation("::ffff:192.0.2.129") < http::ip_representation("::ffff:192.0.2.128"), false); - LT_CHECK_EQ(http::ip_representation("::ffff:192.0.2.128") < http::ip_representation("::ffff:192.0.2.129"), true); - - LT_CHECK_EQ(http::ip_representation("::ffff:192.0.2.128") < http::ip_representation("::192.0.2.128"), false); - LT_CHECK_EQ(http::ip_representation("::ffff:192.0.2.128") < http::ip_representation("::192.0.2.128"), false); - LT_CHECK_EQ(http::ip_representation("::192.0.2.128") < http::ip_representation("::ffff:192.0.2.129"), true); + LT_CHECK_EQ(httpserver::http::ip_representation("127.0.0.1") < httpserver::http::ip_representation("127.0.0.2"), true); + LT_CHECK_EQ(httpserver::http::ip_representation("128.0.0.1") < httpserver::http::ip_representation("127.0.0.2"), false); + LT_CHECK_EQ(httpserver::http::ip_representation("127.0.0.2") < httpserver::http::ip_representation("127.0.0.1"), false); + LT_CHECK_EQ(httpserver::http::ip_representation("127.0.0.1") < httpserver::http::ip_representation("127.0.0.1"), false); + LT_CHECK_EQ(httpserver::http::ip_representation("127.0.0.1") < httpserver::http::ip_representation("127.0.0.1"), false); + + LT_CHECK_EQ(httpserver::http::ip_representation("2001:db8::ff00:42:8329") < httpserver::http::ip_representation("2001:db8::ff00:42:8329"), false); + LT_CHECK_EQ(httpserver::http::ip_representation("2001:db8::ff00:42:8330") < httpserver::http::ip_representation("2001:db8::ff00:42:8329"), false); + LT_CHECK_EQ(httpserver::http::ip_representation("2001:db8::ff00:42:8329") < httpserver::http::ip_representation("2001:db8::ff00:42:8330"), true); + LT_CHECK_EQ(httpserver::http::ip_representation("2002:db8::ff00:42:8329") < httpserver::http::ip_representation("2001:db8::ff00:42:8330"), false); + + LT_CHECK_EQ(httpserver::http::ip_representation("::192.0.2.128") < httpserver::http::ip_representation("::192.0.2.128"), false); + LT_CHECK_EQ(httpserver::http::ip_representation("::192.0.2.129") < httpserver::http::ip_representation("::192.0.2.128"), false); + LT_CHECK_EQ(httpserver::http::ip_representation("::192.0.2.128") < httpserver::http::ip_representation("::192.0.2.129"), true); + + LT_CHECK_EQ(httpserver::http::ip_representation("::ffff:192.0.2.128") < httpserver::http::ip_representation("::ffff:192.0.2.128"), false); + LT_CHECK_EQ(httpserver::http::ip_representation("::ffff:192.0.2.129") < httpserver::http::ip_representation("::ffff:192.0.2.128"), false); + LT_CHECK_EQ(httpserver::http::ip_representation("::ffff:192.0.2.128") < httpserver::http::ip_representation("::ffff:192.0.2.129"), true); + + LT_CHECK_EQ(httpserver::http::ip_representation("::ffff:192.0.2.128") < httpserver::http::ip_representation("::192.0.2.128"), false); + LT_CHECK_EQ(httpserver::http::ip_representation("::ffff:192.0.2.128") < httpserver::http::ip_representation("::192.0.2.128"), false); + LT_CHECK_EQ(httpserver::http::ip_representation("::192.0.2.128") < httpserver::http::ip_representation("::ffff:192.0.2.129"), true); LT_END_AUTO_TEST(ip_representation_less_than) LT_BEGIN_AUTO_TEST(http_utils_suite, ip_representation_less_than_with_masks) - LT_CHECK_EQ(http::ip_representation("127.0.*.*") < http::ip_representation("127.0.0.1"), false); - LT_CHECK_EQ(http::ip_representation("127.0.0.1") < http::ip_representation("127.0.*.*"), false); - LT_CHECK_EQ(http::ip_representation("127.0.0.*") < http::ip_representation("127.0.*.*"), false); - LT_CHECK_EQ(http::ip_representation("127.0.*.1") < http::ip_representation("127.0.0.1"), false); - LT_CHECK_EQ(http::ip_representation("127.0.0.1") < http::ip_representation("127.0.*.1"), false); - LT_CHECK_EQ(http::ip_representation("127.1.0.1") < http::ip_representation("127.0.*.1"), false); - LT_CHECK_EQ(http::ip_representation("127.0.*.1") < http::ip_representation("127.1.0.1"), true); - LT_CHECK_EQ(http::ip_representation("127.1.*.1") < http::ip_representation("127.0.*.1"), false); - LT_CHECK_EQ(http::ip_representation("127.0.*.1") < http::ip_representation("127.1.*.1"), true); - - LT_CHECK_EQ(http::ip_representation("2001:db8::ff00:42:*") < http::ip_representation("2001:db8::ff00:42:8329"), false); - LT_CHECK_EQ(http::ip_representation("2001:db8::ff00:42:8329") < http::ip_representation("2001:db8::ff00:42:*"), false); + LT_CHECK_EQ(httpserver::http::ip_representation("127.0.*.*") < httpserver::http::ip_representation("127.0.0.1"), false); + LT_CHECK_EQ(httpserver::http::ip_representation("127.0.0.1") < httpserver::http::ip_representation("127.0.*.*"), false); + LT_CHECK_EQ(httpserver::http::ip_representation("127.0.0.*") < httpserver::http::ip_representation("127.0.*.*"), false); + LT_CHECK_EQ(httpserver::http::ip_representation("127.0.*.1") < httpserver::http::ip_representation("127.0.0.1"), false); + LT_CHECK_EQ(httpserver::http::ip_representation("127.0.0.1") < httpserver::http::ip_representation("127.0.*.1"), false); + LT_CHECK_EQ(httpserver::http::ip_representation("127.1.0.1") < httpserver::http::ip_representation("127.0.*.1"), false); + LT_CHECK_EQ(httpserver::http::ip_representation("127.0.*.1") < httpserver::http::ip_representation("127.1.0.1"), true); + LT_CHECK_EQ(httpserver::http::ip_representation("127.1.*.1") < httpserver::http::ip_representation("127.0.*.1"), false); + LT_CHECK_EQ(httpserver::http::ip_representation("127.0.*.1") < httpserver::http::ip_representation("127.1.*.1"), true); + + LT_CHECK_EQ(httpserver::http::ip_representation("2001:db8::ff00:42:*") < httpserver::http::ip_representation("2001:db8::ff00:42:8329"), false); + LT_CHECK_EQ(httpserver::http::ip_representation("2001:db8::ff00:42:8329") < httpserver::http::ip_representation("2001:db8::ff00:42:*"), false); LT_END_AUTO_TEST(ip_representation_less_than_with_masks) LT_BEGIN_AUTO_TEST(http_utils_suite, dump_header_map) - std::map header_map; + std::map header_map; header_map["HEADER_ONE"] = "VALUE_ONE"; header_map["HEADER_TWO"] = "VALUE_TWO"; header_map["HEADER_THREE"] = "VALUE_THREE"; std::stringstream ss; - http::dump_header_map(ss, "prefix", header_map); + httpserver::http::dump_header_map(ss, "prefix", header_map); LT_CHECK_EQ(ss.str(), " prefix [HEADER_ONE:\"VALUE_ONE\" HEADER_TWO:\"VALUE_TWO\" HEADER_THREE:\"VALUE_THREE\" ]\n"); LT_END_AUTO_TEST(dump_header_map) LT_BEGIN_AUTO_TEST(http_utils_suite, dump_header_map_no_prefix) - std::map header_map; + std::map header_map; header_map["HEADER_ONE"] = "VALUE_ONE"; header_map["HEADER_TWO"] = "VALUE_TWO"; header_map["HEADER_THREE"] = "VALUE_THREE"; std::stringstream ss; - http::dump_header_map(ss, "", header_map); + httpserver::http::dump_header_map(ss, "", header_map); LT_CHECK_EQ(ss.str(), " [HEADER_ONE:\"VALUE_ONE\" HEADER_TWO:\"VALUE_TWO\" HEADER_THREE:\"VALUE_THREE\" ]\n"); LT_END_AUTO_TEST(dump_header_map_no_prefix) LT_BEGIN_AUTO_TEST(http_utils_suite, dump_arg_map) - std::map arg_map; + std::map arg_map; arg_map["ARG_ONE"] = "VALUE_ONE"; arg_map["ARG_TWO"] = "VALUE_TWO"; arg_map["ARG_THREE"] = "VALUE_THREE"; std::stringstream ss; - http::dump_arg_map(ss, "prefix", arg_map); + httpserver::http::dump_arg_map(ss, "prefix", arg_map); LT_CHECK_EQ(ss.str(), " prefix [ARG_ONE:\"VALUE_ONE\" ARG_TWO:\"VALUE_TWO\" ARG_THREE:\"VALUE_THREE\" ]\n"); LT_END_AUTO_TEST(dump_arg_map) LT_BEGIN_AUTO_TEST(http_utils_suite, dump_arg_map_no_prefix) - std::map arg_map; + std::map arg_map; arg_map["ARG_ONE"] = "VALUE_ONE"; arg_map["ARG_TWO"] = "VALUE_TWO"; arg_map["ARG_THREE"] = "VALUE_THREE"; std::stringstream ss; - http::dump_arg_map(ss, "", arg_map); + httpserver::http::dump_arg_map(ss, "", arg_map); LT_CHECK_EQ(ss.str(), " [ARG_ONE:\"VALUE_ONE\" ARG_TWO:\"VALUE_TWO\" ARG_THREE:\"VALUE_THREE\" ]\n"); LT_END_AUTO_TEST(dump_arg_map_no_prefix) diff --git a/test/unit/string_utilities_test.cpp b/test/unit/string_utilities_test.cpp index 2539ac08..9fb284d0 100644 --- a/test/unit/string_utilities_test.cpp +++ b/test/unit/string_utilities_test.cpp @@ -24,44 +24,32 @@ #include "littletest.hpp" -using namespace httpserver; -using namespace std; +using std::string; +using std::vector; LT_BEGIN_SUITE(string_utilities_suite) - void set_up() - { + void set_up() { } - void tear_down() - { + void tear_down() { } LT_END_SUITE(string_utilities_suite) LT_BEGIN_AUTO_TEST(string_utilities_suite, to_upper_copy) - LT_CHECK_EQ(string_utilities::to_upper_copy("test message"), string("TEST MESSAGE")); - LT_CHECK_EQ(string_utilities::to_upper_copy("tEsT mEssAge 245&$"), string("TEST MESSAGE 245&$")); + LT_CHECK_EQ(httpserver::string_utilities::to_upper_copy("test message"), string("TEST MESSAGE")); + LT_CHECK_EQ(httpserver::string_utilities::to_upper_copy("tEsT mEssAge 245&$"), string("TEST MESSAGE 245&$")); LT_END_AUTO_TEST(to_upper_copy) -LT_BEGIN_AUTO_TEST(string_utilities_suite, to_upper) - string value = "test message"; - string_utilities::to_upper(value); - LT_CHECK_EQ(value, string("TEST MESSAGE")); - - value = "tEsT mEssAge 245&$"; - string_utilities::to_upper(value); - LT_CHECK_EQ(value, string("TEST MESSAGE 245&$")); -LT_END_AUTO_TEST(to_upper) - LT_BEGIN_AUTO_TEST(string_utilities_suite, to_lower_copy) - LT_CHECK_EQ(string_utilities::to_lower_copy("TEST MESSAGE"), string("test message")); - LT_CHECK_EQ(string_utilities::to_lower_copy("tEsT mEssAge 245&$"), string("test message 245&$")); + LT_CHECK_EQ(httpserver::string_utilities::to_lower_copy("TEST MESSAGE"), string("test message")); + LT_CHECK_EQ(httpserver::string_utilities::to_lower_copy("tEsT mEssAge 245&$"), string("test message 245&$")); LT_END_AUTO_TEST(to_lower_copy) LT_BEGIN_AUTO_TEST(string_utilities_suite, split_string) string value = "test this message here"; string expected_arr[] = { "test", "this", "message", "here" }; vector expected(expected_arr, expected_arr + sizeof(expected_arr) / sizeof(expected_arr[0])); - vector actual = string_utilities::string_split(value, ' ', false); + vector actual = httpserver::string_utilities::string_split(value, ' ', false); LT_CHECK_COLLECTIONS_EQ(expected.begin(), expected.end(), actual.begin()); LT_END_AUTO_TEST(split_string) @@ -70,7 +58,7 @@ LT_BEGIN_AUTO_TEST(string_utilities_suite, split_string_multiple_spaces) string value = "test this message here"; string expected_arr[] = { "test", "", "this", "", "message", "", "here" }; vector expected(expected_arr, expected_arr + sizeof(expected_arr) / sizeof(expected_arr[0])); - vector actual = string_utilities::string_split(value, ' ', false); + vector actual = httpserver::string_utilities::string_split(value, ' ', false); LT_CHECK_COLLECTIONS_EQ(expected.begin(), expected.end(), actual.begin()); LT_END_AUTO_TEST(split_string_multiple_spaces) @@ -79,7 +67,7 @@ LT_BEGIN_AUTO_TEST(string_utilities_suite, split_string_multiple_spaces_collapse string value = "test this message here"; string expected_arr[] = { "test", "this", "message", "here" }; vector expected(expected_arr, expected_arr + sizeof(expected_arr) / sizeof(expected_arr[0])); - vector actual = string_utilities::string_split(value, ' ', true); + vector actual = httpserver::string_utilities::string_split(value, ' ', true); LT_CHECK_COLLECTIONS_EQ(expected.begin(), expected.end(), actual.begin()); LT_END_AUTO_TEST(split_string_multiple_spaces_collapse) @@ -88,7 +76,7 @@ LT_BEGIN_AUTO_TEST(string_utilities_suite, split_string_end_space) string value = "test this message here "; string expected_arr[] = { "test", "this", "message", "here" }; vector expected(expected_arr, expected_arr + sizeof(expected_arr) / sizeof(expected_arr[0])); - vector actual = string_utilities::string_split(value, ' ', false); + vector actual = httpserver::string_utilities::string_split(value, ' ', false); LT_CHECK_COLLECTIONS_EQ(expected.begin(), expected.end(), actual.begin()); LT_END_AUTO_TEST(split_string_end_space) From 995728ea14efc00ea4f70060c86b4d5e30d39592 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 7 Mar 2021 21:16:53 -0800 Subject: [PATCH 516/623] Fix documentation --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6830e75a..89b93950 100644 --- a/README.md +++ b/README.md @@ -564,7 +564,7 @@ The `http_request` class has a set of methods you will have access to when imple * _**const std::string** get_user() **const**:_ Returns the `user` as self-identified through basic authentication. The content of the user header will be parsed only if basic authentication is enabled on the server (enabled by default). * _**const std::string** get_pass() **const**:_ Returns the `password` as self-identified through basic authentication. The content of the password header will be parsed only if basic authentication is enabled on the server (enabled by default). * _**const std::string** get_digested_user() **const**:_ Returns the `digested user` as self-identified through digest authentication. The content of the user header will be parsed only if digest authentication is enabled on the server (enabled by default). -* _**bool** check_digest_auth(**const std::string&** realm, **const std::string&** password, **int** nonce_timeout, **bool&** reload_nonce) **const**:_ Allows to check the validity of the authentication token sent through digest authentication (if the provided values in the WWW-Authenticate header are valid and sound according to RFC2716). Takes in input the `realm` of validity of the authentication, the `password` as known to the server to compare against, the `nonce_timeout` to indicate how long the nonce is valid and `reload_nonce` a boolean that will be set by the method to indicate a nonce being reloaded. The method returns `true` if the authentication is valid, `false` otherwise. +* _**bool** check_digest_auth(**const std::string&** realm, **const std::string&** password, **int** nonce_timeout, **bool*** reload_nonce) **const**:_ Allows to check the validity of the authentication token sent through digest authentication (if the provided values in the WWW-Authenticate header are valid and sound according to RFC2716). Takes in input the `realm` of validity of the authentication, the `password` as known to the server to compare against, the `nonce_timeout` to indicate how long the nonce is valid and `reload_nonce` a boolean that will be set by the method to indicate a nonce being reloaded. The method returns `true` if the authentication is valid, `false` otherwise. #### Example of handler reading arguments from a request #include From 9d2a32acd100f90ce7cde16a73e190522b879097 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sat, 13 Mar 2021 21:41:23 -0800 Subject: [PATCH 517/623] Fix failure on ubuntu 20.04 (#225) As github actions moved to 20.04. --- .github/workflows/verify-build.yml | 96 +++++++++++++++++++----------- 1 file changed, 62 insertions(+), 34 deletions(-) diff --git a/.github/workflows/verify-build.yml b/.github/workflows/verify-build.yml index eafc6ca8..0c94ac83 100644 --- a/.github/workflows/verify-build.yml +++ b/.github/workflows/verify-build.yml @@ -24,6 +24,7 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest, macos-latest] + os-type: [ubuntu, mac] test-group: [basic] compiler-family: [none] c-compiler: [gcc, clang] @@ -33,6 +34,10 @@ jobs: linking: [dynamic, static] build-type: [classic] exclude: + - os: ubuntu-latest + os-type: mac + - os: macos-latest + os-type: ubuntu - c-compiler: gcc cc-compiler: clang++ - c-compiler: clang @@ -50,46 +55,52 @@ jobs: include: - test-group: extra os: ubuntu-latest + os-type: ubuntu build-type: asan compiler-family: clang - c-compiler: clang-3.9 - cc-compiler: clang++-3.9 + c-compiler: clang-6.0 + cc-compiler: clang++-6.0 debug: debug coverage: nocoverage - test-group: extra os: ubuntu-latest + os-type: ubuntu build-type: msan compiler-family: clang - c-compiler: clang-3.9 - cc-compiler: clang++-3.9 + c-compiler: clang-6.0 + cc-compiler: clang++-6.0 debug: debug coverage: nocoverage - test-group: extra os: ubuntu-latest + os-type: ubuntu build-type: lsan compiler-family: clang - c-compiler: clang-3.9 - cc-compiler: clang++-3.9 + c-compiler: clang-6.0 + cc-compiler: clang++-6.0 debug: debug coverage: nocoverage - test-group: extra os: ubuntu-latest + os-type: ubuntu build-type: tsan compiler-family: clang - c-compiler: clang-3.9 - cc-compiler: clang++-3.9 + c-compiler: clang-6.0 + cc-compiler: clang++-6.0 debug: debug coverage: nocoverage - test-group: extra os: ubuntu-latest + os-type: ubuntu build-type: ubsan compiler-family: clang - c-compiler: clang-3.9 - cc-compiler: clang++-3.9 + c-compiler: clang-6.0 + cc-compiler: clang++-6.0 debug: debug coverage: nocoverage - test-group: extra - os: ubuntu-latest + os: ubuntu-18.04 + os-type: ubuntu build-type: none compiler-family: gcc c-compiler: gcc-5 @@ -97,7 +108,8 @@ jobs: debug: nodebug coverage: nocoverage - test-group: extra - os: ubuntu-latest + os: ubuntu-18.04 + os-type: ubuntu build-type: none compiler-family: gcc c-compiler: gcc-6 @@ -106,6 +118,7 @@ jobs: coverage: nocoverage - test-group: extra os: ubuntu-latest + os-type: ubuntu build-type: none compiler-family: gcc c-compiler: gcc-7 @@ -114,6 +127,7 @@ jobs: coverage: nocoverage - test-group: extra os: ubuntu-latest + os-type: ubuntu build-type: none compiler-family: gcc c-compiler: gcc-8 @@ -122,6 +136,7 @@ jobs: coverage: nocoverage - test-group: extra os: ubuntu-latest + os-type: ubuntu build-type: none compiler-family: gcc c-compiler: gcc-9 @@ -129,7 +144,8 @@ jobs: debug: nodebug coverage: nocoverage - test-group: extra - os: ubuntu-latest + os: ubuntu-18.04 + os-type: ubuntu build-type: none compiler-family: clang c-compiler: clang-3.9 @@ -137,7 +153,8 @@ jobs: debug: nodebug coverage: nocoverage - test-group: extra - os: ubuntu-latest + os: ubuntu-18.04 + os-type: ubuntu build-type: none compiler-family: clang c-compiler: clang-4.0 @@ -145,7 +162,8 @@ jobs: debug: nodebug coverage: nocoverage - test-group: extra - os: ubuntu-latest + os: ubuntu-18.04 + os-type: ubuntu build-type: none compiler-family: clang c-compiler: clang-5.0 @@ -154,6 +172,7 @@ jobs: coverage: nocoverage - test-group: extra os: ubuntu-latest + os-type: ubuntu build-type: none compiler-family: clang c-compiler: clang-6.0 @@ -162,6 +181,7 @@ jobs: coverage: nocoverage - test-group: extra os: ubuntu-latest + os-type: ubuntu build-type: none compiler-family: clang c-compiler: clang-7 @@ -170,6 +190,7 @@ jobs: coverage: nocoverage - test-group: extra os: ubuntu-latest + os-type: ubuntu build-type: none compiler-family: clang c-compiler: clang-8 @@ -178,6 +199,7 @@ jobs: coverage: nocoverage - test-group: extra os: ubuntu-latest + os-type: ubuntu build-type: none compiler-family: clang c-compiler: clang-9 @@ -186,6 +208,7 @@ jobs: coverage: nocoverage - test-group: extra os: ubuntu-latest + os-type: ubuntu build-type: valgrind compiler-family: gcc c-compiler: gcc-7 @@ -194,6 +217,7 @@ jobs: coverage: nocoverage - test-group: extra os: ubuntu-latest + os-type: ubuntu build-type: iwyu compiler-family: clang c-compiler: clang-9 @@ -202,6 +226,7 @@ jobs: coverage: nocoverage - test-group: performance os: ubuntu-latest + os-type: ubuntu build-type: select compiler-family: gcc c-compiler: gcc-7 @@ -210,6 +235,7 @@ jobs: coverage: nocoverage - test-group: performance os: ubuntu-latest + os-type: ubuntu build-type: nodelay compiler-family: gcc c-compiler: gcc-7 @@ -218,6 +244,7 @@ jobs: coverage: nocoverage - test-group: performance os: ubuntu-latest + os-type: ubuntu build-type: threads compiler-family: gcc c-compiler: gcc-7 @@ -226,6 +253,7 @@ jobs: coverage: nocoverage - test-group: extra os: ubuntu-latest + os-type: ubuntu build-type: lint compiler-family: gcc c-compiler: gcc-7 @@ -249,27 +277,27 @@ jobs: run: | sudo add-apt-repository ppa:ubuntu-toolchain-r/test ; sudo apt-get update ; - if: ${{ matrix.test-group == 'extra' && matrix.os == 'ubuntu-latest' }} + if: ${{ matrix.test-group == 'extra' && matrix.os-type == 'ubuntu' }} - name: Install apache benchmark if needed run: sudo apt-get install apache2-utils ; - if: ${{ matrix.test-group == 'performance' && matrix.os == 'ubuntu-latest' }} + if: ${{ matrix.test-group == 'performance' && matrix.os-type == 'ubuntu' }} - name: Install optional clang if needed run: sudo apt-get install ${{ matrix.c-compiler }} - if: ${{ matrix.compiler-family == 'clang' && matrix.os == 'ubuntu-latest' }} + if: ${{ matrix.compiler-family == 'clang' && matrix.os-type == 'ubuntu' }} - name: Install optional gcc if needed run: sudo apt-get install ${{ matrix.cc-compiler }} - if: ${{ matrix.compiler-family == 'gcc' && matrix.os == 'ubuntu-latest' }} + if: ${{ matrix.compiler-family == 'gcc' && matrix.os-type == 'ubuntu' }} - name: Install valgrind if needed run: sudo apt-get install valgrind valgrind-dbg - if: ${{ matrix.build-type == 'valgrind' && matrix.os == 'ubuntu-latest' }} + if: ${{ matrix.build-type == 'valgrind' && matrix.os-type == 'ubuntu' }} - name: Install cpplint if needed run: sudo pip3 install cpplint ; - if: ${{ matrix.build-type == 'lint' && matrix.os == 'ubuntu-latest' }} + if: ${{ matrix.build-type == 'lint' && matrix.os-type == 'ubuntu' }} - name: Install IWYU dependencies if needed run: | @@ -278,15 +306,15 @@ jobs: # Use same CMAKE used by iwyu in their setup for travis wget -O cmake.sh https://cmake.org/files/v3.10/cmake-3.10.0-Linux-x86_64.sh ; sudo sh cmake.sh --skip-license --exclude-subdir --prefix=/usr/local ; - if: ${{ matrix.build-type == 'iwyu' && matrix.os == 'ubuntu-latest' }} + if: ${{ matrix.build-type == 'iwyu' && matrix.os-type == 'ubuntu' }} - name: IWYU from cache (for testing) id: cache-IWYU uses: actions/cache@v2 with: path: include-what-you-use - key: ${{ matrix.os }}-include-what-you-use-pre-built - if: ${{ matrix.build-type == 'iwyu' && matrix.os == 'ubuntu-latest' }} + key: ${{ matrix.os }}-${{ matrix.c-compiler }}-include-what-you-use-pre-built + if: ${{ matrix.build-type == 'iwyu' && matrix.os-type == 'ubuntu' }} # Installing iwyu manually because clang and iwyu paths won't match on Ubuntu otherwise. - name: Build IWYU if requested @@ -301,13 +329,13 @@ jobs: cmake -G "Unix Makefiles" -DCMAKE_PREFIX_PATH=$CLANG_ROOT_PATH -DCMAKE_C_COMPILER=$CLANG_BIN_PATH/clang -DCMAKE_CXX_COMPILER=$CLANG_BIN_PATH/clang++ ../ ; make ; sudo make install ; - if: ${{ matrix.build-type == 'iwyu' && matrix.os == 'ubuntu-latest' && steps.cache-IWYU.outputs.cache-hit != 'true' }} + if: ${{ matrix.build-type == 'iwyu' && matrix.os-type == 'ubuntu' && steps.cache-IWYU.outputs.cache-hit != 'true' }} - name: Install IWYU if requested run: | cd include-what-you-use/build_iwyu ; sudo make install ; - if: ${{ matrix.build-type == 'iwyu' && matrix.os == 'ubuntu-latest' }} + if: ${{ matrix.build-type == 'iwyu' && matrix.os-type == 'ubuntu' }} - name: CURL from cache (for testing) id: cache-CURL @@ -322,7 +350,7 @@ jobs: curl https://libhttpserver.s3.amazonaws.com/travis_stuff/curl-7.75.0.tar.gz -o curl-7.75.0.tar.gz ; tar -xzf curl-7.75.0.tar.gz ; cd curl-7.75.0 ; - if [ "$matrix.os" = "ubuntu-latest" ]; then ./configure ; else ./configure --with-darwinssl ; fi + if [ "$matrix.os-type" = "ubuntu" ]; then ./configure ; else ./configure --with-darwinssl ; fi make ; if: ${{ matrix.os == 'macos-latest' && steps.cache-CURL.outputs.cache-hit != 'true' }} @@ -332,7 +360,7 @@ jobs: - name: Install CURL (for testing on linux) run: sudo apt-get install libcurl4-openssl-dev - if: ${{ matrix.os == 'ubuntu-latest' }} + if: ${{ matrix.os-type == 'ubuntu' }} - name: Install autotools on mac run: brew install autoconf automake libtool @@ -344,14 +372,14 @@ jobs: sudo pip install codecov ; sudo pip install gcovr ; sudo apt-get install cppcheck ; - if: ${{ matrix.os == 'ubuntu-latest' }} + if: ${{ matrix.os-type == 'ubuntu' }} - name: Fetch libmicrohttpd from cache id: cache-libmicrohttpd uses: actions/cache@v2 with: path: libmicrohttpd-0.9.59 - key: ${{ matrix.os }}-libmicrohttpd-pre-built + key: ${{ matrix.os }}-${{ matrix.c-compiler }}-libmicrohttpd-pre-built - name: Build libmicrohttpd dependency (if not cached) run: | @@ -367,11 +395,11 @@ jobs: - name: Refresh links to shared libs run: sudo ldconfig ; - if: ${{ matrix.os == 'ubuntu-latest' }} + if: ${{ matrix.os-type == 'ubuntu' }} - name: Run cpplint on code run: cpplint --extensions=cpp,hpp --headers=hpp --recursive . ; - if: ${{ matrix.build-type == 'lint' && matrix.os == 'ubuntu-latest' }} + if: ${{ matrix.build-type == 'lint' && matrix.os-type == 'ubuntu' }} - name: Run libhttpserver configure run: | @@ -453,7 +481,7 @@ jobs: run: | cd src/ ; cppcheck --error-exitcode=1 . ; - if: ${{ matrix.os == 'ubuntu-latest' }} + if: ${{ matrix.os-type == 'ubuntu' }} - name: Run performance tests (select) run: | @@ -483,4 +511,4 @@ jobs: run: | cd build ; bash <(curl -s https://codecov.io/bash) ; - if: ${{ matrix.os == 'ubuntu-latest' && matrix.c-compiler == 'gcc' && matrix.debug == 'debug' && matrix.coverage == 'coverage' && success() }} + if: ${{ matrix.os-type == 'ubuntu' && matrix.c-compiler == 'gcc' && matrix.debug == 'debug' && matrix.coverage == 'coverage' && success() }} From 5f480ad635ad38dd6d6e982b6f7ad91bdc9d2274 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 14 Mar 2021 14:58:24 -0700 Subject: [PATCH 518/623] Add builds on clang-10 and gcc-10 to github actions (#226) --- .github/workflows/verify-build.yml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/.github/workflows/verify-build.yml b/.github/workflows/verify-build.yml index 0c94ac83..de25595f 100644 --- a/.github/workflows/verify-build.yml +++ b/.github/workflows/verify-build.yml @@ -143,6 +143,15 @@ jobs: cc-compiler: g++-9 debug: nodebug coverage: nocoverage + - test-group: extra + os: ubuntu-latest + os-type: ubuntu + build-type: none + compiler-family: gcc + c-compiler: gcc-10 + cc-compiler: g++-10 + debug: nodebug + coverage: nocoverage - test-group: extra os: ubuntu-18.04 os-type: ubuntu @@ -206,6 +215,15 @@ jobs: cc-compiler: clang++-9 debug: nodebug coverage: nocoverage + - test-group: extra + os: ubuntu-latest + os-type: ubuntu + build-type: none + compiler-family: clang + c-compiler: clang-10 + cc-compiler: clang++-10 + debug: nodebug + coverage: nocoverage - test-group: extra os: ubuntu-latest os-type: ubuntu From b38ed58fa743c6f6949dfe66705ceb893880c2a1 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 14 Mar 2021 17:13:05 -0700 Subject: [PATCH 519/623] Remove unused parameters from the documentation --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index 89b93950..7228d200 100644 --- a/README.md +++ b/README.md @@ -114,8 +114,6 @@ Here are listed the libhttpserver specific options (the canonical configure opti * _\-\-enable-debug:_ enable debug data generation. (def=no) * _\-\-disable-doxygen-doc:_ don't generate any doxygen documentation. Doxygen is automatically invoked if present on the system. Automatically disabled otherwise. * _\-\-enable-fastopen:_ enable use of TCP_FASTOPEN (def=yes) -* _\-\-enable-poll[=ARG]:_ enable poll support. Internal behavior of the `INTERNAL_SELECT` (yes, no, auto) [auto] -* _\-\-enable-epoll[=ARG]:_ enable epoll support. Internal behavior of the `INTERNAL_SELECT` (yes, no, auto) [auto] * _\-\-enable-static:_ enable use static linking (def=yes) [Back to TOC](#table-of-contents) From cd91b8f4c0ef77346c2b8052fd81577eafee2c3f Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 14 Mar 2021 20:53:35 -0700 Subject: [PATCH 520/623] Check minimal compiler features (C++14) (#227) --- configure.ac | 3 + m4/ax_cxx_compile_stdcxx.m4 | 964 ++++++++++++++++++++++++++++++++++++ 2 files changed, 967 insertions(+) create mode 100644 m4/ax_cxx_compile_stdcxx.m4 diff --git a/configure.ac b/configure.ac index 26a1dc5b..81030212 100644 --- a/configure.ac +++ b/configure.ac @@ -43,6 +43,9 @@ CXXFLAGS=$OLD_CXXFLAGS AC_LANG([C++]) AC_SYS_LARGEFILE +# Minimal feature-set required +AX_CXX_COMPILE_STDCXX([14]) + native_srcdir=$srcdir AC_MSG_CHECKING([whether it is possible to compile in the same directory]) diff --git a/m4/ax_cxx_compile_stdcxx.m4 b/m4/ax_cxx_compile_stdcxx.m4 new file mode 100644 index 00000000..2bb9b25e --- /dev/null +++ b/m4/ax_cxx_compile_stdcxx.m4 @@ -0,0 +1,964 @@ +# =========================================================================== +# https://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_CXX_COMPILE_STDCXX(VERSION, [ext|noext], [mandatory|optional]) +# +# DESCRIPTION +# +# Check for baseline language coverage in the compiler for the specified +# version of the C++ standard. If necessary, add switches to CXX and +# CXXCPP to enable support. VERSION may be '11' (for the C++11 standard) +# or '14' (for the C++14 standard). +# +# The second argument, if specified, indicates whether you insist on an +# extended mode (e.g. -std=gnu++11) or a strict conformance mode (e.g. +# -std=c++11). If neither is specified, you get whatever works, with +# preference for no added switch, and then for an extended mode. +# +# The third argument, if specified 'mandatory' or if left unspecified, +# indicates that baseline support for the specified C++ standard is +# required and that the macro should error out if no mode with that +# support is found. If specified 'optional', then configuration proceeds +# regardless, after defining HAVE_CXX${VERSION} if and only if a +# supporting mode is found. +# +# LICENSE +# +# Copyright (c) 2008 Benjamin Kosnik +# Copyright (c) 2012 Zack Weinberg +# Copyright (c) 2013 Roy Stogner +# Copyright (c) 2014, 2015 Google Inc.; contributed by Alexey Sokolov +# Copyright (c) 2015 Paul Norman +# Copyright (c) 2015 Moritz Klammler +# Copyright (c) 2016, 2018 Krzesimir Nowak +# Copyright (c) 2019 Enji Cooper +# Copyright (c) 2020 Jason Merrill +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 12 + +dnl This macro is based on the code from the AX_CXX_COMPILE_STDCXX_11 macro +dnl (serial version number 13). + +AC_DEFUN([AX_CXX_COMPILE_STDCXX], [dnl + m4_if([$1], [11], [ax_cxx_compile_alternatives="11 0x"], + [$1], [14], [ax_cxx_compile_alternatives="14 1y"], + [$1], [17], [ax_cxx_compile_alternatives="17 1z"], + [m4_fatal([invalid first argument `$1' to AX_CXX_COMPILE_STDCXX])])dnl + m4_if([$2], [], [], + [$2], [ext], [], + [$2], [noext], [], + [m4_fatal([invalid second argument `$2' to AX_CXX_COMPILE_STDCXX])])dnl + m4_if([$3], [], [ax_cxx_compile_cxx$1_required=true], + [$3], [mandatory], [ax_cxx_compile_cxx$1_required=true], + [$3], [optional], [ax_cxx_compile_cxx$1_required=false], + [m4_fatal([invalid third argument `$3' to AX_CXX_COMPILE_STDCXX])]) + AC_LANG_PUSH([C++])dnl + ac_success=no + + m4_if([$2], [], [dnl + AC_CACHE_CHECK(whether $CXX supports C++$1 features by default, + ax_cv_cxx_compile_cxx$1, + [AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])], + [ax_cv_cxx_compile_cxx$1=yes], + [ax_cv_cxx_compile_cxx$1=no])]) + if test x$ax_cv_cxx_compile_cxx$1 = xyes; then + ac_success=yes + fi]) + + m4_if([$2], [noext], [], [dnl + if test x$ac_success = xno; then + for alternative in ${ax_cxx_compile_alternatives}; do + switch="-std=gnu++${alternative}" + cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch]) + AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch, + $cachevar, + [ac_save_CXX="$CXX" + CXX="$CXX $switch" + AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])], + [eval $cachevar=yes], + [eval $cachevar=no]) + CXX="$ac_save_CXX"]) + if eval test x\$$cachevar = xyes; then + CXX="$CXX $switch" + if test -n "$CXXCPP" ; then + CXXCPP="$CXXCPP $switch" + fi + ac_success=yes + break + fi + done + fi]) + + m4_if([$2], [ext], [], [dnl + if test x$ac_success = xno; then + dnl HP's aCC needs +std=c++11 according to: + dnl http://h21007.www2.hp.com/portal/download/files/unprot/aCxx/PDF_Release_Notes/769149-001.pdf + dnl Cray's crayCC needs "-h std=c++11" + for alternative in ${ax_cxx_compile_alternatives}; do + for switch in -std=c++${alternative} +std=c++${alternative} "-h std=c++${alternative}"; do + cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch]) + AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch, + $cachevar, + [ac_save_CXX="$CXX" + CXX="$CXX $switch" + AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])], + [eval $cachevar=yes], + [eval $cachevar=no]) + CXX="$ac_save_CXX"]) + if eval test x\$$cachevar = xyes; then + CXX="$CXX $switch" + if test -n "$CXXCPP" ; then + CXXCPP="$CXXCPP $switch" + fi + ac_success=yes + break + fi + done + if test x$ac_success = xyes; then + break + fi + done + fi]) + AC_LANG_POP([C++]) + if test x$ax_cxx_compile_cxx$1_required = xtrue; then + if test x$ac_success = xno; then + AC_MSG_ERROR([*** A compiler with support for C++$1 language features is required.]) + fi + fi + if test x$ac_success = xno; then + HAVE_CXX$1=0 + AC_MSG_NOTICE([No compiler with C++$1 support was found]) + else + HAVE_CXX$1=1 + AC_DEFINE(HAVE_CXX$1,1, + [define if the compiler supports basic C++$1 syntax]) + fi + AC_SUBST(HAVE_CXX$1) +]) + + +dnl Test body for checking C++11 support + +m4_define([_AX_CXX_COMPILE_STDCXX_testbody_11], + _AX_CXX_COMPILE_STDCXX_testbody_new_in_11 +) + + +dnl Test body for checking C++14 support + +m4_define([_AX_CXX_COMPILE_STDCXX_testbody_14], + _AX_CXX_COMPILE_STDCXX_testbody_new_in_11 + _AX_CXX_COMPILE_STDCXX_testbody_new_in_14 +) + +m4_define([_AX_CXX_COMPILE_STDCXX_testbody_17], + _AX_CXX_COMPILE_STDCXX_testbody_new_in_11 + _AX_CXX_COMPILE_STDCXX_testbody_new_in_14 + _AX_CXX_COMPILE_STDCXX_testbody_new_in_17 +) + +dnl Tests for new features in C++11 + +m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_11], [[ + +// If the compiler admits that it is not ready for C++11, why torture it? +// Hopefully, this will speed up the test. + +#ifndef __cplusplus + +#error "This is not a C++ compiler" + +#elif __cplusplus < 201103L + +#error "This is not a C++11 compiler" + +#else + +namespace cxx11 +{ + + namespace test_static_assert + { + + template + struct check + { + static_assert(sizeof(int) <= sizeof(T), "not big enough"); + }; + + } + + namespace test_final_override + { + + struct Base + { + virtual ~Base() {} + virtual void f() {} + }; + + struct Derived : public Base + { + virtual ~Derived() override {} + virtual void f() override {} + }; + + } + + namespace test_double_right_angle_brackets + { + + template < typename T > + struct check {}; + + typedef check single_type; + typedef check> double_type; + typedef check>> triple_type; + typedef check>>> quadruple_type; + + } + + namespace test_decltype + { + + int + f() + { + int a = 1; + decltype(a) b = 2; + return a + b; + } + + } + + namespace test_type_deduction + { + + template < typename T1, typename T2 > + struct is_same + { + static const bool value = false; + }; + + template < typename T > + struct is_same + { + static const bool value = true; + }; + + template < typename T1, typename T2 > + auto + add(T1 a1, T2 a2) -> decltype(a1 + a2) + { + return a1 + a2; + } + + int + test(const int c, volatile int v) + { + static_assert(is_same::value == true, ""); + static_assert(is_same::value == false, ""); + static_assert(is_same::value == false, ""); + auto ac = c; + auto av = v; + auto sumi = ac + av + 'x'; + auto sumf = ac + av + 1.0; + static_assert(is_same::value == true, ""); + static_assert(is_same::value == true, ""); + static_assert(is_same::value == true, ""); + static_assert(is_same::value == false, ""); + static_assert(is_same::value == true, ""); + return (sumf > 0.0) ? sumi : add(c, v); + } + + } + + namespace test_noexcept + { + + int f() { return 0; } + int g() noexcept { return 0; } + + static_assert(noexcept(f()) == false, ""); + static_assert(noexcept(g()) == true, ""); + + } + + namespace test_constexpr + { + + template < typename CharT > + unsigned long constexpr + strlen_c_r(const CharT *const s, const unsigned long acc) noexcept + { + return *s ? strlen_c_r(s + 1, acc + 1) : acc; + } + + template < typename CharT > + unsigned long constexpr + strlen_c(const CharT *const s) noexcept + { + return strlen_c_r(s, 0UL); + } + + static_assert(strlen_c("") == 0UL, ""); + static_assert(strlen_c("1") == 1UL, ""); + static_assert(strlen_c("example") == 7UL, ""); + static_assert(strlen_c("another\0example") == 7UL, ""); + + } + + namespace test_rvalue_references + { + + template < int N > + struct answer + { + static constexpr int value = N; + }; + + answer<1> f(int&) { return answer<1>(); } + answer<2> f(const int&) { return answer<2>(); } + answer<3> f(int&&) { return answer<3>(); } + + void + test() + { + int i = 0; + const int c = 0; + static_assert(decltype(f(i))::value == 1, ""); + static_assert(decltype(f(c))::value == 2, ""); + static_assert(decltype(f(0))::value == 3, ""); + } + + } + + namespace test_uniform_initialization + { + + struct test + { + static const int zero {}; + static const int one {1}; + }; + + static_assert(test::zero == 0, ""); + static_assert(test::one == 1, ""); + + } + + namespace test_lambdas + { + + void + test1() + { + auto lambda1 = [](){}; + auto lambda2 = lambda1; + lambda1(); + lambda2(); + } + + int + test2() + { + auto a = [](int i, int j){ return i + j; }(1, 2); + auto b = []() -> int { return '0'; }(); + auto c = [=](){ return a + b; }(); + auto d = [&](){ return c; }(); + auto e = [a, &b](int x) mutable { + const auto identity = [](int y){ return y; }; + for (auto i = 0; i < a; ++i) + a += b--; + return x + identity(a + b); + }(0); + return a + b + c + d + e; + } + + int + test3() + { + const auto nullary = [](){ return 0; }; + const auto unary = [](int x){ return x; }; + using nullary_t = decltype(nullary); + using unary_t = decltype(unary); + const auto higher1st = [](nullary_t f){ return f(); }; + const auto higher2nd = [unary](nullary_t f1){ + return [unary, f1](unary_t f2){ return f2(unary(f1())); }; + }; + return higher1st(nullary) + higher2nd(nullary)(unary); + } + + } + + namespace test_variadic_templates + { + + template + struct sum; + + template + struct sum + { + static constexpr auto value = N0 + sum::value; + }; + + template <> + struct sum<> + { + static constexpr auto value = 0; + }; + + static_assert(sum<>::value == 0, ""); + static_assert(sum<1>::value == 1, ""); + static_assert(sum<23>::value == 23, ""); + static_assert(sum<1, 2>::value == 3, ""); + static_assert(sum<5, 5, 11>::value == 21, ""); + static_assert(sum<2, 3, 5, 7, 11, 13>::value == 41, ""); + + } + + // http://stackoverflow.com/questions/13728184/template-aliases-and-sfinae + // Clang 3.1 fails with headers of libstd++ 4.8.3 when using std::function + // because of this. + namespace test_template_alias_sfinae + { + + struct foo {}; + + template + using member = typename T::member_type; + + template + void func(...) {} + + template + void func(member*) {} + + void test(); + + void test() { func(0); } + + } + +} // namespace cxx11 + +#endif // __cplusplus >= 201103L + +]]) + + +dnl Tests for new features in C++14 + +m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_14], [[ + +// If the compiler admits that it is not ready for C++14, why torture it? +// Hopefully, this will speed up the test. + +#ifndef __cplusplus + +#error "This is not a C++ compiler" + +#elif __cplusplus < 201402L + +#error "This is not a C++14 compiler" + +#else + +namespace cxx14 +{ + + namespace test_polymorphic_lambdas + { + + int + test() + { + const auto lambda = [](auto&&... args){ + const auto istiny = [](auto x){ + return (sizeof(x) == 1UL) ? 1 : 0; + }; + const int aretiny[] = { istiny(args)... }; + return aretiny[0]; + }; + return lambda(1, 1L, 1.0f, '1'); + } + + } + + namespace test_binary_literals + { + + constexpr auto ivii = 0b0000000000101010; + static_assert(ivii == 42, "wrong value"); + + } + + namespace test_generalized_constexpr + { + + template < typename CharT > + constexpr unsigned long + strlen_c(const CharT *const s) noexcept + { + auto length = 0UL; + for (auto p = s; *p; ++p) + ++length; + return length; + } + + static_assert(strlen_c("") == 0UL, ""); + static_assert(strlen_c("x") == 1UL, ""); + static_assert(strlen_c("test") == 4UL, ""); + static_assert(strlen_c("another\0test") == 7UL, ""); + + } + + namespace test_lambda_init_capture + { + + int + test() + { + auto x = 0; + const auto lambda1 = [a = x](int b){ return a + b; }; + const auto lambda2 = [a = lambda1(x)](){ return a; }; + return lambda2(); + } + + } + + namespace test_digit_separators + { + + constexpr auto ten_million = 100'000'000; + static_assert(ten_million == 100000000, ""); + + } + + namespace test_return_type_deduction + { + + auto f(int& x) { return x; } + decltype(auto) g(int& x) { return x; } + + template < typename T1, typename T2 > + struct is_same + { + static constexpr auto value = false; + }; + + template < typename T > + struct is_same + { + static constexpr auto value = true; + }; + + int + test() + { + auto x = 0; + static_assert(is_same::value, ""); + static_assert(is_same::value, ""); + return x; + } + + } + +} // namespace cxx14 + +#endif // __cplusplus >= 201402L + +]]) + + +dnl Tests for new features in C++17 + +m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_17], [[ + +// If the compiler admits that it is not ready for C++17, why torture it? +// Hopefully, this will speed up the test. + +#ifndef __cplusplus + +#error "This is not a C++ compiler" + +#elif __cplusplus < 201703L + +#error "This is not a C++17 compiler" + +#else + +#include +#include +#include + +namespace cxx17 +{ + + namespace test_constexpr_lambdas + { + + constexpr int foo = [](){return 42;}(); + + } + + namespace test::nested_namespace::definitions + { + + } + + namespace test_fold_expression + { + + template + int multiply(Args... args) + { + return (args * ... * 1); + } + + template + bool all(Args... args) + { + return (args && ...); + } + + } + + namespace test_extended_static_assert + { + + static_assert (true); + + } + + namespace test_auto_brace_init_list + { + + auto foo = {5}; + auto bar {5}; + + static_assert(std::is_same, decltype(foo)>::value); + static_assert(std::is_same::value); + } + + namespace test_typename_in_template_template_parameter + { + + template typename X> struct D; + + } + + namespace test_fallthrough_nodiscard_maybe_unused_attributes + { + + int f1() + { + return 42; + } + + [[nodiscard]] int f2() + { + [[maybe_unused]] auto unused = f1(); + + switch (f1()) + { + case 17: + f1(); + [[fallthrough]]; + case 42: + f1(); + } + return f1(); + } + + } + + namespace test_extended_aggregate_initialization + { + + struct base1 + { + int b1, b2 = 42; + }; + + struct base2 + { + base2() { + b3 = 42; + } + int b3; + }; + + struct derived : base1, base2 + { + int d; + }; + + derived d1 {{1, 2}, {}, 4}; // full initialization + derived d2 {{}, {}, 4}; // value-initialized bases + + } + + namespace test_general_range_based_for_loop + { + + struct iter + { + int i; + + int& operator* () + { + return i; + } + + const int& operator* () const + { + return i; + } + + iter& operator++() + { + ++i; + return *this; + } + }; + + struct sentinel + { + int i; + }; + + bool operator== (const iter& i, const sentinel& s) + { + return i.i == s.i; + } + + bool operator!= (const iter& i, const sentinel& s) + { + return !(i == s); + } + + struct range + { + iter begin() const + { + return {0}; + } + + sentinel end() const + { + return {5}; + } + }; + + void f() + { + range r {}; + + for (auto i : r) + { + [[maybe_unused]] auto v = i; + } + } + + } + + namespace test_lambda_capture_asterisk_this_by_value + { + + struct t + { + int i; + int foo() + { + return [*this]() + { + return i; + }(); + } + }; + + } + + namespace test_enum_class_construction + { + + enum class byte : unsigned char + {}; + + byte foo {42}; + + } + + namespace test_constexpr_if + { + + template + int f () + { + if constexpr(cond) + { + return 13; + } + else + { + return 42; + } + } + + } + + namespace test_selection_statement_with_initializer + { + + int f() + { + return 13; + } + + int f2() + { + if (auto i = f(); i > 0) + { + return 3; + } + + switch (auto i = f(); i + 4) + { + case 17: + return 2; + + default: + return 1; + } + } + + } + + namespace test_template_argument_deduction_for_class_templates + { + + template + struct pair + { + pair (T1 p1, T2 p2) + : m1 {p1}, + m2 {p2} + {} + + T1 m1; + T2 m2; + }; + + void f() + { + [[maybe_unused]] auto p = pair{13, 42u}; + } + + } + + namespace test_non_type_auto_template_parameters + { + + template + struct B + {}; + + B<5> b1; + B<'a'> b2; + + } + + namespace test_structured_bindings + { + + int arr[2] = { 1, 2 }; + std::pair pr = { 1, 2 }; + + auto f1() -> int(&)[2] + { + return arr; + } + + auto f2() -> std::pair& + { + return pr; + } + + struct S + { + int x1 : 2; + volatile double y1; + }; + + S f3() + { + return {}; + } + + auto [ x1, y1 ] = f1(); + auto& [ xr1, yr1 ] = f1(); + auto [ x2, y2 ] = f2(); + auto& [ xr2, yr2 ] = f2(); + const auto [ x3, y3 ] = f3(); + + } + + namespace test_exception_spec_type_system + { + + struct Good {}; + struct Bad {}; + + void g1() noexcept; + void g2(); + + template + Bad + f(T*, T*); + + template + Good + f(T1*, T2*); + + static_assert (std::is_same_v); + + } + + namespace test_inline_variables + { + + template void f(T) + {} + + template inline T g(T) + { + return T{}; + } + + template<> inline void f<>(int) + {} + + template<> int g<>(int) + { + return 5; + } + + } + +} // namespace cxx17 + +#endif // __cplusplus < 201703L + +]]) + + From d5a5f88111b234efb6aff2c9ed147eb87548c101 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 4 Apr 2021 14:03:26 -0700 Subject: [PATCH 521/623] Always install ubuntu test sources --- .github/workflows/verify-build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/verify-build.yml b/.github/workflows/verify-build.yml index de25595f..8be4c692 100644 --- a/.github/workflows/verify-build.yml +++ b/.github/workflows/verify-build.yml @@ -291,11 +291,11 @@ jobs: - run: git checkout HEAD^2 if: ${{ github.event_name == 'pull_request' }} - - name: Install Ubuntu test sources (for extra builds) + - name: Install Ubuntu test sources run: | sudo add-apt-repository ppa:ubuntu-toolchain-r/test ; sudo apt-get update ; - if: ${{ matrix.test-group == 'extra' && matrix.os-type == 'ubuntu' }} + if: ${{ matrix.os-type == 'ubuntu' }} - name: Install apache benchmark if needed run: sudo apt-get install apache2-utils ; From c5cf5eaa89830ad2aa706a161a647705661bd671 Mon Sep 17 00:00:00 2001 From: bcsgh <33939446+bcsgh@users.noreply.github.com> Date: Wed, 9 Jun 2021 12:27:31 -0700 Subject: [PATCH 522/623] Add a getter to allow access to the gnutls_session_t. (#233) * Add a getter to allow access to the gnutls_session_t. * Placate lint. * Placate lint, some more. * Document get_tls_session. * Add http_request::has_tls_session to allow checking if http_request::get_tls_session is safe to call. * Spelling and grammer. --- README.md | 2 ++ src/http_request.cpp | 13 +++++++++++++ src/httpserver/http_request.hpp | 18 ++++++++++++++++++ 3 files changed, 33 insertions(+) diff --git a/README.md b/README.md index 7228d200..0385f54c 100644 --- a/README.md +++ b/README.md @@ -563,6 +563,8 @@ The `http_request` class has a set of methods you will have access to when imple * _**const std::string** get_pass() **const**:_ Returns the `password` as self-identified through basic authentication. The content of the password header will be parsed only if basic authentication is enabled on the server (enabled by default). * _**const std::string** get_digested_user() **const**:_ Returns the `digested user` as self-identified through digest authentication. The content of the user header will be parsed only if digest authentication is enabled on the server (enabled by default). * _**bool** check_digest_auth(**const std::string&** realm, **const std::string&** password, **int** nonce_timeout, **bool*** reload_nonce) **const**:_ Allows to check the validity of the authentication token sent through digest authentication (if the provided values in the WWW-Authenticate header are valid and sound according to RFC2716). Takes in input the `realm` of validity of the authentication, the `password` as known to the server to compare against, the `nonce_timeout` to indicate how long the nonce is valid and `reload_nonce` a boolean that will be set by the method to indicate a nonce being reloaded. The method returns `true` if the authentication is valid, `false` otherwise. +* _**gnutls_session_t** get_tls_session() **const**:_ Tests if there is am underlying TLS state of the current request. +* _**gnutls_session_t** get_tls_session() **const**:_ Returns the underlying TLS state of the current request for inspection. (It is an error to call this if the state does not exist.) #### Example of handler reading arguments from a request #include diff --git a/src/http_request.cpp b/src/http_request.cpp index 0ac718e4..5f29e0cc 100644 --- a/src/http_request.cpp +++ b/src/http_request.cpp @@ -212,6 +212,19 @@ const std::string http_request::get_digested_user() const { return digested_user; } +#ifdef HAVE_GNUTLS +bool http_request::has_tls_session() const { + const MHD_ConnectionInfo * conninfo = MHD_get_connection_info(underlying_connection, MHD_CONNECTION_INFO_GNUTLS_SESSION); + return (conninfo != nullptr); +} + +gnutls_session_t http_request::get_tls_session() const { + const MHD_ConnectionInfo * conninfo = MHD_get_connection_info(underlying_connection, MHD_CONNECTION_INFO_GNUTLS_SESSION); + + return static_cast(conninfo->tls_session); +} +#endif // HAVE_GNUTLS + const std::string http_request::get_requestor() const { const MHD_ConnectionInfo * conninfo = MHD_get_connection_info(underlying_connection, MHD_CONNECTION_INFO_CLIENT_ADDRESS); diff --git a/src/httpserver/http_request.hpp b/src/httpserver/http_request.hpp index 0f1708cf..2c426c05 100644 --- a/src/httpserver/http_request.hpp +++ b/src/httpserver/http_request.hpp @@ -27,6 +27,10 @@ #include +#ifdef HAVE_GNUTLS +#include +#endif // HAVE_GNUTLS + #include #include #include @@ -183,6 +187,20 @@ class http_request { return version; } +#ifdef HAVE_GNUTLS + /** + * Method used to check if there is a TLS session. + * @return the TLS session + **/ + bool has_tls_session() const; + + /** + * Method used to get the TLS session. + * @return the TLS session + **/ + gnutls_session_t get_tls_session() const; +#endif // HAVE_GNUTLS + /** * Method used to get the requestor. * @return the requestor From 263f2bdef4d5f8887a46c19fa08695170ad679f7 Mon Sep 17 00:00:00 2001 From: Alexander Dahl Date: Tue, 9 Nov 2021 01:02:07 +0100 Subject: [PATCH 523/623] Test shared_ptr before accessing members (#239) * Add check for empty shared_ptr Users can register arbitrary resources and could technically return empty shared_ptr without managed http_response object. This case should not crash the library. Signed-off-by: Alexander Dahl References: #238 * Test shared_ptr before accessing members Avoid possible segfault if someone returns an empty shared_ptr in her render method. Signed-off-by: Alexander Dahl Fixes: #238 --- src/webserver.cpp | 2 +- test/integ/basic.cpp | 23 +++++++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/webserver.cpp b/src/webserver.cpp index 7d50fe00..40ce85cb 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -596,7 +596,7 @@ MHD_Result webserver::finalize_answer(MHD_Connection* connection, struct details try { if (hrm->is_allowed(method)) { mr->dhrs = ((hrm)->*(mr->callback))(*mr->dhr); // copy in memory (move in case) - if (mr->dhrs->get_response_code() == -1) { + if (mr->dhrs.get() == nullptr || mr->dhrs->get_response_code() == -1) { mr->dhrs = internal_error_page(mr); } } else { diff --git a/test/integ/basic.cpp b/test/integ/basic.cpp index e340ff8a..0334317a 100644 --- a/test/integ/basic.cpp +++ b/test/integ/basic.cpp @@ -193,6 +193,13 @@ class no_response_resource : public http_resource { } }; +class empty_response_resource : public http_resource { + public: + const shared_ptr render_GET(const http_request&) { + return shared_ptr(nullptr); + } +}; + class file_response_resource : public http_resource { public: const shared_ptr render_GET(const http_request&) { @@ -629,6 +636,22 @@ LT_BEGIN_AUTO_TEST(basic_suite, no_response) curl_easy_cleanup(curl); LT_END_AUTO_TEST(no_response) +LT_BEGIN_AUTO_TEST(basic_suite, empty_response) + empty_response_resource resource; + ws->register_resource("base", &resource); + curl_global_init(CURL_GLOBAL_ALL); + + CURL* curl = curl_easy_init(); + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + CURLcode res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + int64_t http_code = 0; + curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code); + LT_ASSERT_EQ(http_code, 500); + curl_easy_cleanup(curl); +LT_END_AUTO_TEST(empty_response) + LT_BEGIN_AUTO_TEST(basic_suite, regex_matching) simple_resource resource; ws->register_resource("regex/matching/number/[0-9]+", &resource); From 8901082ee574a1c7040d6717dc3984c37cabc06b Mon Sep 17 00:00:00 2001 From: Alexander Dahl Date: Fri, 12 Nov 2021 16:18:38 +0100 Subject: [PATCH 524/623] webserver: Fix garbage output on debug print (#244) The `upload_data` we get in the callback from libmicrohttpd might not be null terminated. Fixes: #242 Signed-off-by: Alexander Dahl --- src/webserver.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/webserver.cpp b/src/webserver.cpp index 40ce85cb..363ac0ce 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -525,7 +525,7 @@ MHD_Result webserver::requests_answer_second_step(MHD_Connection* connection, co if (mr->has_body) { #ifdef DEBUG - std::cout << "Writing content: " << upload_data << std::endl; + std::cout << "Writing content: " << std::string(upload_data, *upload_data_size) << std::endl; #endif // DEBUG mr->dhr->grow_content(upload_data, *upload_data_size); From 6b195cf5760a9884541544da13d1e880e36a4ac4 Mon Sep 17 00:00:00 2001 From: Alexander Dahl Date: Fri, 12 Nov 2021 22:43:52 +0100 Subject: [PATCH 525/623] Fixed method not allowed to return Allow header (#243) * test: Replace depracted CURL macro According to CURL source that old macro is long gone, and was replaced. Real issue is: you don't find the old macro in CURL's documentation anymore, which makes it harder to understand what libhttpserver is doing here. Signed-off-by: Alexander Dahl * http_resource: Rename private member Preparation for adding an interface to get a list of only allowed methods aka methods where the boolean property is true. References: #240 Suggested-by: Sebastiano Merlino Signed-off-by: Alexander Dahl * http_resource: Add new public class method 'get_allowed_methods' While the class had public methods for setting and testing the currently allowed HTTP methods, there was no way to discover all at once. A hypothetic user could have build such a list by calling 'is_allowed()' on each possible HTTP method, but the http_resource class itself contains the actual comprehensive list (map) of methods, which would make that approach error prone. Such a list is needed however for constructing the HTTP header 'Allow:' which is mandatory under certain conditions according to RFC. References: #240 Signed-off-by: Alexander Dahl * test: Introduce new unit test suite for http_resource We want to test the recently introduced new API methods. References: #240 Signed-off-by: Alexander Dahl * test: Add check for 'Allow:' header when returning 405 RFC 7231 states an additional header "Allow:" must be sent along with a response with code 405 (Method Not Allowed). References: #240 Link: https://datatracker.ietf.org/doc/html/rfc7231#section-6.5.5 Link: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Allow Signed-off-by: Alexander Dahl * webserver: Send 'Allow:' header along with 405 response RFC 7231 states an additional header "Allow:" MUST be sent along with a response with code 405 (Method Not Allowed). Fixes: #240 Link: https://datatracker.ietf.org/doc/html/rfc7231#section-6.5.5 Link: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Allow Signed-off-by: Alexander Dahl --- src/http_resource.cpp | 20 +++---- src/httpserver/http_resource.hpp | 39 ++++++++++---- src/webserver.cpp | 9 ++++ test/Makefile.am | 3 +- test/integ/basic.cpp | 29 +++++++++- test/unit/http_resource_test.cpp | 93 ++++++++++++++++++++++++++++++++ 6 files changed, 170 insertions(+), 23 deletions(-) create mode 100644 test/unit/http_resource_test.cpp diff --git a/src/http_resource.cpp b/src/http_resource.cpp index 6e75b542..eb1fe976 100644 --- a/src/http_resource.cpp +++ b/src/http_resource.cpp @@ -28,16 +28,16 @@ namespace httpserver { class http_response; } namespace httpserver { // RESOURCE -void resource_init(std::map* allowed_methods) { - (*allowed_methods)[MHD_HTTP_METHOD_GET] = true; - (*allowed_methods)[MHD_HTTP_METHOD_POST] = true; - (*allowed_methods)[MHD_HTTP_METHOD_PUT] = true; - (*allowed_methods)[MHD_HTTP_METHOD_HEAD] = true; - (*allowed_methods)[MHD_HTTP_METHOD_DELETE] = true; - (*allowed_methods)[MHD_HTTP_METHOD_TRACE] = true; - (*allowed_methods)[MHD_HTTP_METHOD_CONNECT] = true; - (*allowed_methods)[MHD_HTTP_METHOD_OPTIONS] = true; - (*allowed_methods)[MHD_HTTP_METHOD_PATCH] = true; +void resource_init(std::map* method_state) { + (*method_state)[MHD_HTTP_METHOD_GET] = true; + (*method_state)[MHD_HTTP_METHOD_POST] = true; + (*method_state)[MHD_HTTP_METHOD_PUT] = true; + (*method_state)[MHD_HTTP_METHOD_HEAD] = true; + (*method_state)[MHD_HTTP_METHOD_DELETE] = true; + (*method_state)[MHD_HTTP_METHOD_TRACE] = true; + (*method_state)[MHD_HTTP_METHOD_CONNECT] = true; + (*method_state)[MHD_HTTP_METHOD_OPTIONS] = true; + (*method_state)[MHD_HTTP_METHOD_PATCH] = true; } namespace details { diff --git a/src/httpserver/http_resource.hpp b/src/httpserver/http_resource.hpp index 0def56d7..9a754718 100644 --- a/src/httpserver/http_resource.hpp +++ b/src/httpserver/http_resource.hpp @@ -33,6 +33,7 @@ #include #include #include +#include namespace httpserver { class http_request; } namespace httpserver { class http_response; } @@ -149,8 +150,8 @@ class http_resource { * @param allowed boolean indicating if the method is allowed or not **/ void set_allowing(const std::string& method, bool allowed) { - if (allowed_methods.count(method)) { - allowed_methods[method] = allowed; + if (method_state.count(method)) { + method_state[method] = allowed; } } @@ -159,8 +160,8 @@ class http_resource { **/ void allow_all() { std::map::iterator it; - for (it=allowed_methods.begin(); it != allowed_methods.end(); ++it) { - allowed_methods[(*it).first] = true; + for (it=method_state.begin(); it != method_state.end(); ++it) { + method_state[(*it).first] = true; } } @@ -169,8 +170,8 @@ class http_resource { **/ void disallow_all() { std::map::iterator it; - for (it=allowed_methods.begin(); it != allowed_methods.end(); ++it) { - allowed_methods[(*it).first] = false; + for (it=method_state.begin(); it != method_state.end(); ++it) { + method_state[(*it).first] = false; } } @@ -180,12 +181,12 @@ class http_resource { * @return true if the method is allowed **/ bool is_allowed(const std::string& method) { - if (allowed_methods.count(method)) { - return allowed_methods[method]; + if (method_state.count(method)) { + return method_state[method]; } else { #ifdef DEBUG std::map::iterator it; - for (it = allowed_methods.begin(); it != allowed_methods.end(); ++it) { + for (it = method_state.begin(); it != method_state.end(); ++it) { std::cout << (*it).first << " -> " << (*it).second << std::endl; } #endif // DEBUG @@ -193,12 +194,28 @@ class http_resource { } } + /** + * Method used to return a list of currently allowed HTTP methods for this resource + * @return vector of strings + **/ + std::vector get_allowed_methods() { + std::vector allowed_methods; + + for (auto it = method_state.cbegin(); it != method_state.cend(); ++it) { + if ( (*it).second ) { + allowed_methods.push_back((*it).first); + } + } + + return allowed_methods; + } + protected: /** * Constructor of the class **/ http_resource() { - resource_init(&allowed_methods); + resource_init(&method_state); } /** @@ -212,7 +229,7 @@ class http_resource { private: friend class webserver; friend void resource_init(std::map* res); - std::map allowed_methods; + std::map method_state; }; } // namespace httpserver diff --git a/src/webserver.cpp b/src/webserver.cpp index 363ac0ce..7a86c9ec 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -601,6 +601,15 @@ MHD_Result webserver::finalize_answer(MHD_Connection* connection, struct details } } else { mr->dhrs = method_not_allowed_page(mr); + + vector allowed_methods = hrm->get_allowed_methods(); + if (allowed_methods.size() > 0) { + string header_value = allowed_methods[0]; + for (auto it = allowed_methods.cbegin() + 1; it != allowed_methods.cend(); ++it) { + header_value += ", " + (*it); + } + mr->dhrs->with_header(http_utils::http_header_allow, header_value); + } } } catch(const std::exception& e) { mr->dhrs = internal_error_page(mr); diff --git a/test/Makefile.am b/test/Makefile.am index e6c91b02..7b8aba08 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -19,7 +19,7 @@ LDADD = $(top_builddir)/src/libhttpserver.la AM_CPPFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/src/httpserver/ METASOURCES = AUTO -check_PROGRAMS = basic http_utils threaded nodelay string_utilities http_endpoint ban_system ws_start_stop authentication deferred +check_PROGRAMS = basic http_utils threaded nodelay string_utilities http_endpoint ban_system ws_start_stop authentication deferred http_resource MOSTLYCLEANFILES = *.gcda *.gcno *.gcov @@ -33,6 +33,7 @@ http_utils_SOURCES = unit/http_utils_test.cpp string_utilities_SOURCES = unit/string_utilities_test.cpp http_endpoint_SOURCES = unit/http_endpoint_test.cpp nodelay_SOURCES = integ/nodelay.cpp +http_resource_SOURCES = unit/http_resource_test.cpp noinst_HEADERS = littletest.hpp AM_CXXFLAGS += -lcurl -Wall -fPIC diff --git a/test/integ/basic.cpp b/test/integ/basic.cpp index 0334317a..e6a0d31f 100644 --- a/test/integ/basic.cpp +++ b/test/integ/basic.cpp @@ -366,7 +366,7 @@ LT_BEGIN_AUTO_TEST(basic_suite, resource_setting_header) curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, headerfunc); - curl_easy_setopt(curl, CURLOPT_WRITEHEADER, &ss); + curl_easy_setopt(curl, CURLOPT_HEADERDATA, &ss); res = curl_easy_perform(curl); LT_ASSERT_EQ(res, 0); LT_CHECK_EQ(s, "OK"); @@ -1021,6 +1021,33 @@ LT_BEGIN_AUTO_TEST(basic_suite, url_with_regex_like_pieces) curl_easy_cleanup(curl); LT_END_AUTO_TEST(url_with_regex_like_pieces) +LT_BEGIN_AUTO_TEST(basic_suite, method_not_allowed_header) + simple_resource resource; + resource.disallow_all(); + resource.set_allowing("POST", true); + resource.set_allowing("HEAD", true); + ws->register_resource("base", &resource); + curl_global_init(CURL_GLOBAL_ALL); + string s; + map ss; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, headerfunc); + curl_easy_setopt(curl, CURLOPT_HEADERDATA, &ss); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + int64_t http_code = 0; + curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code); + LT_ASSERT_EQ(http_code, 405); + // elements in http_resource::method_state are sorted (std::map) + LT_CHECK_EQ(ss["Allow"], "HEAD, POST"); + curl_easy_cleanup(curl); +LT_END_AUTO_TEST(method_not_allowed_header) + LT_BEGIN_AUTO_TEST_ENV() AUTORUN_TESTS() LT_END_AUTO_TEST_ENV() diff --git a/test/unit/http_resource_test.cpp b/test/unit/http_resource_test.cpp new file mode 100644 index 00000000..054f21ee --- /dev/null +++ b/test/unit/http_resource_test.cpp @@ -0,0 +1,93 @@ +/* + This file is part of libhttpserver + Copyright (C) 2021 Alexander Dahl + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#include + +#include +#include +#include +#include + +#include "httpserver.hpp" +#include "littletest.hpp" + +using std::shared_ptr; +using std::sort; +using std::string; +using std::vector; + +using httpserver::http_request; +using httpserver::http_resource; +using httpserver::http_response; +using httpserver::string_response; + +class simple_resource : public http_resource { + public: + const shared_ptr render_GET(const http_request&) { + return shared_ptr(new string_response("OK")); + } +}; + +LT_BEGIN_SUITE(http_resource_suite) + void set_up() { + } + + void tear_down() { + } +LT_END_SUITE(http_resource_suite) + +LT_BEGIN_AUTO_TEST(http_resource_suite, disallow_all_methods) + simple_resource sr; + sr.disallow_all(); + auto allowed_methods = sr.get_allowed_methods(); + LT_CHECK_EQ(allowed_methods.size(), 0); +LT_END_AUTO_TEST(disallow_all_methods) + +LT_BEGIN_AUTO_TEST(http_resource_suite, allow_some_methods) + simple_resource sr; + sr.disallow_all(); + sr.set_allowing(MHD_HTTP_METHOD_GET, true); + sr.set_allowing(MHD_HTTP_METHOD_POST, true); + auto allowed_methods = sr.get_allowed_methods(); + LT_CHECK_EQ(allowed_methods.size(), 2); + // elements in http_resource::method_state are sorted (std::map) + vector some_methods{MHD_HTTP_METHOD_GET, MHD_HTTP_METHOD_POST}; + sort(some_methods.begin(), some_methods.end()); + LT_CHECK_COLLECTIONS_EQ(allowed_methods.cbegin(), allowed_methods.cend(), + some_methods.cbegin()) +LT_END_AUTO_TEST(allow_some_methods) + +LT_BEGIN_AUTO_TEST(http_resource_suite, allow_all_methods) + simple_resource sr; + sr.allow_all(); + auto allowed_methods = sr.get_allowed_methods(); + // elements in http_resource::method_state are sorted (std::map) + vector all_methods{MHD_HTTP_METHOD_GET, MHD_HTTP_METHOD_POST, + MHD_HTTP_METHOD_PUT, MHD_HTTP_METHOD_HEAD, MHD_HTTP_METHOD_DELETE, + MHD_HTTP_METHOD_TRACE, MHD_HTTP_METHOD_CONNECT, + MHD_HTTP_METHOD_OPTIONS, MHD_HTTP_METHOD_PATCH}; + sort(all_methods.begin(), all_methods.end()); + LT_CHECK_COLLECTIONS_EQ(allowed_methods.cbegin(), allowed_methods.cend(), + all_methods.cbegin()) +LT_END_AUTO_TEST(allow_all_methods) + +LT_BEGIN_AUTO_TEST_ENV() + AUTORUN_TESTS() +LT_END_AUTO_TEST_ENV() From 27b401895b9bf02955aa15278a997387218836c5 Mon Sep 17 00:00:00 2001 From: Alexander Dahl Date: Tue, 23 Nov 2021 16:14:20 +0100 Subject: [PATCH 526/623] Use keyword nullptr where possible (#247) Replace every usage of 0x0 or NULL with nullptr, which is available since C++11. NULL, or 0x0, or nullptr, or even 0 was used over the entiry code base before for expressing the concept of a null pointer. Use the C++ special word nullptr instead to unify the code base and minimize friction with expectations of modern C++ developers. References: #241 Signed-off-by: Alexander Dahl --- examples/service.cpp | 2 +- src/deferred_response.cpp | 2 +- src/file_response.cpp | 2 +- src/http_request.cpp | 26 ++++++++-------- src/http_response.cpp | 2 +- src/http_utils.cpp | 10 +++---- src/httpserver/create_webserver.hpp | 16 +++++----- src/httpserver/details/modded_request.hpp | 12 ++++---- src/httpserver/http_request.hpp | 4 +-- src/webserver.cpp | 36 +++++++++++------------ test/integ/basic.cpp | 14 ++++----- test/integ/ws_start_stop.cpp | 4 +-- test/unit/http_utils_test.cpp | 4 +-- 13 files changed, 67 insertions(+), 67 deletions(-) diff --git a/examples/service.cpp b/examples/service.cpp index 8880d96a..3f8ccb03 100644 --- a/examples/service.cpp +++ b/examples/service.cpp @@ -158,7 +158,7 @@ int main(int argc, char **argv) { while ((c = getopt(argc, argv, "p:k:c:sv?")) != EOF) { switch (c) { case 'p': - port = strtoul(optarg, NULL, 10); + port = strtoul(optarg, nullptr, 10); break; case 'k': key = optarg; diff --git a/src/deferred_response.cpp b/src/deferred_response.cpp index fa42134f..f2764810 100644 --- a/src/deferred_response.cpp +++ b/src/deferred_response.cpp @@ -29,7 +29,7 @@ namespace httpserver { namespace details { MHD_Response* get_raw_response_helper(void* cls, ssize_t (*cb)(void*, uint64_t, char*, size_t)) { - return MHD_create_response_from_callback(MHD_SIZE_UNKNOWN, 1024, cb, cls, NULL); + return MHD_create_response_from_callback(MHD_SIZE_UNKNOWN, 1024, cb, cls, nullptr); } } // namespace details diff --git a/src/file_response.cpp b/src/file_response.cpp index 623c7036..3bfdcec3 100644 --- a/src/file_response.cpp +++ b/src/file_response.cpp @@ -35,7 +35,7 @@ MHD_Response* file_response::get_raw_response() { if (size) { return MHD_create_response_from_fd(size, fd); } else { - return MHD_create_response_from_buffer(0, 0x0, MHD_RESPMEM_PERSISTENT); + return MHD_create_response_from_buffer(0, nullptr, MHD_RESPMEM_PERSISTENT); } } diff --git a/src/http_request.cpp b/src/http_request.cpp index 5f29e0cc..4c3f0c82 100644 --- a/src/http_request.cpp +++ b/src/http_request.cpp @@ -58,7 +58,7 @@ bool http_request::check_digest_auth(const std::string& realm, const std::string const std::string http_request::get_connection_value(const std::string& key, enum MHD_ValueKind kind) const { const char* header_c = MHD_lookup_connection_value(underlying_connection, kind, key.c_str()); - if (header_c == NULL) return EMPTY; + if (header_c == nullptr) return EMPTY; return header_c; } @@ -140,7 +140,7 @@ MHD_Result http_request::build_request_args(void *cls, enum MHD_ValueKind kind, std::ignore = kind; arguments_accumulator* aa = static_cast(cls); - std::string value = ((arg_value == NULL) ? "" : arg_value); + std::string value = ((arg_value == nullptr) ? "" : arg_value); http::base_unescaper(&value, aa->unescaper); (*aa->arguments)[key] = value; @@ -152,7 +152,7 @@ MHD_Result http_request::build_request_querystring(void *cls, enum MHD_ValueKind std::ignore = kind; std::string* querystring = static_cast(cls); - std::string value = ((arg_value == NULL) ? "" : arg_value); + std::string value = ((arg_value == nullptr) ? "" : arg_value); int buffer_size = std::string(key).size() + value.size() + 3; char* buf = new char[buffer_size]; @@ -170,14 +170,14 @@ MHD_Result http_request::build_request_querystring(void *cls, enum MHD_ValueKind } const std::string http_request::get_user() const { - char* username = 0x0; - char* password = 0x0; + char* username = nullptr; + char* password = nullptr; username = MHD_basic_auth_get_username_password(underlying_connection, &password); - if (password != 0x0) free(password); + if (password != nullptr) free(password); std::string user; - if (username != 0x0) user = username; + if (username != nullptr) user = username; free(username); @@ -185,14 +185,14 @@ const std::string http_request::get_user() const { } const std::string http_request::get_pass() const { - char* username = 0x0; - char* password = 0x0; + char* username = nullptr; + char* password = nullptr; username = MHD_basic_auth_get_username_password(underlying_connection, &password); - if (username != 0x0) free(username); + if (username != nullptr) free(username); std::string pass; - if (password != 0x0) pass = password; + if (password != nullptr) pass = password; free(password); @@ -200,11 +200,11 @@ const std::string http_request::get_pass() const { } const std::string http_request::get_digested_user() const { - char* digested_user_c = 0x0; + char* digested_user_c = nullptr; digested_user_c = MHD_digest_auth_get_username(underlying_connection); std::string digested_user = EMPTY; - if (digested_user_c != 0x0) { + if (digested_user_c != nullptr) { digested_user = digested_user_c; free(digested_user_c); } diff --git a/src/http_response.cpp b/src/http_response.cpp index 052e58f8..c801fa66 100644 --- a/src/http_response.cpp +++ b/src/http_response.cpp @@ -27,7 +27,7 @@ namespace httpserver { MHD_Response* http_response::get_raw_response() { - return MHD_create_response_from_buffer(0, 0x0, MHD_RESPMEM_PERSISTENT); + return MHD_create_response_from_buffer(0, nullptr, MHD_RESPMEM_PERSISTENT); } void http_response::decorate_response(MHD_Response* response) { diff --git a/src/http_utils.cpp b/src/http_utils.cpp index a79f8f91..e275f128 100644 --- a/src/http_utils.cpp +++ b/src/http_utils.cpp @@ -345,8 +345,8 @@ ip_representation::ip_representation(const std::string& ip) { } if (parts[i].size() == 4) { - pieces[y] = strtol((parts[i].substr(0, 2)).c_str(), NULL, 16); - pieces[y+1] = strtol((parts[i].substr(2, 2)).c_str(), NULL, 16); + pieces[y] = strtol((parts[i].substr(0, 2)).c_str(), nullptr, 16); + pieces[y+1] = strtol((parts[i].substr(2, 2)).c_str(), nullptr, 16); y += 2; } else { @@ -371,7 +371,7 @@ ip_representation::ip_representation(const std::string& ip) { for (unsigned int ii = 0; ii < subparts.size(); ii++) { if (subparts[ii] != "*") { - pieces[y+ii] = strtol(subparts[ii].c_str(), NULL, 10); + pieces[y+ii] = strtol(subparts[ii].c_str(), nullptr, 10); if (pieces[y+ii] > 255) throw std::invalid_argument("IP is badly formatted. 255 is max value for ip part."); } else { CLEAR_BIT(mask, y+ii); @@ -396,7 +396,7 @@ ip_representation::ip_representation(const std::string& ip) { if (parts.size() == 4) { for (unsigned int i = 0; i < parts.size(); i++) { if (parts[i] != "*") { - pieces[12+i] = strtol(parts[i].c_str(), NULL, 10); + pieces[12+i] = strtol(parts[i].c_str(), nullptr, 10); if (pieces[12+i] > 255) throw std::invalid_argument("IP is badly formatted. 255 is max value for ip part."); } else { CLEAR_BIT(mask, 12+i); @@ -481,7 +481,7 @@ void dump_arg_map(std::ostream &os, const std::string &prefix, const std::mapsize(); } diff --git a/src/httpserver/create_webserver.hpp b/src/httpserver/create_webserver.hpp index 74509264..d5e2b07e 100644 --- a/src/httpserver/create_webserver.hpp +++ b/src/httpserver/create_webserver.hpp @@ -335,11 +335,11 @@ class create_webserver { size_t _content_size_limit = static_cast(-1); int _connection_timeout = DEFAULT_WS_TIMEOUT; int _per_IP_connection_limit = 0; - log_access_ptr _log_access = 0x0; - log_error_ptr _log_error = 0x0; - validator_ptr _validator = 0x0; - unescaper_ptr _unescaper = 0x0; - const struct sockaddr* _bind_address = 0x0; + log_access_ptr _log_access = nullptr; + log_error_ptr _log_error = nullptr; + validator_ptr _validator = nullptr; + unescaper_ptr _unescaper = nullptr; + const struct sockaddr* _bind_address = nullptr; int _bind_socket = 0; int _max_thread_stack_size = 0; bool _use_ssl = false; @@ -363,9 +363,9 @@ class create_webserver { bool _deferred_enabled = false; bool _single_resource = false; bool _tcp_nodelay = false; - render_ptr _not_found_resource = 0x0; - render_ptr _method_not_allowed_resource = 0x0; - render_ptr _internal_error_resource = 0x0; + render_ptr _not_found_resource = nullptr; + render_ptr _method_not_allowed_resource = nullptr; + render_ptr _internal_error_resource = nullptr; friend class webserver; }; diff --git a/src/httpserver/details/modded_request.hpp b/src/httpserver/details/modded_request.hpp index a0c0086c..c4f18638 100644 --- a/src/httpserver/details/modded_request.hpp +++ b/src/httpserver/details/modded_request.hpp @@ -35,14 +35,14 @@ namespace httpserver { namespace details { struct modded_request { - struct MHD_PostProcessor *pp = 0x0; - std::string* complete_uri = 0x0; - std::string* standardized_url = 0x0; - webserver* ws = 0x0; + struct MHD_PostProcessor *pp = nullptr; + std::string* complete_uri = nullptr; + std::string* standardized_url = nullptr; + webserver* ws = nullptr; const std::shared_ptr (httpserver::http_resource::*callback)(const httpserver::http_request&); - http_request* dhr = 0x0; + http_request* dhr = nullptr; std::shared_ptr dhrs; bool second = false; bool has_body = false; @@ -56,7 +56,7 @@ struct modded_request { modded_request& operator=(modded_request&& b) = default; ~modded_request() { - if (NULL != pp) { + if (nullptr != pp) { MHD_destroy_post_processor(pp); } if (second) { diff --git a/src/httpserver/http_request.hpp b/src/httpserver/http_request.hpp index 2c426c05..00bc2d45 100644 --- a/src/httpserver/http_request.hpp +++ b/src/httpserver/http_request.hpp @@ -244,9 +244,9 @@ class http_request { size_t content_size_limit = static_cast(-1); std::string version; - struct MHD_Connection* underlying_connection = 0x0; + struct MHD_Connection* underlying_connection = nullptr; - unescaper_ptr unescaper = 0x0; + unescaper_ptr unescaper = nullptr; static MHD_Result build_request_header(void *cls, enum MHD_ValueKind kind, const char *key, const char *value); diff --git a/src/webserver.cpp b/src/webserver.cpp index 7a86c9ec..a1392bdc 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -159,8 +159,8 @@ webserver::webserver(const create_webserver& params): method_not_allowed_resource(params._method_not_allowed_resource), internal_error_resource(params._internal_error_resource) { ignore_sigpipe(); - pthread_mutex_init(&mutexwait, NULL); - pthread_cond_init(&mutexcond, NULL); + pthread_mutex_init(&mutexwait, nullptr); + pthread_cond_init(&mutexcond, nullptr); } webserver::~webserver() { @@ -180,10 +180,10 @@ void webserver::request_completed(void *cls, struct MHD_Connection *connection, std::ignore = toe; details::modded_request* mr = static_cast(*con_cls); - if (mr == 0x0) return; + if (mr == nullptr) return; delete mr; - mr = 0x0; + mr = nullptr; } bool webserver::register_resource(const std::string& resource, http_resource* hrm, bool family) { @@ -204,14 +204,14 @@ bool webserver::register_resource(const std::string& resource, http_resource* hr bool webserver::start(bool blocking) { struct { - MHD_OptionItem operator ()(enum MHD_OPTION opt, intptr_t val, void *ptr = 0) { + MHD_OptionItem operator ()(enum MHD_OPTION opt, intptr_t val, void *ptr = nullptr) { MHD_OptionItem x = {opt, val, ptr}; return x; } } gen; vector iov; - iov.push_back(gen(MHD_OPTION_NOTIFY_COMPLETED, (intptr_t) &request_completed, NULL)); + iov.push_back(gen(MHD_OPTION_NOTIFY_COMPLETED, (intptr_t) &request_completed, nullptr)); iov.push_back(gen(MHD_OPTION_URI_LOG_CALLBACK, (intptr_t) &uri_log, this)); iov.push_back(gen(MHD_OPTION_EXTERNAL_LOGGER, (intptr_t) &error_log, this)); iov.push_back(gen(MHD_OPTION_UNESCAPE_CALLBACK, (intptr_t) &unescaper_func, this)); @@ -279,7 +279,7 @@ bool webserver::start(bool blocking) { } #endif // HAVE_GNUTLS - iov.push_back(gen(MHD_OPTION_END, 0, NULL)); + iov.push_back(gen(MHD_OPTION_END, 0, nullptr)); int start_conf = start_method; @@ -310,8 +310,8 @@ bool webserver::start(bool blocking) { start_conf |= MHD_USE_TCP_FASTOPEN; #endif - daemon = NULL; - if (bind_address == 0x0) { + daemon = nullptr; + if (bind_address == nullptr) { daemon = MHD_start_daemon(start_conf, port, &policy_callback, this, &answer_to_connection, this, MHD_OPTION_ARRAY, &iov[0], MHD_OPTION_END); @@ -321,7 +321,7 @@ bool webserver::start(bool blocking) { &iov[0], MHD_OPTION_SOCK_ADDR, bind_address, MHD_OPTION_END); } - if (daemon == NULL) { + if (daemon == nullptr) { throw std::invalid_argument("Unable to connect daemon to port: " + std::to_string(port)); } @@ -430,11 +430,11 @@ void error_log(void* cls, const char* fmt, va_list ap) { std::ignore = ap; webserver* dws = static_cast(cls); - if (dws->log_error != 0x0) dws->log_error(fmt); + if (dws->log_error != nullptr) dws->log_error(fmt); } void access_log(webserver* dws, string uri) { - if (dws->log_access != 0x0) dws->log_access(uri); + if (dws->log_access != nullptr) dws->log_access(uri); } size_t unescaper_func(void * cls, struct MHD_Connection *c, char *s) { @@ -472,7 +472,7 @@ void webserver::upgrade_handler(void *cls, struct MHD_Connection* connection, vo } const std::shared_ptr webserver::not_found_page(details::modded_request* mr) const { - if (not_found_resource != 0x0) { + if (not_found_resource != nullptr) { return not_found_resource(*mr->dhr); } else { return std::shared_ptr(new string_response(NOT_FOUND_ERROR, http_utils::http_not_found)); @@ -480,7 +480,7 @@ const std::shared_ptr webserver::not_found_page(details::modded_r } const std::shared_ptr webserver::method_not_allowed_page(details::modded_request* mr) const { - if (method_not_allowed_resource != 0x0) { + if (method_not_allowed_resource != nullptr) { return method_not_allowed_resource(*mr->dhr); } else { return std::shared_ptr(new string_response(METHOD_ERROR, http_utils::http_method_not_allowed)); @@ -488,7 +488,7 @@ const std::shared_ptr webserver::method_not_allowed_page(details: } const std::shared_ptr webserver::internal_error_page(details::modded_request* mr, bool force_our) const { - if (internal_error_resource != 0x0 && !force_our) { + if (internal_error_resource != nullptr && !force_our) { return internal_error_resource(*mr->dhr); } else { return std::shared_ptr(new string_response(GENERIC_ERROR, http_utils::http_internal_server_error, "text/plain")); @@ -507,13 +507,13 @@ MHD_Result webserver::requests_answer_first_step(MHD_Connection* connection, str const char *encoding = MHD_lookup_connection_value(connection, MHD_HEADER_KIND, http_utils::http_header_content_type); if (post_process_enabled && - (0x0 != encoding && + (nullptr != encoding && ((0 == strncasecmp(http_utils::http_post_encoding_form_urlencoded, encoding, strlen(http_utils::http_post_encoding_form_urlencoded))) || (0 == strncasecmp(http_utils::http_post_encoding_multipart_formdata, encoding, strlen(http_utils::http_post_encoding_multipart_formdata)))))) { const size_t post_memory_limit(32 * 1024); // Same as #MHD_POOL_SIZE_DEFAULT mr->pp = MHD_create_post_processor(connection, post_memory_limit, &post_iterator, mr); } else { - mr->pp = NULL; + mr->pp = nullptr; } return MHD_YES; } @@ -529,7 +529,7 @@ MHD_Result webserver::requests_answer_second_step(MHD_Connection* connection, co #endif // DEBUG mr->dhr->grow_content(upload_data, *upload_data_size); - if (mr->pp != NULL) MHD_post_process(mr->pp, upload_data, *upload_data_size); + if (mr->pp != nullptr) MHD_post_process(mr->pp, upload_data, *upload_data_size); } *upload_data_size = 0; diff --git a/test/integ/basic.cpp b/test/integ/basic.cpp index e6a0d31f..7c8669c2 100644 --- a/test/integ/basic.cpp +++ b/test/integ/basic.cpp @@ -416,7 +416,7 @@ LT_BEGIN_AUTO_TEST(basic_suite, request_with_header) curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); - struct curl_slist *list = NULL; + struct curl_slist *list = nullptr; list = curl_slist_append(list, "MyHeader: MyValue"); curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list); @@ -502,7 +502,7 @@ LT_BEGIN_AUTO_TEST(basic_suite, complete) CURL* curl = curl_easy_init(); curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); curl_easy_setopt(curl, CURLOPT_POST, 1L); - curl_easy_setopt(curl, CURLOPT_POSTFIELDS, NULL); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, nullptr); curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, 0); CURLcode res = curl_easy_perform(curl); LT_ASSERT_EQ(res, 0); @@ -579,7 +579,7 @@ LT_BEGIN_AUTO_TEST(basic_suite, only_render) curl = curl_easy_init(); curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); curl_easy_setopt(curl, CURLOPT_POST, 1L); - curl_easy_setopt(curl, CURLOPT_POSTFIELDS, NULL); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, nullptr); curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, 0); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); @@ -931,8 +931,8 @@ LT_BEGIN_AUTO_TEST(basic_suite, request_is_printable) curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); - struct curl_slist *list = NULL; - list = curl_slist_append(NULL, "MyHeader: MyValue"); + struct curl_slist *list = nullptr; + list = curl_slist_append(nullptr, "MyHeader: MyValue"); curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list); res = curl_easy_perform(curl); @@ -966,8 +966,8 @@ LT_BEGIN_AUTO_TEST(basic_suite, response_is_printable) curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); - struct curl_slist *list = NULL; - list = curl_slist_append(NULL, "MyHeader: MyValue"); + struct curl_slist *list = nullptr; + list = curl_slist_append(nullptr, "MyHeader: MyValue"); curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list); res = curl_easy_perform(curl); diff --git a/test/integ/ws_start_stop.cpp b/test/integ/ws_start_stop.cpp index 89506afc..0d9b77cc 100644 --- a/test/integ/ws_start_stop.cpp +++ b/test/integ/ws_start_stop.cpp @@ -494,14 +494,14 @@ void* start_ws_blocking(void* par) { ws->register_resource("base", &ok); ws->start(true); - return 0x0; + return nullptr; } LT_BEGIN_AUTO_TEST(ws_start_stop_suite, blocking_server) httpserver::webserver ws = httpserver::create_webserver(8080); pthread_t tid; - pthread_create(&tid, NULL, start_ws_blocking, reinterpret_cast(&ws)); + pthread_create(&tid, nullptr, start_ws_blocking, reinterpret_cast(&ws)); sleep(1); diff --git a/test/unit/http_utils_test.cpp b/test/unit/http_utils_test.cpp index 5a31bfb9..0bf80024 100644 --- a/test/unit/http_utils_test.cpp +++ b/test/unit/http_utils_test.cpp @@ -193,7 +193,7 @@ LT_BEGIN_AUTO_TEST(http_utils_suite, ip_to_str_invalid_family) LT_END_AUTO_TEST(ip_to_str_invalid_family) LT_BEGIN_AUTO_TEST(http_utils_suite, ip_to_str_null) - LT_CHECK_THROW(httpserver::http::get_ip_str((struct sockaddr*) 0x0)); + LT_CHECK_THROW(httpserver::http::get_ip_str((struct sockaddr*) nullptr)); LT_END_AUTO_TEST(ip_to_str_null) LT_BEGIN_AUTO_TEST(http_utils_suite, get_port_invalid_family) @@ -207,7 +207,7 @@ LT_BEGIN_AUTO_TEST(http_utils_suite, get_port_invalid_family) LT_END_AUTO_TEST(get_port_invalid_family) LT_BEGIN_AUTO_TEST(http_utils_suite, get_port_null) - LT_CHECK_THROW(httpserver::http::get_port((struct sockaddr*) 0x0)); + LT_CHECK_THROW(httpserver::http::get_port((struct sockaddr*) nullptr)); LT_END_AUTO_TEST(get_port_null) LT_BEGIN_AUTO_TEST(http_utils_suite, ip_representation4_str) From 0a02a1eed1602969ac33d58317a4be936bb446af Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Mon, 29 Nov 2021 21:24:35 -0800 Subject: [PATCH 527/623] Update AUTHORS to add @lespocky --- AUTHORS | 3 +++ 1 file changed, 3 insertions(+) diff --git a/AUTHORS b/AUTHORS index 06796b4c..890327a0 100644 --- a/AUTHORS +++ b/AUTHORS @@ -45,3 +45,6 @@ Jagat - Simplification of microhttpd dependency management Christian Grothoff + +- Fixes to the behavior of "Method Not Allowed" and other cleanups +Alexander Dahl From 6718e0a634f4570ac24b9b91063555ca9774a1c6 Mon Sep 17 00:00:00 2001 From: Alexander Dahl Date: Sun, 12 Dec 2021 23:16:20 +0100 Subject: [PATCH 528/623] file_response: Change default argument for content-type (#249) The default value for content-type should be as broad and unspecific as possible, to allow using file_response and don't care about the content-type. Mozilla recommends to use application/octet-stream for data with unknown type. text/plain is too specific for using files of unknown type with the file_response class, and could lead to browsers misinterpreting, showing garbage, or even breaking sites. Link: https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types Fixes: #248 Signed-off-by: Alexander Dahl --- src/http_utils.cpp | 1 + src/httpserver/file_response.hpp | 2 +- src/httpserver/http_utils.hpp | 1 + test/integ/basic.cpp | 25 +++++++++++++++++++++++++ 4 files changed, 28 insertions(+), 1 deletion(-) diff --git a/src/http_utils.cpp b/src/http_utils.cpp index e275f128..203bbb16 100644 --- a/src/http_utils.cpp +++ b/src/http_utils.cpp @@ -190,6 +190,7 @@ const char* http_utils::http_method_patch = MHD_HTTP_METHOD_PATCH; const char* http_utils::http_post_encoding_form_urlencoded = MHD_HTTP_POST_ENCODING_FORM_URLENCODED; const char* http_utils::http_post_encoding_multipart_formdata = MHD_HTTP_POST_ENCODING_MULTIPART_FORMDATA; +const char* http_utils::application_octet_stream = "application/octet-stream"; const char* http_utils::text_plain = "text/plain"; std::vector http_utils::tokenize_url(const std::string& str, const char separator) { diff --git a/src/httpserver/file_response.hpp b/src/httpserver/file_response.hpp index 51e5103d..71e8984f 100644 --- a/src/httpserver/file_response.hpp +++ b/src/httpserver/file_response.hpp @@ -40,7 +40,7 @@ class file_response : public http_response { explicit file_response( const std::string& filename, int response_code = http::http_utils::http_ok, - const std::string& content_type = http::http_utils::text_plain): + const std::string& content_type = http::http_utils::application_octet_stream): http_response(response_code, content_type), filename(filename) { } diff --git a/src/httpserver/http_utils.hpp b/src/httpserver/http_utils.hpp index 566955fb..71a051d3 100644 --- a/src/httpserver/http_utils.hpp +++ b/src/httpserver/http_utils.hpp @@ -232,6 +232,7 @@ class http_utils { static const char* http_post_encoding_form_urlencoded; static const char* http_post_encoding_multipart_formdata; + static const char* application_octet_stream; static const char* text_plain; static std::vector tokenize_url(const std::string&, const char separator = '/'); diff --git a/test/integ/basic.cpp b/test/integ/basic.cpp index 7c8669c2..05b771ab 100644 --- a/test/integ/basic.cpp +++ b/test/integ/basic.cpp @@ -214,6 +214,13 @@ class file_response_resource_empty : public http_resource { } }; +class file_response_resource_default_content_type : public http_resource { + public: + const shared_ptr render_GET(const http_request&) { + return shared_ptr(new file_response("test_content", 200)); + } +}; + class exception_resource : public http_resource { public: const shared_ptr render_GET(const http_request&) { @@ -871,6 +878,24 @@ LT_BEGIN_AUTO_TEST(basic_suite, file_serving_resource_empty) curl_easy_cleanup(curl); LT_END_AUTO_TEST(file_serving_resource_empty) +LT_BEGIN_AUTO_TEST(basic_suite, file_serving_resource_default_content_type) + file_response_resource_default_content_type resource; + ws->register_resource("base", &resource); + curl_global_init(CURL_GLOBAL_ALL); + + map ss; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, headerfunc); + curl_easy_setopt(curl, CURLOPT_HEADERDATA, &ss); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(ss["Content-Type"], "application/octet-stream"); + curl_easy_cleanup(curl); +LT_END_AUTO_TEST(file_serving_resource_default_content_type) + LT_BEGIN_AUTO_TEST(basic_suite, exception_forces_500) exception_resource resource; ws->register_resource("base", &resource); From 2d7ee351a52babac260b8bec86777321ffd2d54f Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Wed, 22 Dec 2021 23:32:43 -0800 Subject: [PATCH 529/623] Remove forced std=C++11 (#250) The library should determine automatically the flag (ideally using C++14) --- configure.ac | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/configure.ac b/configure.ac index 81030212..46e51ca7 100644 --- a/configure.ac +++ b/configure.ac @@ -1,6 +1,6 @@ # # This file is part of libhttpserver -# Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino +# Copyright (C) 2011-2021 Sebastiano Merlino # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -118,7 +118,7 @@ if test x"$host" = x"$build"; then [AC_MSG_ERROR(["microhttpd.h not found"])] ) - CXXFLAGS="-std=c++11 -DHTTPSERVER_COMPILATION -D_REENTRANT $LIBMICROHTTPD_CFLAGS $CXXFLAGS" + CXXFLAGS="-DHTTPSERVER_COMPILATION -D_REENTRANT $LIBMICROHTTPD_CFLAGS $CXXFLAGS" LDFLAGS="$LIBMICROHTTPD_LIBS $NETWORK_LIBS $ADDITIONAL_LIBS $LDFLAGS" cond_cross_compile="no" @@ -131,7 +131,7 @@ else [AC_MSG_ERROR(["microhttpd.h not found"])] ) - CXXFLAGS="-std=c++11 -DHTTPSERVER_COMPILATION -D_REENTRANT $CXXFLAGS" + CXXFLAGS="-DHTTPSERVER_COMPILATION -D_REENTRANT $CXXFLAGS" LDFLAGS="$NETWORK_LIBS $ADDITIONAL_LIBS $LDFLAGS" cond_cross_compile="yes" From 48495b0aacfd12f9ff6e0a71e2031fb61d4cec90 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 16 Jan 2022 14:48:16 -0800 Subject: [PATCH 530/623] Adding tools now missing in appveyor (#252) --- appveyor.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/appveyor.yml b/appveyor.yml index fa44b1df..3847c65e 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -16,6 +16,7 @@ init: - ps: iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1')) install: - 'if "%compiler%"=="msys2" C:\msys64\msys2_shell.cmd -defterm -no-start -msys2 -c "pacman --noconfirm -S --needed mingw-w64-$MSYS2_ARCH-{libtool,make,pkg-config,libsystre,doxygen,gnutls,graphviz,curl}"' + - 'if "%compiler%"=="msys2" C:\msys64\msys2_shell.cmd -defterm -no-start -msys2 -c "pacman --noconfirm -S --needed autotools"' - 'if "%compiler%"=="msys2" C:\msys64\msys2_shell.cmd -defterm -no-start -mingw64 -full-path -here -c "cd $APPVEYOR_BUILD_FOLDER && curl https://s3.amazonaws.com/libhttpserver/libmicrohttpd_releases/libmicrohttpd-0.9.59.tar.gz -o libmicrohttpd-0.9.59.tar.gz"' - 'if "%compiler%"=="msys2" C:\msys64\msys2_shell.cmd -defterm -no-start -mingw64 -full-path -here -c "cd $APPVEYOR_BUILD_FOLDER && tar -xzf libmicrohttpd-0.9.59.tar.gz"' - 'if "%compiler%"=="msys2" C:\msys64\msys2_shell.cmd -defterm -no-start -mingw64 -full-path -here -c "cd $APPVEYOR_BUILD_FOLDER/libmicrohttpd-0.9.59 && ./configure --disable-examples --enable-poll=no --prefix /C/msys64 && make && make install"' From d79b092f6286cd3d766a46e0a6710cc907976e08 Mon Sep 17 00:00:00 2001 From: LIMITLESS-IO <63381667+LIMITLESS-IO@users.noreply.github.com> Date: Mon, 17 Jan 2022 19:20:50 +0200 Subject: [PATCH 531/623] Update README.md (#251) fixed a typo in README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0385f54c..5acd3c13 100644 --- a/README.md +++ b/README.md @@ -157,7 +157,7 @@ You can also check this example on [github](https://github.com/etr/libhttpserver * _http_request:_ Represents the request received by the resource that process it. * _http_response:_ Represents the response sent by the server once the resource finished its work. * _string_response:_ A simple string response. - * _file_response:_ A response getting content from a fail. + * _file_response:_ A response getting content from a file. * _basic_auth_fail_response:_ A failure in basic authentication. * _digest_auth_fail_response:_ A failure in digest authentication. * _deferred_response:_ A response getting content from a callback. From ae1ab5d4f1b6612323e2ef275cc89051df94c89c Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Wed, 19 Jan 2022 14:20:17 -0800 Subject: [PATCH 532/623] Remove --build option from bootstrap (#254) It was unused and did not work --- bootstrap | 8 -------- 1 file changed, 8 deletions(-) diff --git a/bootstrap b/bootstrap index bc1ad731..7de7c1be 100755 --- a/bootstrap +++ b/bootstrap @@ -26,11 +26,3 @@ else fi automake --add-missing autoconf - -# Optionally do the build as well. -if [ "$1" = "-build" -o "$1" = "--build" ] ; then - shift - ./configure "$@" - make - make check -fi From 5cb0be15745b335b1aed1414ba00055cf6e98b05 Mon Sep 17 00:00:00 2001 From: Alexander Dahl Date: Thu, 3 Feb 2022 17:50:50 +0100 Subject: [PATCH 533/623] Fix the webserver class to return 500 on invalid MHD Response This particularly applies in case of a file_response (in case the file doesn't exist or is a directory). * webserver: Return 500 on invalid MHD response If `http_response::get_raw_response()` returns nullptr instead of a valid `MHD_Response*` for whatever reason, that pointer would be passed on to `http_response::decorate_response()` and `http_response::enqueue_response()` eventually, leading to different API calls to libmicrohttpd with NULL as argument `struct MHD_Response *response`. MHD does not guarantee any form of behaviour for invalid input, so we have to consider it undefined behaviour and avoid passing invalid input to MHD. HTTP status 500 (Internal Server Error) is returned for consistency with surrounding error handling, we don't know what caused `http_response::get_raw_response()` to return nullptr, so we can not give a better answer here. Fixes: #255 * file_response: Add return value checks Both `open()` and `lseek()` might fail depending on how `filename` is set in object of class `httpserver::file_response()`. In the case of a missing file *fd* got -1 and lseek set *size* (which had the wrong type btw.) to 0xffffffff aka (off_t) -1. Passing an invalid file descriptor and a massively huge size value on to `MHD_create_response_from_fd()` might lead to unpredictable results depending how well libmicrohttpd treats such invalid values. Note: Before f9b769106435 ("Use MHD_create_response_from_fd for files") `httpserver::http::load_file()` was used, which throws an exception in case a file can not be opened successfully. That exception would have lead to returning HTTP status 500 (Internal Server Error). References: #255 * test: Add unit test for missing file response The constructor of class `httpserver::file_response` can be called with a `filename` pointing into the void, to a file which does not actually exist. The webserver should fail predictably in that case. References: #255 * file_response: Test on regular file It was possible before to pass a path to a directory, or a to a device file, or to basically any path. `open()` would happily open it. In case of a directory, `lseek()` returns LONG_MAX (0x7FFFFFFFFFFFFFFF) and `MHD_create_response_from_fd()` is invoked. To avoid such nonsense, we test the path now and allow regular files only. References: #255 * file_response: Add API doc to constructor This documents a possible pitfall for users when passing filename of not existing files. References: #255 * test: Add unit test for file_response pointing to directory The constructor of class `httpserver::file_response` can be called with a `filename` pointing to a directory instead of a regular file. The webserver should fail predictably in that case. References: #255 * readme: Document requirements and behaviour of file_response Suggested-by: Sebastiano Merlino References: #255 --- README.md | 2 +- src/file_response.cpp | 17 ++++++++- src/httpserver/file_response.hpp | 13 +++++++ src/webserver.cpp | 4 +++ test/integ/basic.cpp | 60 ++++++++++++++++++++++++++++++++ 5 files changed, 94 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 5acd3c13..d76c84a6 100644 --- a/README.md +++ b/README.md @@ -603,7 +603,7 @@ As seen in the documentation of [http_resource](#the-resource-object), every ext There are 5 types of response that you can create - we will describe them here through their constructors: * _string_response(**const std::string&** content, **int** response_code = `200`, **const std::string&** content_type = `"text/plain"`):_ The most basic type of response. It uses the `content` string passed in construction as body of the HTTP response. The other two optional parameters are the `response_code` and the `content_type`. You can find constant definition for the various response codes within the [http_utils](https://github.com/etr/libhttpserver/blob/master/src/httpserver/http_utils.hpp) library file. -* _file_response(**const std::string&** filename, **int** response_code = `200`, **const std::string&** content_type = `"text/plain"`):_ Uses the `filename` passed in construction as pointer to a file on disk. The body of the HTTP response will be set using the content of the file. The other two optional parameters are the `response_code` and the `content_type`. You can find constant definition for the various response codes within the [http_utils](https://github.com/etr/libhttpserver/blob/master/src/httpserver/http_utils.hpp) library file. +* _file_response(**const std::string&** filename, **int** response_code = `200`, **const std::string&** content_type = `"text/plain"`):_ Uses the `filename` passed in construction as pointer to a file on disk. The body of the HTTP response will be set using the content of the file. The file must be a regular file and exist on disk. Otherwise libhttpserver will return an error 500 (Internal Server Error). The other two optional parameters are the `response_code` and the `content_type`. You can find constant definition for the various response codes within the [http_utils](https://github.com/etr/libhttpserver/blob/master/src/httpserver/http_utils.hpp) library file. * _basic_auth_fail_response(**const std::string&** content, **const std::string&** realm = `""`, **int** response_code = `200`, **const std::string&** content_type = `"text/plain"`):_ A response in return to a failure during basic authentication. It allows to specify a `content` string as a message to send back to the client. The `realm` parameter should contain your realm of authentication (if any). The other two optional parameters are the `response_code` and the `content_type`. You can find constant definition for the various response codes within the [http_utils](https://github.com/etr/libhttpserver/blob/master/src/httpserver/http_utils.hpp) library file. * _digest_auth_fail_response(**const std::string&** content, **const std::string&** realm = `""`, **const std::string&** opaque = `""`, **bool** reload_nonce = `false`, **int** response_code = `200`, **const std::string&** content_type = `"text/plain"`):_ A response in return to a failure during digest authentication. It allows to specify a `content` string as a message to send back to the client. The `realm` parameter should contain your realm of authentication (if any). The `opaque` represents a value that gets passed to the client and expected to be passed again to the server as-is. This value can be a hexadecimal or base64 string. The `reload_nonce` parameter tells the server to reload the nonce (you should use the value returned by the `check_digest_auth` method on the `http_request`. The other two optional parameters are the `response_code` and the `content_type`. You can find constant definition for the various response codes within the [http_utils](https://github.com/etr/libhttpserver/blob/master/src/httpserver/http_utils.hpp) library file. * _deferred_response(**ssize_t(*cycle_callback_ptr)(shared_ptr<T>, char*, size_t)** cycle_callback, **const std::string&** content = `""`, **int** response_code = `200`, **const std::string&** content_type = `"text/plain"`):_ A response that obtains additional content from a callback executed in a deferred way. It leaves the client in pending state (returning a `100 CONTINUE` message) and suspends the connection. Besides the callback, optionally, you can provide a `content` parameter that sets the initial message sent immediately to the client. The other two optional parameters are the `response_code` and the `content_type`. You can find constant definition for the various response codes within the [http_utils](https://github.com/etr/libhttpserver/blob/master/src/httpserver/http_utils.hpp) library file. To use `deferred_response` you need to have the `deferred` option active on your webserver (enabled by default). diff --git a/src/file_response.cpp b/src/file_response.cpp index 3bfdcec3..669f5e8b 100644 --- a/src/file_response.cpp +++ b/src/file_response.cpp @@ -22,6 +22,8 @@ #include #include #include +#include +#include #include #include @@ -30,8 +32,21 @@ struct MHD_Response; namespace httpserver { MHD_Response* file_response::get_raw_response() { + struct stat sb; + + // Deny everything but regular files + if (stat(filename.c_str(), &sb) == 0) { + if (!S_ISREG(sb.st_mode)) return nullptr; + } else { + return nullptr; + } + int fd = open(filename.c_str(), O_RDONLY); - size_t size = lseek(fd, 0, SEEK_END); + if (fd == -1) return nullptr; + + off_t size = lseek(fd, 0, SEEK_END); + if (size == (off_t) -1) return nullptr; + if (size) { return MHD_create_response_from_fd(size, fd); } else { diff --git a/src/httpserver/file_response.hpp b/src/httpserver/file_response.hpp index 71e8984f..9e03a0c9 100644 --- a/src/httpserver/file_response.hpp +++ b/src/httpserver/file_response.hpp @@ -37,6 +37,19 @@ class file_response : public http_response { public: file_response() = default; + /** + * Constructor of the class file_response. You usually use this to pass a + * filename to the instance. + * @param filename Name of the file which content should be sent with the + * response. User must make sure file exists and is a + * regular file, otherwise libhttpserver will return a + * generic response with HTTP status 500 (Internal Server + * Error). + * @param response_code HTTP response code in good case, optional, + * default is 200 (OK). + * @param content_type Mime type of the file content, e.g. "text/html", + * optional, default is "application/octet-stream". + **/ explicit file_response( const std::string& filename, int response_code = http::http_utils::http_ok, diff --git a/src/webserver.cpp b/src/webserver.cpp index a1392bdc..4c4a034a 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -623,6 +623,10 @@ MHD_Result webserver::finalize_answer(MHD_Connection* connection, struct details try { try { raw_response = mr->dhrs->get_raw_response(); + if (raw_response == nullptr) { + mr->dhrs = internal_error_page(mr); + raw_response = mr->dhrs->get_raw_response(); + } } catch(const std::invalid_argument& iae) { mr->dhrs = not_found_page(mr); raw_response = mr->dhrs->get_raw_response(); diff --git a/test/integ/basic.cpp b/test/integ/basic.cpp index 05b771ab..3d2aca50 100644 --- a/test/integ/basic.cpp +++ b/test/integ/basic.cpp @@ -221,6 +221,20 @@ class file_response_resource_default_content_type : public http_resource { } }; +class file_response_resource_missing : public http_resource { + public: + const shared_ptr render_GET(const http_request&) { + return shared_ptr(new file_response("missing", 200)); + } +}; + +class file_response_resource_dir : public http_resource { + public: + const shared_ptr render_GET(const http_request&) { + return shared_ptr(new file_response("integ", 200)); + } +}; + class exception_resource : public http_resource { public: const shared_ptr render_GET(const http_request&) { @@ -896,6 +910,52 @@ LT_BEGIN_AUTO_TEST(basic_suite, file_serving_resource_default_content_type) curl_easy_cleanup(curl); LT_END_AUTO_TEST(file_serving_resource_default_content_type) +LT_BEGIN_AUTO_TEST(basic_suite, file_serving_resource_missing) + file_response_resource_missing resource; + ws->register_resource("base", &resource); + curl_global_init(CURL_GLOBAL_ALL); + + string s; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "Internal Error"); + + int64_t http_code = 0; + curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code); + LT_ASSERT_EQ(http_code, 500); + + curl_easy_cleanup(curl); +LT_END_AUTO_TEST(file_serving_resource_missing) + +LT_BEGIN_AUTO_TEST(basic_suite, file_serving_resource_dir) + file_response_resource_dir resource; + ws->register_resource("base", &resource); + curl_global_init(CURL_GLOBAL_ALL); + + string s; + CURL *curl = curl_easy_init(); + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/base"); + curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s); + res = curl_easy_perform(curl); + LT_ASSERT_EQ(res, 0); + LT_CHECK_EQ(s, "Internal Error"); + + int64_t http_code = 0; + curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code); + LT_ASSERT_EQ(http_code, 500); + + curl_easy_cleanup(curl); +LT_END_AUTO_TEST(file_serving_resource_dir) + LT_BEGIN_AUTO_TEST(basic_suite, exception_forces_500) exception_resource resource; ws->register_resource("base", &resource); From 4eb69fbc823257104fd39a518a2f7c91edc6ef86 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Thu, 3 Feb 2022 08:54:38 -0800 Subject: [PATCH 534/623] Updated the Authors file to add new contributions --- AUTHORS | 2 ++ 1 file changed, 2 insertions(+) diff --git a/AUTHORS b/AUTHORS index 890327a0..a30f6b9e 100644 --- a/AUTHORS +++ b/AUTHORS @@ -47,4 +47,6 @@ Jagat Christian Grothoff - Fixes to the behavior of "Method Not Allowed" and other cleanups +- Fixed the behavior of the server when receiving invalid responses from libmicrohttpd. +- Fixed the file_response to handle not existing files and directories. Alexander Dahl From a85929712111a46558c5867fc420e43e3ef845ff Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Sun, 20 Feb 2022 14:24:36 -0800 Subject: [PATCH 535/623] Using full path for includes (#260) --- src/httpserver/basic_auth_fail_response.hpp | 2 +- src/httpserver/deferred_response.hpp | 2 +- src/httpserver/digest_auth_fail_response.hpp | 2 +- src/httpserver/file_response.hpp | 2 +- src/httpserver/string_response.hpp | 2 +- src/httpserver/webserver.hpp | 2 +- test/integ/authentication.cpp | 4 ++-- test/integ/ban_system.cpp | 4 ++-- test/integ/basic.cpp | 4 ++-- test/integ/deferred.cpp | 4 ++-- test/integ/nodelay.cpp | 4 ++-- test/integ/threaded.cpp | 4 ++-- test/integ/ws_start_stop.cpp | 4 ++-- test/unit/http_endpoint_test.cpp | 2 +- test/unit/http_resource_test.cpp | 4 ++-- test/unit/http_utils_test.cpp | 2 +- test/unit/string_utilities_test.cpp | 2 +- 17 files changed, 25 insertions(+), 25 deletions(-) diff --git a/src/httpserver/basic_auth_fail_response.hpp b/src/httpserver/basic_auth_fail_response.hpp index 11cf6820..87a124f5 100644 --- a/src/httpserver/basic_auth_fail_response.hpp +++ b/src/httpserver/basic_auth_fail_response.hpp @@ -26,7 +26,7 @@ #define SRC_HTTPSERVER_BASIC_AUTH_FAIL_RESPONSE_HPP_ #include -#include "http_utils.hpp" +#include "httpserver/http_utils.hpp" #include "httpserver/string_response.hpp" struct MHD_Connection; diff --git a/src/httpserver/deferred_response.hpp b/src/httpserver/deferred_response.hpp index 5ad6071d..85f8791f 100644 --- a/src/httpserver/deferred_response.hpp +++ b/src/httpserver/deferred_response.hpp @@ -30,7 +30,7 @@ #include #include #include -#include "http_utils.hpp" +#include "httpserver/http_utils.hpp" #include "httpserver/string_response.hpp" struct MHD_Response; diff --git a/src/httpserver/digest_auth_fail_response.hpp b/src/httpserver/digest_auth_fail_response.hpp index 0cd22f85..bbc1543a 100644 --- a/src/httpserver/digest_auth_fail_response.hpp +++ b/src/httpserver/digest_auth_fail_response.hpp @@ -26,7 +26,7 @@ #define SRC_HTTPSERVER_DIGEST_AUTH_FAIL_RESPONSE_HPP_ #include -#include "http_utils.hpp" +#include "httpserver/http_utils.hpp" #include "httpserver/string_response.hpp" struct MHD_Connection; diff --git a/src/httpserver/file_response.hpp b/src/httpserver/file_response.hpp index 9e03a0c9..c85978ef 100644 --- a/src/httpserver/file_response.hpp +++ b/src/httpserver/file_response.hpp @@ -26,7 +26,7 @@ #define SRC_HTTPSERVER_FILE_RESPONSE_HPP_ #include -#include "http_utils.hpp" +#include "httpserver/http_utils.hpp" #include "httpserver/http_response.hpp" struct MHD_Response; diff --git a/src/httpserver/string_response.hpp b/src/httpserver/string_response.hpp index d50eb417..d2bff4a8 100644 --- a/src/httpserver/string_response.hpp +++ b/src/httpserver/string_response.hpp @@ -27,7 +27,7 @@ #include #include -#include "http_utils.hpp" +#include "httpserver/http_utils.hpp" #include "httpserver/http_response.hpp" struct MHD_Response; diff --git a/src/httpserver/webserver.hpp b/src/httpserver/webserver.hpp index 763ba86d..1a5b1255 100644 --- a/src/httpserver/webserver.hpp +++ b/src/httpserver/webserver.hpp @@ -45,7 +45,7 @@ #include #include -#include "http_utils.hpp" +#include "httpserver/http_utils.hpp" #include "httpserver/create_webserver.hpp" #include "httpserver/details/http_endpoint.hpp" diff --git a/test/integ/authentication.cpp b/test/integ/authentication.cpp index 3ed4d993..e43d9f8c 100644 --- a/test/integ/authentication.cpp +++ b/test/integ/authentication.cpp @@ -32,8 +32,8 @@ #include -#include "httpserver.hpp" -#include "littletest.hpp" +#include "./httpserver.hpp" +#include "./littletest.hpp" #define MY_OPAQUE "11733b200778ce33060f31c9af70a870ba96ddd4" diff --git a/test/integ/ban_system.cpp b/test/integ/ban_system.cpp index a5a86a1a..e845f3d4 100644 --- a/test/integ/ban_system.cpp +++ b/test/integ/ban_system.cpp @@ -22,9 +22,9 @@ #include #include -#include "httpserver.hpp" +#include "./httpserver.hpp" #include "httpserver/http_utils.hpp" -#include "littletest.hpp" +#include "./littletest.hpp" using std::shared_ptr; diff --git a/test/integ/basic.cpp b/test/integ/basic.cpp index 3d2aca50..a30756a8 100644 --- a/test/integ/basic.cpp +++ b/test/integ/basic.cpp @@ -23,9 +23,9 @@ #include #include -#include "httpserver.hpp" +#include "./httpserver.hpp" #include "httpserver/string_utilities.hpp" -#include "littletest.hpp" +#include "./littletest.hpp" using std::string; using std::map; diff --git a/test/integ/deferred.cpp b/test/integ/deferred.cpp index 9c093adf..0b37e322 100644 --- a/test/integ/deferred.cpp +++ b/test/integ/deferred.cpp @@ -34,8 +34,8 @@ #include #include -#include "httpserver.hpp" -#include "littletest.hpp" +#include "./httpserver.hpp" +#include "./littletest.hpp" using std::shared_ptr; using std::string; diff --git a/test/integ/nodelay.cpp b/test/integ/nodelay.cpp index 21aa74b9..8da00355 100644 --- a/test/integ/nodelay.cpp +++ b/test/integ/nodelay.cpp @@ -22,8 +22,8 @@ #include #include -#include "httpserver.hpp" -#include "littletest.hpp" +#include "./httpserver.hpp" +#include "./littletest.hpp" using std::shared_ptr; diff --git a/test/integ/threaded.cpp b/test/integ/threaded.cpp index 08abe2f7..39ef02cc 100644 --- a/test/integ/threaded.cpp +++ b/test/integ/threaded.cpp @@ -26,8 +26,8 @@ #include #include -#include "httpserver.hpp" -#include "littletest.hpp" +#include "./httpserver.hpp" +#include "./littletest.hpp" using std::shared_ptr; diff --git a/test/integ/ws_start_stop.cpp b/test/integ/ws_start_stop.cpp index 0d9b77cc..0db04230 100644 --- a/test/integ/ws_start_stop.cpp +++ b/test/integ/ws_start_stop.cpp @@ -34,8 +34,8 @@ #include #include -#include "httpserver.hpp" -#include "littletest.hpp" +#include "./httpserver.hpp" +#include "./littletest.hpp" using std::shared_ptr; diff --git a/test/unit/http_endpoint_test.cpp b/test/unit/http_endpoint_test.cpp index 2199af0c..1cce2bcd 100644 --- a/test/unit/http_endpoint_test.cpp +++ b/test/unit/http_endpoint_test.cpp @@ -20,7 +20,7 @@ #include "httpserver/details/http_endpoint.hpp" -#include "littletest.hpp" +#include "./littletest.hpp" using httpserver::details::http_endpoint; using std::string; diff --git a/test/unit/http_resource_test.cpp b/test/unit/http_resource_test.cpp index 054f21ee..84b6efdc 100644 --- a/test/unit/http_resource_test.cpp +++ b/test/unit/http_resource_test.cpp @@ -25,8 +25,8 @@ #include #include -#include "httpserver.hpp" -#include "littletest.hpp" +#include "./httpserver.hpp" +#include "./littletest.hpp" using std::shared_ptr; using std::sort; diff --git a/test/unit/http_utils_test.cpp b/test/unit/http_utils_test.cpp index 0bf80024..f1b63afc 100644 --- a/test/unit/http_utils_test.cpp +++ b/test/unit/http_utils_test.cpp @@ -32,7 +32,7 @@ #include -#include "littletest.hpp" +#include "./littletest.hpp" using std::string; using std::vector; diff --git a/test/unit/string_utilities_test.cpp b/test/unit/string_utilities_test.cpp index 9fb284d0..0bc7a213 100644 --- a/test/unit/string_utilities_test.cpp +++ b/test/unit/string_utilities_test.cpp @@ -22,7 +22,7 @@ #include -#include "littletest.hpp" +#include "./littletest.hpp" using std::string; using std::vector; From 45739fa93d6277340706d3bf555e69dc9b52ef61 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Mon, 21 Feb 2022 08:55:04 -0800 Subject: [PATCH 536/623] Adding empty codecov file to enable coverage on PR --- codecov.yml | 1 + 1 file changed, 1 insertion(+) create mode 100644 codecov.yml diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/codecov.yml @@ -0,0 +1 @@ + From 7a3a9129cfcd1c8faad8753141ef978a4c03c9fe Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Mon, 21 Feb 2022 08:59:57 -0800 Subject: [PATCH 537/623] Fix codacy badge --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d76c84a6..c47c756a 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ Copyright (C) 2011-2019 Sebastiano Merlino. ![GA: Build Status](https://github.com/etr/libhttpserver/actions/workflows/verify-build.yml/badge.svg) [![Build status](https://ci.appveyor.com/api/projects/status/ktoy6ewkrf0q1hw6/branch/master?svg=true)](https://ci.appveyor.com/project/etr/libhttpserver/branch/master) [![codecov](https://codecov.io/gh/etr/libhttpserver/branch/master/graph/badge.svg)](https://codecov.io/gh/etr/libhttpserver) -[![Codacy Badge](https://api.codacy.com/project/badge/Grade/5fa4bdc3815b4c10977f3badefedecd6)](https://www.codacy.com/app/etr/libhttpserver?utm_source=github.com&utm_medium=referral&utm_content=etr/libhttpserver&utm_campaign=Badge_Grade) +[![Codacy Badge](https://app.codacy.com/project/badge/Grade/1bd1e8c21f66400fb70e5a5ce357b525)](https://www.codacy.com/gh/etr/libhttpserver/dashboard?utm_source=github.com&utm_medium=referral&utm_content=etr/libhttpserver&utm_campaign=Badge_Grade) [![Gitter chat](https://badges.gitter.im/etr/libhttpserver.png)](https://gitter.im/libhttpserver/community) [![ko-fi](https://www.ko-fi.com/img/donate_sm.png)](https://ko-fi.com/F1F5HY8B) From 6e58cd005d75c389f0513f8e085b0b87f271aa5b Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Mon, 21 Feb 2022 09:08:27 -0800 Subject: [PATCH 538/623] Using default codecov coverage --- codecov.yml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/codecov.yml b/codecov.yml index 8b137891..a6e1de74 100644 --- a/codecov.yml +++ b/codecov.yml @@ -1 +1,20 @@ +codecov: + require_ci_to_pass: yes +coverage: + precision: 2 + round: down + range: "70...100" + +parsers: + gcov: + branch_detection: + conditional: yes + loop: yes + method: no + macro: no + +comment: + layout: "reach,diff,flags,files,footer" + behavior: default + require_changes: no From bd559af1a2ead64445d04ecdb2e640897ae77b38 Mon Sep 17 00:00:00 2001 From: Alexander Dahl Date: Thu, 24 Feb 2022 20:23:09 +0100 Subject: [PATCH 539/623] Moved minimal libmicrohttpd dep to v0.9.74 (#259) This resolves the warnings on deprecated collections. * http_utils: Remove 'http_unordered_collection' That HTTP status code was removed from RFC and marked deprecated in libmicrohttpd from v0.9.64 onwards. MHD throws a deprecation warning now when using MHD_HTTP_UNORDERED_COLLECTION, leading to build failures if -Werror is set (as with libhttpserver debug builds). Fixes: #176 Suggested-by: Sebastiano Merlino Signed-off-by: Alexander Dahl * http_utils: Redefine deprecated MHD definitions libmicrohttpd deprecated those two definitions, and replaced them with new ones for the same numeric codes with version v0.9.74. Compiler throws a warning when using those definitions in libhttpserver, and thus debug build fails due to -Werror. Fixes: #258 Signed-off-by: Alexander Dahl * Bump libmicrohttpd for CI builds We are about to update the minimal required version of libmicrohttpd to 0.9.64 and depend on that in configure.ac and CI should not fail then. * github: codeql: Move building libmicrohttpd before analyze init Previously analyze init came before building libmicrohttpd which let CodeQL analyze libmicrohttpd as well. Since libmicrohttpd is not under our control, each change in that could introduce distracting analyze warnings/errors. Apparently CodeQL analyzes everything built after that init step for compiled languages. Moving dependencies before init seems to solve that. * Bump libmicrohttpd required version to 0.9.64 libmicrohttpd deprecated the definition of MHD_HTTP_UNORDERED_COLLECTION with 0.9.64 without alternative. Thus `http_utils::http_unordered_collection` was removed from libhttpserver and the requirement bump reflects those changes. Goal is to get rid of the deprecation warnings reported with #176 and #258. libmicrohttpd 0.9.64 was released in June 2019 (2019-06-09), almost three years ago. --- .github/workflows/codeql-analysis.yml | 20 ++++++++++---------- .github/workflows/verify-build.yml | 10 +++++----- README.md | 2 +- appveyor.yml | 6 +++--- configure.ac | 8 ++++---- src/http_utils.cpp | 11 ++++++++--- src/httpserver/http_utils.hpp | 1 - 7 files changed, 31 insertions(+), 27 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index fb1041db..cc0f95be 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -36,6 +36,15 @@ jobs: - run: git checkout HEAD^2 if: ${{ github.event_name == 'pull_request' }} + - name: Install libmicrohttpd dependency + run: | + curl https://s3.amazonaws.com/libhttpserver/libmicrohttpd_releases/libmicrohttpd-0.9.64.tar.gz -o libmicrohttpd-0.9.64.tar.gz ; + tar -xzf libmicrohttpd-0.9.64.tar.gz ; + cd libmicrohttpd-0.9.64 ; + ./configure --disable-examples ; + make ; + sudo make install ; + # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL uses: github/codeql-action/init@v1 @@ -54,20 +63,11 @@ jobs: # and modify them (or add more) to build your code if your project # uses a compiled language - - name: Install libmicrohttpd dependency - run: | - curl https://s3.amazonaws.com/libhttpserver/libmicrohttpd_releases/libmicrohttpd-0.9.59.tar.gz -o libmicrohttpd-0.9.59.tar.gz ; - tar -xzf libmicrohttpd-0.9.59.tar.gz ; - cd libmicrohttpd-0.9.59 ; - ./configure --disable-examples ; - make ; - sudo make install ; - - name: Manual steps to build the library run: | ./bootstrap ; ./configure --enable-same-directory-build; make ; - + - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v1 diff --git a/.github/workflows/verify-build.yml b/.github/workflows/verify-build.yml index 8be4c692..97b081e5 100644 --- a/.github/workflows/verify-build.yml +++ b/.github/workflows/verify-build.yml @@ -396,20 +396,20 @@ jobs: id: cache-libmicrohttpd uses: actions/cache@v2 with: - path: libmicrohttpd-0.9.59 + path: libmicrohttpd-0.9.64 key: ${{ matrix.os }}-${{ matrix.c-compiler }}-libmicrohttpd-pre-built - name: Build libmicrohttpd dependency (if not cached) run: | - curl https://s3.amazonaws.com/libhttpserver/libmicrohttpd_releases/libmicrohttpd-0.9.59.tar.gz -o libmicrohttpd-0.9.59.tar.gz ; - tar -xzf libmicrohttpd-0.9.59.tar.gz ; - cd libmicrohttpd-0.9.59 ; + curl https://s3.amazonaws.com/libhttpserver/libmicrohttpd_releases/libmicrohttpd-0.9.64.tar.gz -o libmicrohttpd-0.9.64.tar.gz ; + tar -xzf libmicrohttpd-0.9.64.tar.gz ; + cd libmicrohttpd-0.9.64 ; ./configure --disable-examples ; make ; if: steps.cache-libmicrohttpd.outputs.cache-hit != 'true' - name: Install libmicrohttpd - run: cd libmicrohttpd-0.9.59 ; sudo make install ; + run: cd libmicrohttpd-0.9.64 ; sudo make install ; - name: Refresh links to shared libs run: sudo ldconfig ; diff --git a/README.md b/README.md index c47c756a..34592205 100644 --- a/README.md +++ b/README.md @@ -79,7 +79,7 @@ libhttpserver can be used without any dependencies aside from libmicrohttpd. The minimum versions required are: * g++ >= 5.5.0 or clang-3.6 -* libmicrohttpd >= 0.9.53 +* libmicrohttpd >= 0.9.64 * [Optionally]: for TLS (HTTPS) support, you'll need [libgnutls](http://www.gnutls.org/). * [Optionally]: to compile the code-reference, you'll need [doxygen](http://www.doxygen.nl/). diff --git a/appveyor.yml b/appveyor.yml index 3847c65e..bc2fb9e7 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -17,9 +17,9 @@ init: install: - 'if "%compiler%"=="msys2" C:\msys64\msys2_shell.cmd -defterm -no-start -msys2 -c "pacman --noconfirm -S --needed mingw-w64-$MSYS2_ARCH-{libtool,make,pkg-config,libsystre,doxygen,gnutls,graphviz,curl}"' - 'if "%compiler%"=="msys2" C:\msys64\msys2_shell.cmd -defterm -no-start -msys2 -c "pacman --noconfirm -S --needed autotools"' - - 'if "%compiler%"=="msys2" C:\msys64\msys2_shell.cmd -defterm -no-start -mingw64 -full-path -here -c "cd $APPVEYOR_BUILD_FOLDER && curl https://s3.amazonaws.com/libhttpserver/libmicrohttpd_releases/libmicrohttpd-0.9.59.tar.gz -o libmicrohttpd-0.9.59.tar.gz"' - - 'if "%compiler%"=="msys2" C:\msys64\msys2_shell.cmd -defterm -no-start -mingw64 -full-path -here -c "cd $APPVEYOR_BUILD_FOLDER && tar -xzf libmicrohttpd-0.9.59.tar.gz"' - - 'if "%compiler%"=="msys2" C:\msys64\msys2_shell.cmd -defterm -no-start -mingw64 -full-path -here -c "cd $APPVEYOR_BUILD_FOLDER/libmicrohttpd-0.9.59 && ./configure --disable-examples --enable-poll=no --prefix /C/msys64 && make && make install"' + - 'if "%compiler%"=="msys2" C:\msys64\msys2_shell.cmd -defterm -no-start -mingw64 -full-path -here -c "cd $APPVEYOR_BUILD_FOLDER && curl https://s3.amazonaws.com/libhttpserver/libmicrohttpd_releases/libmicrohttpd-0.9.64.tar.gz -o libmicrohttpd-0.9.64.tar.gz"' + - 'if "%compiler%"=="msys2" C:\msys64\msys2_shell.cmd -defterm -no-start -mingw64 -full-path -here -c "cd $APPVEYOR_BUILD_FOLDER && tar -xzf libmicrohttpd-0.9.64.tar.gz"' + - 'if "%compiler%"=="msys2" C:\msys64\msys2_shell.cmd -defterm -no-start -mingw64 -full-path -here -c "cd $APPVEYOR_BUILD_FOLDER/libmicrohttpd-0.9.64 && ./configure --disable-examples --enable-poll=no --prefix /C/msys64 && make && make install"' - 'if "%compiler%"=="msys2" C:\msys64\msys2_shell.cmd -defterm -no-start -mingw64 -full-path -here -c "cd $APPVEYOR_BUILD_FOLDER && ./bootstrap"' - 'if "%compiler%"=="msys2" C:\msys64\msys2_shell.cmd -defterm -no-start -mingw64 -full-path -here -c "cd $APPVEYOR_BUILD_FOLDER && mkdir build && cd build && MANIFEST_TOOL=no; ../configure --disable-fastopen --prefix /C/msys64 CXXFLAGS=-I/C/msys64/include LDFLAGS=-L/C/msys64/lib; make"' build_script: diff --git a/configure.ac b/configure.ac index 46e51ca7..bac1c1c6 100644 --- a/configure.ac +++ b/configure.ac @@ -100,17 +100,17 @@ AC_CHECK_HEADER([gnutls/gnutls.h],[have_gnutls="yes"],[AC_MSG_WARN("gnutls/gnutl if test x"$host" = x"$build"; then AC_CHECK_HEADER([microhttpd.h], AC_CHECK_LIB([microhttpd], [MHD_get_fdset2], - [AC_MSG_CHECKING([for libmicrohttpd >= 0.9.53]) + [AC_MSG_CHECKING([for libmicrohttpd >= 0.9.64]) AC_COMPILE_IFELSE( [AC_LANG_SOURCE([ #include - #if (MHD_VERSION < 0x00095300) - #error needs at least version 0.9.53 + #if (MHD_VERSION < 0x00096400) + #error needs at least version 0.9.64 #endif int main () { return 0; } ])], [], - [AC_MSG_ERROR("libmicrohttpd is too old - install libmicrohttpd >= 0.9.53")] + [AC_MSG_ERROR("libmicrohttpd is too old - install libmicrohttpd >= 0.9.64")] ) ], [AC_MSG_ERROR(["libmicrohttpd not found"])] diff --git a/src/http_utils.cpp b/src/http_utils.cpp index 203bbb16..85a5047a 100644 --- a/src/http_utils.cpp +++ b/src/http_utils.cpp @@ -61,6 +61,12 @@ typedef unsigned char u_char; #endif // CYGWIN +// libmicrohttpd deprecated some definitions with v0.9.74, and introduced new ones +#if MHD_VERSION < 0x00097314 +#define MHD_HTTP_CONTENT_TOO_LARGE MHD_HTTP_PAYLOAD_TOO_LARGE +#define MHD_HTTP_UNPROCESSABLE_CONTENT MHD_HTTP_UNPROCESSABLE_ENTITY +#endif + namespace httpserver { namespace http { @@ -101,15 +107,14 @@ const int http_utils::http_conflict = MHD_HTTP_CONFLICT; const int http_utils::http_gone = MHD_HTTP_GONE; const int http_utils::http_length_required = MHD_HTTP_LENGTH_REQUIRED; const int http_utils::http_precondition_failed = MHD_HTTP_PRECONDITION_FAILED; -const int http_utils::http_request_entity_too_large = MHD_HTTP_PAYLOAD_TOO_LARGE; +const int http_utils::http_request_entity_too_large = MHD_HTTP_CONTENT_TOO_LARGE; const int http_utils::http_request_uri_too_long = MHD_HTTP_URI_TOO_LONG; const int http_utils::http_unsupported_media_type = MHD_HTTP_UNSUPPORTED_MEDIA_TYPE; const int http_utils::http_requested_range_not_satisfiable = MHD_HTTP_RANGE_NOT_SATISFIABLE; const int http_utils::http_expectation_failed = MHD_HTTP_EXPECTATION_FAILED; -const int http_utils::http_unprocessable_entity = MHD_HTTP_UNPROCESSABLE_ENTITY; +const int http_utils::http_unprocessable_entity = MHD_HTTP_UNPROCESSABLE_CONTENT; const int http_utils::http_locked = MHD_HTTP_LOCKED; const int http_utils::http_failed_dependency = MHD_HTTP_FAILED_DEPENDENCY; -const int http_utils::http_unordered_collection = MHD_HTTP_UNORDERED_COLLECTION; const int http_utils::http_upgrade_required = MHD_HTTP_UPGRADE_REQUIRED; const int http_utils::http_retry_with = MHD_HTTP_RETRY_WITH; diff --git a/src/httpserver/http_utils.hpp b/src/httpserver/http_utils.hpp index 71a051d3..b768fe6c 100644 --- a/src/httpserver/http_utils.hpp +++ b/src/httpserver/http_utils.hpp @@ -150,7 +150,6 @@ class http_utils { static const int http_unprocessable_entity; static const int http_locked; static const int http_failed_dependency; - static const int http_unordered_collection; static const int http_upgrade_required; static const int http_retry_with; From 3fd99c87b8ea999df593060999be59786e844bba Mon Sep 17 00:00:00 2001 From: Christian Tomahogh Date: Thu, 3 Mar 2022 23:39:24 +0100 Subject: [PATCH 540/623] Add options to store files directly to the file system and to omit duplicating files in memory (#257) * Add options to store files directly to the file system and to omit duplicating files in memory * Delete accidentially added newline in http_resource.hpp * Removed unused function, fixed some code style and typos in comments * Removed asprintf in favor or string concatenation As asprintf is a gnu extension it should not be used in a multi plattform code. To generate the needed string, std::string is used As the finally called function mkstemp changes its first argument of type char * the concatenated string is still strdup-ed into a char * before handing it to mkstemp * Made http_utils::generate_random_upload_filename plattform independant Mkstemp is only available on windows. As there is no suitable plattform independant function to generate a unique filename within a specified directory, use mkstemp on linux and a combination of _mktemp_s and _sopen_s on windows. Furthermore the path separator differs on linux and windows so use a static const char which provides the appropriate path separator Additionally within this patch the function http_utils::generate_random_upload_filename uses an exception to reports errors rather than a empty string as return code * Input and output of http_utils::generate_random_upload_filename are const * Added options and functions for file upload to README.md * Use correct include and functions in generate_random_upload_filename to run on windows The current code won't compile on ApVeyor. So this is a an untested patch to include the correct headers and not use functions, which are not supported (or deprecated) on windows. * Add missing includes for windows, fixed wrong argument for _mktemp_s * Removed accidentially pushed debug code * Use a static const char* for the random file system template This is only used inside http_utils.cpp and therefor can be static * Make output of get_or_create_file_info const and introduce new setters The output of get_or_create_file_info should be const. As previously the result was used to direclty alter the content of the struct new setters to set the file_system_file_name and to grow the size are introduced (and used) * Use const variables for the webserver options * Added an example for file upload file_upload.cpp is a very basic example for a file upload server. It features a simple HTML input form with file upload (which supports multiple files). The uploaded files will be stored to the directory, which has to be given as command line parameter to the example program. All uploaded files will be printed in a table including their original name, the generated random filepath and the size. * Use a prefixed operator in for loop * Added safety check for use of strlen and some missing free()s Static code analysis complained about use of unprotected strlen. This changes adds a safety check directly before the us of strlen. Additionally some free()s (before some throws) where missing. * Changed file map to be a map of keys with a nested map of files There is the possibility to upload the same file with two different keys or to upload multiple (different) files with the same key within the same request. The current solution can only handle the second use case but not the first. This patch changes the file map to use a nested map of files within a map of keys to support both use cases. * Adjusted file_upload example to use new files map * Updated description of files map in README.md * Removed strlen from generate_random_upload_filename As Codacy static code analysis warns about the safety issue of strlen if a given string is not terminated, this is removed. The solution uses the size of the original string, which is just duplicated to a char* as the used function _mktemp_s requires a char* which will be changed by the function and the changed result is needed afterwards. * Use a pointer to std::ofstream in modded_request One of the workflows did not run successfully, as it is obviously not allowed to have a std::ofstream in a class if it is not a pointer or reference. This patch uses a pointer to the ofstream in the modded requests and adjusts all usage of the ofstream in webserver.cpp * Moved struct file_info_s to class file_info * Setters of class file_info are private and class webserver is a friend of this class As the file_info has to be changed (especially the file_size) in multiple iterations of the post iterator, this class cannot be fully immutable. To realise a interface without setters to the outside of the library these setters are private now. The only other classe, which uses these setters is the class webserver and is therefor declared as friend to the file_info class. * Use const reference as parameter for set_file_system_file_name * Don't create zero length files if no file is uploaded If an upload field is submitted without a file selected, the previous implementation would still create a random file with zero length. This is due to the fact, that the key would be present in the content, but the filename is empty. So a simple check, whether the filename is really present is introduced. This is done by simply looking at the very first sign of the const char* (which is given from libmicrohttpd). As strlen is considered unsafe and there is already a check, whether the const char* is not a nullptr, it is always safe to dereference and inspect the first char. * Updated README.md as file_info is now a class * Some code styling and small refactorings after review * Replaced std::exception with custom generateFilenameException Although there is no distinct error handling, if the filename generation throws an exception, it should be usable in the future and destinguishable from other common exceptions. Therefor the std::exception was replaced by generateFilenameException * Some more comments and code style fixes after review * Some more code style changes after review * Added error string to generateFilenameException Though it is not used at the moment the generateFilenameException features now a error string, which is set and can be used for debugging. * Added comment for c functions in generate_random_upload_filename Actually we should use standard C++ classes like ofstream for file operations. But as these do not feature some wanted functions, this is done by C functions. This patch adds some comments to explain the reasons for this decision. * Removed const qualifier from file_info::get_file_size() This is removed as the const qualifier is superfluous and generates warning on pedantic checks. * Added unit test for generate_random_upload_filename This is a first show on a unit test for the function generate_random_upload_filename. After calling the function, this unit test not only checks the returned filename, but also, whether the file exists. Maybe to be improved: - the filename template and the path_separator are currently duplicated - should there be a check, whether the removal of the temporary file worked? * Removed filesystem include from unit tests again, as it would require C++17 * Close open upload file after post processor is done If the file is not closed after the post processor is done, the last info of the file might not be written to disk until the calling application accesses the file. This could easily be reproduceable with uploading very small files ( < 100 Bytes). In this case the content of the file was missing for the calling process. * Added content_type and transfer_encoding to file_info The post iterator can set the content_type and the transfer_encoding of the uploaded file into the file_info. According to the documentation of libmicrohttpd this is sometimes unknown. In this case the values will be nullptr and therefor the file_info may be empty on this values. * Fixed file_upload example * Made filename_template and path_separator part of class http_utils As these two constant values are used in the unit tests they could no longer be static only in http_utils.cpp. So they are members of the class http_utils and therefor accessible in the unit tests * Added integration test for file upload Added an integration test for file upload which tests the upload with different settings of the webserver: - Upload to memory and disk - Upload to disk only - Upload to memory, duplicated in get_content and args - Upload to memory, only stored in args * Fixed integration test for file upload * Removed empty AUTO_TEST from file_upload integration test * Use internal values for http_ressource class in integration tests Avoid using pointers in class print_file_upload_resource in favour of internal values and according getter functions. Additionally simplified the code by wrapping lines and using the auto keyword instead of long map specifiers. * Added further integration tests for file upload Added the following integration tests - Upload of a file via PUT (then there is no post_processor used and the content of the file will only be available via get_content - Upload of two files - Upload of a file and an additional ordinary POST parameter * Added test_content_2 to configure script As the test_content_2 was missing in the configure script the file was not copy to the build/test directory and therefor the integration tests failed * Fixed memory leak in post_iterator If multiple files are uploaded, the ofstream to write the data to the file was created again (with new()) for the next file without deleting the previously created one. This resulted in a memory leak. * Some code style fixed on file upload integration test * Use map definition instead of auto in integration test Co-authored-by: Christian Tomahogh --- README.md | 9 + configure.ac | 1 + examples/Makefile.am | 3 +- examples/file_upload.cpp | 116 +++++ src/Makefile.am | 4 +- src/file_info.cpp | 56 +++ src/http_request.cpp | 4 + src/http_utils.cpp | 52 +++ src/httpserver.hpp | 1 + src/httpserver/create_webserver.hpp | 34 ++ src/httpserver/details/modded_request.hpp | 6 + src/httpserver/file_info.hpp | 61 +++ src/httpserver/http_request.hpp | 18 + src/httpserver/http_utils.hpp | 24 ++ src/httpserver/webserver.hpp | 4 + src/webserver.cpp | 83 +++- test/Makefile.am | 5 +- test/integ/file_upload.cpp | 492 ++++++++++++++++++++++ test/test_content_2 | 1 + test/unit/http_utils_test.cpp | 17 + 20 files changed, 978 insertions(+), 13 deletions(-) create mode 100644 examples/file_upload.cpp create mode 100644 src/file_info.cpp create mode 100644 src/httpserver/file_info.hpp create mode 100644 test/integ/file_upload.cpp create mode 100644 test/test_content_2 diff --git a/README.md b/README.md index 34592205..270a8dd8 100644 --- a/README.md +++ b/README.md @@ -190,6 +190,13 @@ For example, if your connection limit is “1”, a browser may open a first con * _.debug() and .no_debug():_ Enables debug messages from the library. `off` by default. * _.regex_checking() and .no_regex_checking():_ Enables pattern matching for endpoints. Read more [here](#registering-resources). `on` by default. * _.post_process() and .no_post_process():_ Enables/Disables the library to automatically parse the body of the http request as arguments if in querystring format. Read more [here](#parsing-requests). `on` by default. +* _.put_processed_data_to_content() and .no_put_processed_data_to_content():_ Enables/Disables the library to copy parsed body data to the content or to only store it in the arguments map. `on` by default. +* _.file_upload_target(**file_upload_target_T** file_upload_target):_ Controls, how the library stores uploaded files. Default value is `FILE_UPLOAD_MEMORY_ONLY`. + * `FILE_UPLOAD_MEMORY_ONLY`: The content of the file is only stored in memory. Depending on `put_processed_data_to_content` only as part of the arguments map or additionally in the content. + * `FILE_UPLOAD_DISK_ONLY`: The content of the file is stored only in the file system. The path is created from `file_upload_dir` and either a random name (if `generate_random_filename_on_upload` is true) or the actually uploaded file name. + * `FILE_UPLOAD_MEMORY_AND_DISK`: The content of the file is stored in memory and on the file system. +* _.file_upload_dir(**const std::string&** file_upload_dir):_ Specifies the directory to store all uploaded files. Default value is `/tmp`. +* _.generate_random_filename_on_upload() and .no_generate_random_filename_on_upload():_ Enables/Disables the library to generate a unique and unused filename to store the uploaded file to. Otherwise the actually uploaded file name is used. `off` by default. * _.deferred()_ and _.no_deferred():_ Enables/Disables the ability for the server to suspend and resume connections. Simply put, it enables/disables the ability to use `deferred_response`. Read more [here](#building-responses-to-requests). `on` by default. * _.single_resource() and .no_single_resource:_ Sets or unsets the server in single resource mode. This limits all endpoints to be served from a single resource. The resultant is that the webserver will process the request matching to the endpoint skipping any complex semantic. Because of this, the option is incompatible with `regex_checking` and requires the resource to be registered against an empty endpoint or the root endpoint (`"/"`). The resource will also have to be registered as family. (For more information on resource registration, read more [here](#registering-resources)). `off` by default. @@ -553,6 +560,8 @@ The `http_request` class has a set of methods you will have access to when imple * _**const std::map** get_cookies() **const**:_ Returns a map containing all the cookies present in the HTTP request. * _**const std::map** get_footers() **const**:_ Returns a map containing all the footers present in the HTTP request (only for http 1.1 chunked encodings). * _**const std::map** get_args() **const**:_ Returns all the arguments present in the HTTP request. Arguments can be (1) querystring parameters, (2) path argument (in case of parametric endpoint, (3) parameters parsed from the HTTP request body if the body is in `application/x-www-form-urlencoded` or `multipart/form-data` formats and the postprocessor is enabled in the webserver (enabled by default). +* _**const std::map** get_files() **const**:_ Returns information about all the uploaded files (if the files are stored to disk). This information includes the original file name, the size of the file and the path to the file in the file system. +* _**const std::map>** get_files() **const**:_ Returns information about all the uploaded files (if the files are stored to disk). This information includes the key (as identifier of the outer map), the original file name (as identifier of the inner map) and a class `file_info`, which includes the size of the file and the path to the file in the file system. * _**const std::string&** get_content() **const**:_ Returns the body of the HTTP request. * _**bool** content_too_large() **const**:_ Returns `true` if the body length of the HTTP request sent by the client is longer than the max allowed on the server. * _**const std::string** get_querystring() **const**:_ Returns the `querystring` of the HTTP request. diff --git a/configure.ac b/configure.ac index bac1c1c6..23adba36 100644 --- a/configure.ac +++ b/configure.ac @@ -273,6 +273,7 @@ AC_SUBST(EXT_LIB_PATH) AC_SUBST(EXT_LIBS) AC_CONFIG_FILES([test/test_content:test/test_content]) +AC_CONFIG_FILES([test/test_content_2:test/test_content_2]) AC_CONFIG_FILES([test/test_content_empty:test/test_content_empty]) AC_CONFIG_FILES([test/cert.pem:test/cert.pem]) AC_CONFIG_FILES([test/key.pem:test/key.pem]) diff --git a/examples/Makefile.am b/examples/Makefile.am index 318a7a8d..0fe116af 100644 --- a/examples/Makefile.am +++ b/examples/Makefile.am @@ -19,7 +19,7 @@ LDADD = $(top_builddir)/src/libhttpserver.la AM_CPPFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/src/httpserver/ METASOURCES = AUTO -noinst_PROGRAMS = hello_world service minimal_hello_world custom_error allowing_disallowing_methods handlers hello_with_get_arg setting_headers custom_access_log basic_authentication digest_authentication minimal_https minimal_file_response minimal_deferred url_registration minimal_ip_ban benchmark_select benchmark_threads benchmark_nodelay deferred_with_accumulator +noinst_PROGRAMS = hello_world service minimal_hello_world custom_error allowing_disallowing_methods handlers hello_with_get_arg setting_headers custom_access_log basic_authentication digest_authentication minimal_https minimal_file_response minimal_deferred url_registration minimal_ip_ban benchmark_select benchmark_threads benchmark_nodelay deferred_with_accumulator file_upload hello_world_SOURCES = hello_world.cpp service_SOURCES = service.cpp @@ -41,3 +41,4 @@ minimal_ip_ban_SOURCES = minimal_ip_ban.cpp benchmark_select_SOURCES = benchmark_select.cpp benchmark_threads_SOURCES = benchmark_threads.cpp benchmark_nodelay_SOURCES = benchmark_nodelay.cpp +file_upload_SOURCES = file_upload.cpp diff --git a/examples/file_upload.cpp b/examples/file_upload.cpp new file mode 100644 index 00000000..2c82e2a5 --- /dev/null +++ b/examples/file_upload.cpp @@ -0,0 +1,116 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011, 2012, 2013, 2014, 2015 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#include +#include + +class file_upload_resource : public httpserver::http_resource { + public: + const std::shared_ptr render_GET(const httpserver::http_request&) { + std::string get_response = "\n"; + get_response += " \n"; + get_response += "
\n"; + get_response += "

Upload 1 (key is 'files', multiple files can be selected)


\n"; + get_response += " \n"; + get_response += "

\n"; + get_response += "

Upload 2 (key is 'files2', multiple files can be selected)


\n"; + get_response += "

\n"; + get_response += " \n"; + get_response += "
\n"; + get_response += " \n"; + get_response += "\n"; + + return std::shared_ptr(new httpserver::string_response(get_response, 200, "text/html")); + } + + const std::shared_ptr render_POST(const httpserver::http_request& req) { + std::string post_response = "\n"; + post_response += "\n"; + post_response += " \n"; + post_response += "\n"; + post_response += "\n"; + post_response += " Uploaded files:\n"; + post_response += "

\n"; + post_response += " \n"; + post_response += " \n"; + post_response += " \n"; + post_response += " \n"; + post_response += " \n"; + post_response += " \n"; + post_response += " \n"; + post_response += " \n"; + post_response += " \n"; + + for (auto &file_key : req.get_files()) { + for (auto &files : file_key.second) { + post_response += " \n"; + } + } + + post_response += "
KeyUploaded filenameFile system pathFile sizeContent typeTransfer encoding
"; + post_response += file_key.first; + post_response += ""; + post_response += files.first; + post_response += ""; + post_response += files.second.get_file_system_file_name(); + post_response += ""; + post_response += std::to_string(files.second.get_file_size()); + post_response += ""; + post_response += files.second.get_content_type(); + post_response += ""; + post_response += files.second.get_transfer_encoding(); + post_response += "


\n"; + post_response += " back\n"; + post_response += "\n"; + return std::shared_ptr(new httpserver::string_response(post_response, 201, "text/html")); + } +}; + +int main(int argc, char** argv) { + // this example needs a directory as parameter + if (2 != argc) { + std::cout << "Usage: file_upload " << std::endl; + std::cout << std::endl; + std::cout << " file_upload: writeable directory where uploaded files will be stored" << std::endl; + return -1; + } + + std::cout << "CAUTION: this example will create files in the directory " << std::string(argv[1]) << std::endl; + std::cout << "These files won't be deleted at termination" << std::endl; + std::cout << "Please make sure, that the given directory exists and is writeable" << std::endl; + + httpserver::webserver ws = httpserver::create_webserver(8080) + .no_put_processed_data_to_content() + .file_upload_dir(std::string(argv[1])) + .generate_random_filename_on_upload() + .file_upload_target(httpserver::FILE_UPLOAD_DISK_ONLY); + + file_upload_resource fur; + ws.register_resource("/", &fur); + ws.start(true); + + return 0; +} + diff --git a/src/Makefile.am b/src/Makefile.am index 5e549bbc..cb4a8209 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -19,9 +19,9 @@ AM_CPPFLAGS = -I../ -I$(srcdir)/httpserver/ METASOURCES = AUTO lib_LTLIBRARIES = libhttpserver.la -libhttpserver_la_SOURCES = string_utilities.cpp webserver.cpp http_utils.cpp http_request.cpp http_response.cpp string_response.cpp basic_auth_fail_response.cpp digest_auth_fail_response.cpp deferred_response.cpp file_response.cpp http_resource.cpp details/http_endpoint.cpp +libhttpserver_la_SOURCES = string_utilities.cpp webserver.cpp http_utils.cpp file_info.cpp http_request.cpp http_response.cpp string_response.cpp basic_auth_fail_response.cpp digest_auth_fail_response.cpp deferred_response.cpp file_response.cpp http_resource.cpp details/http_endpoint.cpp noinst_HEADERS = httpserver/string_utilities.hpp httpserver/details/modded_request.hpp gettext.h -nobase_include_HEADERS = httpserver.hpp httpserver/create_webserver.hpp httpserver/webserver.hpp httpserver/http_utils.hpp httpserver/details/http_endpoint.hpp httpserver/http_request.hpp httpserver/http_response.hpp httpserver/http_resource.hpp httpserver/string_response.hpp httpserver/basic_auth_fail_response.hpp httpserver/digest_auth_fail_response.hpp httpserver/deferred_response.hpp httpserver/file_response.hpp +nobase_include_HEADERS = httpserver.hpp httpserver/create_webserver.hpp httpserver/webserver.hpp httpserver/http_utils.hpp httpserver/file_info.hpp httpserver/details/http_endpoint.hpp httpserver/http_request.hpp httpserver/http_response.hpp httpserver/http_resource.hpp httpserver/string_response.hpp httpserver/basic_auth_fail_response.hpp httpserver/digest_auth_fail_response.hpp httpserver/deferred_response.hpp httpserver/file_response.hpp AM_CXXFLAGS += -fPIC -Wall diff --git a/src/file_info.cpp b/src/file_info.cpp new file mode 100644 index 00000000..88a21583 --- /dev/null +++ b/src/file_info.cpp @@ -0,0 +1,56 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011-2019 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#include +#include "httpserver/file_info.hpp" + +namespace httpserver { +namespace http { + +void file_info::set_file_system_file_name(const std::string& file_system_file_name) { + _file_system_file_name = file_system_file_name; +} + +void file_info::set_content_type(const std::string& content_type) { + _content_type = content_type; +} + +void file_info::set_transfer_encoding(const std::string& transfer_encoding) { + _transfer_encoding = transfer_encoding; +} + +void file_info::grow_file_size(size_t additional_file_size) { + _file_size += additional_file_size; +} +size_t file_info::get_file_size() const { + return _file_size; +} +const std::string file_info::get_file_system_file_name() const { + return _file_system_file_name; +} +const std::string file_info::get_content_type() const { + return _content_type; +} +const std::string file_info::get_transfer_encoding() const { + return _transfer_encoding; +} + +} // namespace http +} // namespace httpserver diff --git a/src/http_request.cpp b/src/http_request.cpp index 4c3f0c82..3d88a382 100644 --- a/src/http_request.cpp +++ b/src/http_request.cpp @@ -127,6 +127,10 @@ const std::map http_request::get return arguments; } +http::file_info& http_request::get_or_create_file_info(const std::string& key, const std::string& upload_file_name) { + return files[key][upload_file_name]; +} + const std::string http_request::get_querystring() const { std::string querystring = ""; diff --git a/src/http_utils.cpp b/src/http_utils.cpp index 85a5047a..552bfc19 100644 --- a/src/http_utils.cpp +++ b/src/http_utils.cpp @@ -23,6 +23,10 @@ #if defined(_WIN32) && !defined(__CYGWIN__) #include #include +#include +#include +#include +#include #else // WIN32 check #include #include @@ -33,6 +37,7 @@ #include #include +#include #include #include #include @@ -198,6 +203,14 @@ const char* http_utils::http_post_encoding_multipart_formdata = MHD_HTTP_POST_EN const char* http_utils::application_octet_stream = "application/octet-stream"; const char* http_utils::text_plain = "text/plain"; +const char* http_utils::upload_filename_template = "libhttpserver.XXXXXX"; + +#if defined(_WIN32) + const char http_utils::path_separator = '\\'; +#else // _WIN32 + const char http_utils::path_separator = '/'; +#endif // _WIN32 + std::vector http_utils::tokenize_url(const std::string& str, const char separator) { return string_utilities::string_split(str, separator); } @@ -221,6 +234,45 @@ std::string http_utils::standardize_url(const std::string& url) { return result; } +const std::string http_utils::generate_random_upload_filename(const std::string& directory) { + std::string filename = directory + http_utils::path_separator + http_utils::upload_filename_template; + char *template_filename = strdup(filename.c_str()); + int fd = 0; + +#if defined(_WIN32) + // only function for win32 which creates unique filenames and can handle a given template including a path + // all other functions like tmpnam() always create filenames in the 'temp' directory + if (0 != _mktemp_s(template_filename, filename.size() + 1)) { + free(template_filename); + throw generateFilenameException("Failed to create unique filename"); + } + + // as no existing file should be overwritten the operation should fail if the file already exists + // fstream or ofstream classes don't feature such an option + // with the function _sopen_s this can be achieved by setting the flag _O_EXCL + if (0 != _sopen_s(&fd, template_filename, _O_CREAT | _O_EXCL | _O_NOINHERIT, _SH_DENYNO, _S_IREAD | _S_IWRITE)) { + free(template_filename); + throw generateFilenameException("Failed to create file"); + } + if (fd == -1) { + free(template_filename); + throw generateFilenameException("File descriptor after successful _sopen_s is -1"); + } + _close(fd); +#else // _WIN32 + fd = mkstemp(template_filename); + + if (fd == -1) { + free(template_filename); + throw generateFilenameException("Failed to create unique file"); + } + close(fd); +#endif // _WIN32 + std::string ret_filename = template_filename; + free(template_filename); + return ret_filename; +} + std::string get_ip_str(const struct sockaddr *sa) { if (!sa) throw std::invalid_argument("socket pointer is null"); diff --git a/src/httpserver.hpp b/src/httpserver.hpp index 04eb251a..52f9263c 100644 --- a/src/httpserver.hpp +++ b/src/httpserver.hpp @@ -31,6 +31,7 @@ #include "httpserver/http_resource.hpp" #include "httpserver/http_response.hpp" #include "httpserver/http_utils.hpp" +#include "httpserver/file_info.hpp" #include "httpserver/string_response.hpp" #include "httpserver/webserver.hpp" diff --git a/src/httpserver/create_webserver.hpp b/src/httpserver/create_webserver.hpp index d5e2b07e..99369f60 100644 --- a/src/httpserver/create_webserver.hpp +++ b/src/httpserver/create_webserver.hpp @@ -296,6 +296,36 @@ class create_webserver { return *this; } + create_webserver& no_put_processed_data_to_content() { + _put_processed_data_to_content = false; + return *this; + } + + create_webserver& put_processed_data_to_content() { + _put_processed_data_to_content = true; + return *this; + } + + create_webserver& file_upload_target(const file_upload_target_T& file_upload_target) { + _file_upload_target = file_upload_target; + return *this; + } + + create_webserver& file_upload_dir(const std::string& file_upload_dir) { + _file_upload_dir = file_upload_dir; + return *this; + } + + create_webserver& no_generate_random_filename_on_upload() { + _generate_random_filename_on_upload = false; + return *this; + } + + create_webserver& generate_random_filename_on_upload() { + _generate_random_filename_on_upload = true; + return *this; + } + create_webserver& single_resource() { _single_resource = true; return *this; @@ -360,6 +390,10 @@ class create_webserver { bool _regex_checking = true; bool _ban_system_enabled = true; bool _post_process_enabled = true; + bool _put_processed_data_to_content = true; + file_upload_target_T _file_upload_target = FILE_UPLOAD_MEMORY_ONLY; + std::string _file_upload_dir = "/tmp"; + bool _generate_random_filename_on_upload = false; bool _deferred_enabled = false; bool _single_resource = false; bool _tcp_nodelay = false; diff --git a/src/httpserver/details/modded_request.hpp b/src/httpserver/details/modded_request.hpp index c4f18638..dada29d8 100644 --- a/src/httpserver/details/modded_request.hpp +++ b/src/httpserver/details/modded_request.hpp @@ -27,6 +27,7 @@ #include #include +#include #include "httpserver/http_request.hpp" @@ -47,6 +48,10 @@ struct modded_request { bool second = false; bool has_body = false; + std::string upload_key; + std::string upload_filename; + std::ofstream* upload_ostrm = nullptr; + modded_request() = default; modded_request(const modded_request& b) = default; @@ -64,6 +69,7 @@ struct modded_request { } delete complete_uri; delete standardized_url; + delete upload_ostrm; } }; diff --git a/src/httpserver/file_info.hpp b/src/httpserver/file_info.hpp new file mode 100644 index 00000000..f78c55fa --- /dev/null +++ b/src/httpserver/file_info.hpp @@ -0,0 +1,61 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011-2019 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#if !defined (_HTTPSERVER_HPP_INSIDE_) && !defined (HTTPSERVER_COMPILATION) +#error "Only or can be included directly." +#endif + +#ifndef SRC_HTTPSERVER_FILE_INFO_HPP_ +#define SRC_HTTPSERVER_FILE_INFO_HPP_ + +#include + +namespace httpserver { +class webserver; + +namespace http { + +class file_info { + public: + size_t get_file_size() const; + const std::string get_file_system_file_name() const; + const std::string get_content_type() const; + const std::string get_transfer_encoding() const; + + file_info() = default; + + private: + size_t _file_size; + std::string _file_system_file_name; + std::string _content_type; + std::string _transfer_encoding; + + void set_file_system_file_name(const std::string& file_system_file_name); + void set_content_type(const std::string& content_type); + void set_transfer_encoding(const std::string& transfer_encoding); + void grow_file_size(size_t additional_file_size); + + friend class httpserver::webserver; +}; + +} // namespace http +} // namespace httpserver +#endif // SRC_HTTPSERVER_FILE_INFO_HPP_ + diff --git a/src/httpserver/http_request.hpp b/src/httpserver/http_request.hpp index 00bc2d45..bea32079 100644 --- a/src/httpserver/http_request.hpp +++ b/src/httpserver/http_request.hpp @@ -40,6 +40,7 @@ #include #include "httpserver/http_utils.hpp" +#include "httpserver/file_info.hpp" struct MHD_Connection; @@ -135,6 +136,22 @@ class http_request { **/ const std::map get_args() const; + /** + * Method to get or create a file info struct in the map if the provided filename is already in the map + * return the exiting file info struct, otherwise create one in the map and return it. + * @param upload_file_name the file name the user uploaded (this is the identifier for the map entry) + * @result a file info struct file_info_s + **/ + http::file_info& get_or_create_file_info(const std::string& key, const std::string& upload_file_name); + + /** + * Method used to get all files passed with the request. + * @result result a map > that will be filled with all files + **/ + const std::map> get_files() const { + return files; + } + /** * Method used to get a specific header passed with the request. * @param key the specific header to get the value from @@ -240,6 +257,7 @@ class http_request { std::string path; std::string method; std::map args; + std::map> files; std::string content = ""; size_t content_size_limit = static_cast(-1); std::string version; diff --git a/src/httpserver/http_utils.hpp b/src/httpserver/http_utils.hpp index b768fe6c..6de523ea 100644 --- a/src/httpserver/http_utils.hpp +++ b/src/httpserver/http_utils.hpp @@ -64,10 +64,29 @@ typedef int MHD_Result; namespace httpserver { +enum file_upload_target_T { + FILE_UPLOAD_MEMORY_ONLY, + FILE_UPLOAD_DISK_ONLY, + FILE_UPLOAD_MEMORY_AND_DISK, +}; + typedef void(*unescaper_ptr)(std::string&); namespace http { +struct generateFilenameException : public std::exception { + public: + explicit generateFilenameException(const std::string& message) noexcept : error_message(message) { + } + + const char* what() const noexcept { + return this->error_message.c_str(); + } + + private: + std::string error_message; +}; + class http_utils { public: enum cred_type_T { @@ -234,8 +253,13 @@ class http_utils { static const char* application_octet_stream; static const char* text_plain; + static const char* upload_filename_template; + static const char path_separator; + static std::vector tokenize_url(const std::string&, const char separator = '/'); static std::string standardize_url(const std::string&); + + static const std::string generate_random_upload_filename(const std::string& directory); }; #define COMPARATOR(x, y, op) { \ diff --git a/src/httpserver/webserver.hpp b/src/httpserver/webserver.hpp index 1a5b1255..ef28cdb6 100644 --- a/src/httpserver/webserver.hpp +++ b/src/httpserver/webserver.hpp @@ -162,6 +162,10 @@ class webserver { const bool regex_checking; const bool ban_system_enabled; const bool post_process_enabled; + const bool put_processed_data_to_content; + const file_upload_target_T file_upload_target; + const std::string file_upload_dir; + const bool generate_random_filename_on_upload; const bool deferred_enabled; bool single_resource; bool tcp_nodelay; diff --git a/src/webserver.cpp b/src/webserver.cpp index 4c4a034a..b345c53e 100644 --- a/src/webserver.cpp +++ b/src/webserver.cpp @@ -42,7 +42,6 @@ #include #include #include -#include #include #include #include @@ -152,6 +151,10 @@ webserver::webserver(const create_webserver& params): regex_checking(params._regex_checking), ban_system_enabled(params._ban_system_enabled), post_process_enabled(params._post_process_enabled), + put_processed_data_to_content(params._put_processed_data_to_content), + file_upload_target(params._file_upload_target), + file_upload_dir(params._file_upload_dir), + generate_random_filename_on_upload(params._generate_random_filename_on_upload), deferred_enabled(params._deferred_enabled), single_resource(params._single_resource), tcp_nodelay(params._tcp_nodelay), @@ -454,14 +457,66 @@ MHD_Result webserver::post_iterator(void *cls, enum MHD_ValueKind kind, const char *transfer_encoding, const char *data, uint64_t off, size_t size) { // Parameter needed to respect MHD interface, but not needed here. std::ignore = kind; - std::ignore = filename; - std::ignore = content_type; - std::ignore = transfer_encoding; std::ignore = off; struct details::modded_request* mr = (struct details::modded_request*) cls; - mr->dhr->set_arg(key, mr->dhr->get_arg(key) + std::string(data, size)); - return MHD_YES; + + try { + if (filename == nullptr || mr->ws->file_upload_target != FILE_UPLOAD_DISK_ONLY) { + mr->dhr->set_arg(key, mr->dhr->get_arg(key) + std::string(data, size)); + } + + if (filename && *filename != '\0' && mr->ws->file_upload_target != FILE_UPLOAD_MEMORY_ONLY) { + // either get the existing file info struct or create a new one in the file map + http::file_info& file = mr->dhr->get_or_create_file_info(key, filename); + // if the file_system_file_name is not filled yet, this is a new entry and the name has to be set + // (either random or copy of the original filename) + if (file.get_file_system_file_name().empty()) { + if (mr->ws->generate_random_filename_on_upload) { + file.set_file_system_file_name(http_utils::generate_random_upload_filename(mr->ws->file_upload_dir)); + } else { + file.set_file_system_file_name(mr->ws->file_upload_dir + "/" + std::string(filename)); + } + // to not append to an already existing file, delete an already existing file + unlink(file.get_file_system_file_name().c_str()); + if (content_type != nullptr) { + file.set_content_type(content_type); + } + if (transfer_encoding != nullptr) { + file.set_transfer_encoding(transfer_encoding); + } + } + + // if multiple files are uploaded, a different filename or a different key indicates + // the start of a new file, so close the previous one + if (mr->upload_filename.empty() || + mr->upload_key.empty() || + 0 != strcmp(filename, mr->upload_filename.c_str()) || + 0 != strcmp(key, mr->upload_key.c_str())) { + if (mr->upload_ostrm != nullptr) { + mr->upload_ostrm->close(); + } + } + + if (mr->upload_ostrm == nullptr || !mr->upload_ostrm->is_open()) { + mr->upload_key = key; + mr->upload_filename = filename; + delete mr->upload_ostrm; + mr->upload_ostrm = new std::ofstream(); + mr->upload_ostrm->open(file.get_file_system_file_name(), std::ios::binary | std::ios::app); + } + + if (size > 0) { + mr->upload_ostrm->write(data, size); + } + + // update the file size in the map + file.grow_file_size(size); + } + return MHD_YES; + } catch(const http::generateFilenameException& e) { + return MHD_NO; + } } void webserver::upgrade_handler(void *cls, struct MHD_Connection* connection, void **con_cls, int upgrade_socket) { @@ -527,9 +582,21 @@ MHD_Result webserver::requests_answer_second_step(MHD_Connection* connection, co #ifdef DEBUG std::cout << "Writing content: " << std::string(upload_data, *upload_data_size) << std::endl; #endif // DEBUG - mr->dhr->grow_content(upload_data, *upload_data_size); + // The post iterator is only created from the libmicrohttpd for content of type + // multipart/form-data and application/x-www-form-urlencoded + // all other content (which is indicated by mr-pp == nullptr) + // has to be put to the content even if put_processed_data_to_content is set to false + if (mr->pp == nullptr || put_processed_data_to_content) { + mr->dhr->grow_content(upload_data, *upload_data_size); + } - if (mr->pp != nullptr) MHD_post_process(mr->pp, upload_data, *upload_data_size); + if (mr->pp != nullptr) { + mr->ws = this; + MHD_post_process(mr->pp, upload_data, *upload_data_size); + if (mr->upload_ostrm != nullptr && mr->upload_ostrm->is_open()) { + mr->upload_ostrm->close(); + } + } } *upload_data_size = 0; diff --git a/test/Makefile.am b/test/Makefile.am index 7b8aba08..68ddb554 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -19,11 +19,12 @@ LDADD = $(top_builddir)/src/libhttpserver.la AM_CPPFLAGS = -I$(top_srcdir)/src -I$(top_srcdir)/src/httpserver/ METASOURCES = AUTO -check_PROGRAMS = basic http_utils threaded nodelay string_utilities http_endpoint ban_system ws_start_stop authentication deferred http_resource +check_PROGRAMS = basic file_upload http_utils threaded nodelay string_utilities http_endpoint ban_system ws_start_stop authentication deferred http_resource MOSTLYCLEANFILES = *.gcda *.gcno *.gcov -basic_SOURCES = integ/basic.cpp +basic_SOURCES = integ/basic.cpp +file_upload_SOURCES = integ/file_upload.cpp threaded_SOURCES = integ/threaded.cpp ban_system_SOURCES = integ/ban_system.cpp ws_start_stop_SOURCES = integ/ws_start_stop.cpp diff --git a/test/integ/file_upload.cpp b/test/integ/file_upload.cpp new file mode 100644 index 00000000..00cbd9ab --- /dev/null +++ b/test/integ/file_upload.cpp @@ -0,0 +1,492 @@ +/* + This file is part of libhttpserver + Copyright (C) 2011-2019 Sebastiano Merlino + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 + USA +*/ + +#include +#include +#include +#include +#include + +#include "./httpserver.hpp" +#include "httpserver/string_utilities.hpp" +#include "./littletest.hpp" + +using std::string; +using std::map; +using std::shared_ptr; +using std::vector; +using std::stringstream; + +using httpserver::http_resource; +using httpserver::http_request; +using httpserver::http_response; +using httpserver::string_response; +using httpserver::file_response; +using httpserver::webserver; +using httpserver::create_webserver; + +static const char* TEST_CONTENT_FILENAME = "test_content"; +static const char* TEST_CONTENT_FILEPATH = "./test_content"; +static const char* FILENAME_IN_GET_CONTENT = "filename=\"test_content\""; +static const char* TEST_CONTENT = "test content of file\n"; +static const char* TEST_KEY = "file"; +static size_t TEST_CONTENT_SIZE = 21; + +static const char* TEST_CONTENT_FILENAME_2 = "test_content_2"; +static const char* TEST_CONTENT_FILEPATH_2 = "./test_content_2"; +static const char* FILENAME_IN_GET_CONTENT_2 = "filename=\"test_content_2\""; +static const char* TEST_CONTENT_2 = "test content of second file\n"; +static const char* TEST_KEY_2 = "file2"; +static size_t TEST_CONTENT_SIZE_2 = 28; + +static const char* TEST_PARAM_KEY = "param_key"; +static const char* TEST_PARAM_VALUE = "Value of test param"; + +static CURLcode send_file_to_webserver(bool add_second_file, bool append_parameters) { + curl_global_init(CURL_GLOBAL_ALL); + + CURL *curl = curl_easy_init(); + + curl_mime *form = curl_mime_init(curl); + curl_mimepart *field = curl_mime_addpart(form); + curl_mime_name(field, TEST_KEY); + curl_mime_filedata(field, TEST_CONTENT_FILEPATH); + if (add_second_file) { + field = curl_mime_addpart(form); + curl_mime_name(field, TEST_KEY_2); + curl_mime_filedata(field, TEST_CONTENT_FILEPATH_2); + } + + if (append_parameters) { + field = curl_mime_addpart(form); + curl_mime_name(field, TEST_PARAM_KEY); + curl_mime_data(field, TEST_PARAM_VALUE, CURL_ZERO_TERMINATED); + } + + CURLcode res; + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/upload"); + curl_easy_setopt(curl, CURLOPT_MIMEPOST, form); + + res = curl_easy_perform(curl); + + curl_easy_cleanup(curl); + curl_mime_free(form); + return res; +} + +static bool send_file_via_put() { + curl_global_init(CURL_GLOBAL_ALL); + + CURL *curl; + CURLcode res; + struct stat file_info; + FILE *fd; + + fd = fopen(TEST_CONTENT_FILEPATH, "rb"); + if (!fd) { + return false; + } + + if (fstat(fileno(fd), &file_info) != 0) { + return false; + } + + curl = curl_easy_init(); + if (!curl) { + fclose(fd); + return false; + } + + curl_easy_setopt(curl, CURLOPT_URL, "localhost:8080/upload"); + curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L); + curl_easy_setopt(curl, CURLOPT_READDATA, fd); + curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE, (curl_off_t) file_info.st_size); + + res = curl_easy_perform(curl); + curl_easy_cleanup(curl); + + fclose(fd); + if (res == CURLE_OK) { + return true; + } + return false; +} + +class print_file_upload_resource : public http_resource { + public: + const shared_ptr render_POST(const http_request& req) { + content = req.get_content(); + args = req.get_args(); + files = req.get_files(); + shared_ptr hresp(new string_response("OK", 201, "text/plain")); + return hresp; + } + + const shared_ptr render_PUT(const http_request& req) { + content = req.get_content(); + args = req.get_args(); + files = req.get_files(); + shared_ptr hresp(new string_response("OK", 200, "text/plain")); + return hresp; + } + + const map get_args() const { + return args; + } + + const map> get_files() const { + return files; + } + + const string get_content() const { + return content; + } + + private: + map args; + map> files; + string content; +}; + +LT_BEGIN_SUITE(file_upload_suite) + void set_up() { + } + + void tear_down() { + } +LT_END_SUITE(file_upload_suite) + +LT_BEGIN_AUTO_TEST(file_upload_suite, file_upload_memory_and_disk) + string upload_directory = "."; + webserver* ws; + + ws = new webserver(create_webserver(8080) + .put_processed_data_to_content() + .file_upload_target(httpserver::FILE_UPLOAD_MEMORY_AND_DISK) + .file_upload_dir(upload_directory) + .generate_random_filename_on_upload()); + ws->start(false); + LT_CHECK_EQ(ws->is_running(), true); + + print_file_upload_resource resource; + ws->register_resource("upload", &resource); + + CURLcode res = send_file_to_webserver(false, false); + LT_ASSERT_EQ(res, 0); + + string actual_content = resource.get_content(); + LT_CHECK_EQ(actual_content.find(FILENAME_IN_GET_CONTENT) != string::npos, true); + LT_CHECK_EQ(actual_content.find(TEST_CONTENT) != string::npos, true); + + map args = resource.get_args(); + LT_CHECK_EQ(args.size(), 1); + map::iterator arg = args.begin(); + LT_CHECK_EQ(arg->first, TEST_KEY); + LT_CHECK_EQ(arg->second, TEST_CONTENT); + + map> files = resource.get_files(); + LT_CHECK_EQ(files.size(), 1); + map>::iterator file_key = files.begin(); + LT_CHECK_EQ(file_key->first, TEST_KEY); + LT_CHECK_EQ(file_key->second.size(), 1); + map::iterator file = file_key->second.begin(); + LT_CHECK_EQ(file->first, TEST_CONTENT_FILENAME); + LT_CHECK_EQ(file->second.get_file_size(), TEST_CONTENT_SIZE); + LT_CHECK_EQ(file->second.get_content_type(), httpserver::http::http_utils::application_octet_stream); + + string expected_filename = upload_directory + + httpserver::http::http_utils::path_separator + + httpserver::http::http_utils::upload_filename_template; + LT_CHECK_EQ(file->second.get_file_system_file_name().substr(0, file->second.get_file_system_file_name().size() - 6), + expected_filename.substr(0, expected_filename.size() - 6)); + unlink(file->second.get_file_system_file_name().c_str()); + + ws->stop(); + delete ws; +LT_END_AUTO_TEST(file_upload_memory_and_disk) + +LT_BEGIN_AUTO_TEST(file_upload_suite, file_upload_memory_and_disk_via_put) + string upload_directory = "."; + webserver* ws; + + ws = new webserver(create_webserver(8080) + .put_processed_data_to_content() + .file_upload_target(httpserver::FILE_UPLOAD_MEMORY_AND_DISK) + .file_upload_dir(upload_directory) + .generate_random_filename_on_upload()); + ws->start(false); + LT_CHECK_EQ(ws->is_running(), true); + + print_file_upload_resource resource; + ws->register_resource("upload", &resource); + + int ret = send_file_via_put(); + LT_ASSERT_EQ(ret, true); + + string actual_content = resource.get_content(); + LT_CHECK_EQ(actual_content, TEST_CONTENT); + + map args = resource.get_args(); + LT_CHECK_EQ(args.size(), 0); + + map> files = resource.get_files(); + LT_CHECK_EQ(files.size(), 0); + + ws->stop(); + delete ws; +LT_END_AUTO_TEST(file_upload_memory_and_disk_via_put) + +LT_BEGIN_AUTO_TEST(file_upload_suite, file_upload_memory_and_disk_additional_params) + string upload_directory = "."; + webserver* ws; + + ws = new webserver(create_webserver(8080) + .put_processed_data_to_content() + .file_upload_target(httpserver::FILE_UPLOAD_MEMORY_AND_DISK) + .file_upload_dir(upload_directory) + .generate_random_filename_on_upload()); + ws->start(false); + LT_CHECK_EQ(ws->is_running(), true); + + print_file_upload_resource resource; + ws->register_resource("upload", &resource); + + CURLcode res = send_file_to_webserver(false, true); + LT_ASSERT_EQ(res, 0); + + string actual_content = resource.get_content(); + LT_CHECK_EQ(actual_content.find(FILENAME_IN_GET_CONTENT) != string::npos, true); + LT_CHECK_EQ(actual_content.find(TEST_CONTENT) != string::npos, true); + LT_CHECK_EQ(actual_content.find(TEST_PARAM_KEY) != string::npos, true); + LT_CHECK_EQ(actual_content.find(TEST_PARAM_VALUE) != string::npos, true); + + map args = resource.get_args(); + LT_CHECK_EQ(args.size(), 2); + map::iterator arg = args.begin(); + LT_CHECK_EQ(arg->first, TEST_KEY); + LT_CHECK_EQ(arg->second, TEST_CONTENT); + arg++; + LT_CHECK_EQ(arg->first, TEST_PARAM_KEY); + LT_CHECK_EQ(arg->second, TEST_PARAM_VALUE); + + map> files = resource.get_files(); + LT_CHECK_EQ(files.size(), 1); + map>::iterator file_key = files.begin(); + LT_CHECK_EQ(file_key->first, TEST_KEY); + LT_CHECK_EQ(file_key->second.size(), 1); + map::iterator file = file_key->second.begin(); + LT_CHECK_EQ(file->first, TEST_CONTENT_FILENAME); + LT_CHECK_EQ(file->second.get_file_size(), TEST_CONTENT_SIZE); + LT_CHECK_EQ(file->second.get_content_type(), httpserver::http::http_utils::application_octet_stream); + + string expected_filename = upload_directory + + httpserver::http::http_utils::path_separator + + httpserver::http::http_utils::upload_filename_template; + LT_CHECK_EQ(file->second.get_file_system_file_name().substr(0, file->second.get_file_system_file_name().size() - 6), + expected_filename.substr(0, expected_filename.size() - 6)); + unlink(file->second.get_file_system_file_name().c_str()); + + ws->stop(); + delete ws; +LT_END_AUTO_TEST(file_upload_memory_and_disk_additional_params) + +LT_BEGIN_AUTO_TEST(file_upload_suite, file_upload_memory_and_disk_two_files) + string upload_directory = "."; + webserver* ws; + + ws = new webserver(create_webserver(8080) + .put_processed_data_to_content() + .file_upload_target(httpserver::FILE_UPLOAD_MEMORY_AND_DISK) + .file_upload_dir(upload_directory) + .generate_random_filename_on_upload()); + ws->start(false); + LT_CHECK_EQ(ws->is_running(), true); + + print_file_upload_resource resource; + ws->register_resource("upload", &resource); + + CURLcode res = send_file_to_webserver(true, false); + LT_ASSERT_EQ(res, 0); + + string actual_content = resource.get_content(); + LT_CHECK_EQ(actual_content.find(FILENAME_IN_GET_CONTENT) != string::npos, true); + LT_CHECK_EQ(actual_content.find(TEST_CONTENT) != string::npos, true); + LT_CHECK_EQ(actual_content.find(FILENAME_IN_GET_CONTENT_2) != string::npos, true); + LT_CHECK_EQ(actual_content.find(TEST_CONTENT_2) != string::npos, true); + + map args = resource.get_args(); + LT_CHECK_EQ(args.size(), 2); + map::iterator arg = args.begin(); + LT_CHECK_EQ(arg->first, TEST_KEY); + LT_CHECK_EQ(arg->second, TEST_CONTENT); + arg++; + LT_CHECK_EQ(arg->first, TEST_KEY_2); + LT_CHECK_EQ(arg->second, TEST_CONTENT_2); + + map> files = resource.get_files(); + LT_CHECK_EQ(files.size(), 2); + map>::iterator file_key = files.begin(); + LT_CHECK_EQ(file_key->first, TEST_KEY); + LT_CHECK_EQ(file_key->second.size(), 1); + map::iterator file = file_key->second.begin(); + LT_CHECK_EQ(file->first, TEST_CONTENT_FILENAME); + LT_CHECK_EQ(file->second.get_file_size(), TEST_CONTENT_SIZE); + LT_CHECK_EQ(file->second.get_content_type(), httpserver::http::http_utils::application_octet_stream); + + string expected_filename = upload_directory + + httpserver::http::http_utils::path_separator + + httpserver::http::http_utils::upload_filename_template; + LT_CHECK_EQ(file->second.get_file_system_file_name().substr(0, file->second.get_file_system_file_name().size() - 6), + expected_filename.substr(0, expected_filename.size() - 6)); + unlink(file->second.get_file_system_file_name().c_str()); + + file_key++; + LT_CHECK_EQ(file_key->first, TEST_KEY_2); + LT_CHECK_EQ(file_key->second.size(), 1); + file = file_key->second.begin(); + LT_CHECK_EQ(file->first, TEST_CONTENT_FILENAME_2); + LT_CHECK_EQ(file->second.get_file_size(), TEST_CONTENT_SIZE_2); + LT_CHECK_EQ(file->second.get_content_type(), httpserver::http::http_utils::application_octet_stream); + + expected_filename = upload_directory + + httpserver::http::http_utils::path_separator + + httpserver::http::http_utils::upload_filename_template; + LT_CHECK_EQ(file->second.get_file_system_file_name().substr(0, file->second.get_file_system_file_name().size() - 6), + expected_filename.substr(0, expected_filename.size() - 6)); + unlink(file->second.get_file_system_file_name().c_str()); + + + ws->stop(); + delete ws; +LT_END_AUTO_TEST(file_upload_memory_and_disk_two_files) + +LT_BEGIN_AUTO_TEST(file_upload_suite, file_upload_disk_only) + string upload_directory = "."; + webserver* ws; + + ws = new webserver(create_webserver(8080) + .no_put_processed_data_to_content() + .file_upload_target(httpserver::FILE_UPLOAD_DISK_ONLY) + .file_upload_dir(upload_directory) + .generate_random_filename_on_upload()); + ws->start(false); + LT_CHECK_EQ(ws->is_running(), true); + + print_file_upload_resource resource; + ws->register_resource("upload", &resource); + + CURLcode res = send_file_to_webserver(false, false); + LT_ASSERT_EQ(res, 0); + + string actual_content = resource.get_content(); + LT_CHECK_EQ(actual_content.size(), 0); + + map args = resource.get_args(); + LT_CHECK_EQ(args.size(), 0); + + map> files = resource.get_files(); + LT_CHECK_EQ(files.size(), 1); + map>::iterator file_key = files.begin(); + LT_CHECK_EQ(file_key->first, TEST_KEY); + LT_CHECK_EQ(file_key->second.size(), 1); + map::iterator file = file_key->second.begin(); + LT_CHECK_EQ(file->first, TEST_CONTENT_FILENAME); + LT_CHECK_EQ(file->second.get_file_size(), TEST_CONTENT_SIZE); + LT_CHECK_EQ(file->second.get_content_type(), httpserver::http::http_utils::application_octet_stream); + + string expected_filename = upload_directory + + httpserver::http::http_utils::path_separator + + httpserver::http::http_utils::upload_filename_template; + LT_CHECK_EQ(file->second.get_file_system_file_name().substr(0, file->second.get_file_system_file_name().size() - 6), + expected_filename.substr(0, expected_filename.size() - 6)); + unlink(file->second.get_file_system_file_name().c_str()); + + ws->stop(); + delete ws; +LT_END_AUTO_TEST(file_upload_disk_only) + +LT_BEGIN_AUTO_TEST(file_upload_suite, file_upload_memory_only_incl_content) + string upload_directory = "."; + webserver* ws; + + ws = new webserver(create_webserver(8080) + .put_processed_data_to_content() + .file_upload_target(httpserver::FILE_UPLOAD_MEMORY_ONLY)); + ws->start(false); + LT_CHECK_EQ(ws->is_running(), true); + + print_file_upload_resource resource; + ws->register_resource("upload", &resource); + + CURLcode res = send_file_to_webserver(false, false); + LT_ASSERT_EQ(res, 0); + + string actual_content = resource.get_content(); + LT_CHECK_EQ(actual_content.find(FILENAME_IN_GET_CONTENT) != string::npos, true); + LT_CHECK_EQ(actual_content.find(TEST_CONTENT) != string::npos, true); + + map args = resource.get_args(); + LT_CHECK_EQ(args.size(), 1); + map::iterator arg = args.begin(); + LT_CHECK_EQ(arg->first, TEST_KEY); + LT_CHECK_EQ(arg->second, TEST_CONTENT); + + map> files = resource.get_files(); + LT_CHECK_EQ(resource.get_files().size(), 0); + + ws->stop(); + delete ws; +LT_END_AUTO_TEST(file_upload_memory_only_incl_content) + +LT_BEGIN_AUTO_TEST(file_upload_suite, file_upload_memory_only_excl_content) + string upload_directory = "."; + webserver* ws; + + ws = new webserver(create_webserver(8080) + .no_put_processed_data_to_content() + .file_upload_target(httpserver::FILE_UPLOAD_MEMORY_ONLY)); + ws->start(false); + LT_CHECK_EQ(ws->is_running(), true); + + print_file_upload_resource resource; + ws->register_resource("upload", &resource); + + CURLcode res = send_file_to_webserver(false, false); + LT_ASSERT_EQ(res, 0); + + string actual_content = resource.get_content(); + LT_CHECK_EQ(actual_content.size(), 0); + + map args = resource.get_args(); + LT_CHECK_EQ(args.size(), 1); + map::iterator arg = args.begin(); + LT_CHECK_EQ(arg->first, TEST_KEY); + LT_CHECK_EQ(arg->second, TEST_CONTENT); + + map> files = resource.get_files(); + LT_CHECK_EQ(files.size(), 0); + + ws->stop(); + delete ws; +LT_END_AUTO_TEST(file_upload_memory_only_excl_content) + +LT_BEGIN_AUTO_TEST_ENV() + AUTORUN_TESTS() +LT_END_AUTO_TEST_ENV() diff --git a/test/test_content_2 b/test/test_content_2 new file mode 100644 index 00000000..18c3afa0 --- /dev/null +++ b/test/test_content_2 @@ -0,0 +1 @@ +test content of second file diff --git a/test/unit/http_utils_test.cpp b/test/unit/http_utils_test.cpp index f1b63afc..71c1a655 100644 --- a/test/unit/http_utils_test.cpp +++ b/test/unit/http_utils_test.cpp @@ -30,6 +30,7 @@ #include #endif +#include #include #include "./littletest.hpp" @@ -154,6 +155,22 @@ LT_BEGIN_AUTO_TEST(http_utils_suite, standardize_url) LT_CHECK_EQ(result, "/abc/pqr"); LT_END_AUTO_TEST(standardize_url) +LT_BEGIN_AUTO_TEST(http_utils_suite, generate_random_upload_filename) + struct stat sb; + string directory = ".", filename = ""; + string expected_output = directory + httpserver::http::http_utils::path_separator + httpserver::http::http_utils::upload_filename_template; + try { + filename = httpserver::http::http_utils::generate_random_upload_filename(directory); + } catch(const httpserver::http::generateFilenameException& e) { + LT_FAIL(e.what()); + } + LT_CHECK_EQ(stat(filename.c_str(), &sb), 0); + // unlink the file again, to not mess up the test directory with files + unlink(filename.c_str()); + // omit the last 6 signs in the check, as the "XXXXXX" is substituted to be random and unique + LT_CHECK_EQ(filename.substr(0, filename.size() - 6), expected_output.substr(0, expected_output.size() - 6)); +LT_END_AUTO_TEST(generate_random_upload_filename) + LT_BEGIN_AUTO_TEST(http_utils_suite, ip_to_str) struct sockaddr_in ip4addr; From d53069f597cb0d279e8694c311484367adc2aa4c Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Thu, 3 Mar 2022 15:05:35 -0800 Subject: [PATCH 541/623] Updated the readme with detail on file upload --- README.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 270a8dd8..07021ed9 100644 --- a/README.md +++ b/README.md @@ -560,7 +560,6 @@ The `http_request` class has a set of methods you will have access to when imple * _**const std::map** get_cookies() **const**:_ Returns a map containing all the cookies present in the HTTP request. * _**const std::map** get_footers() **const**:_ Returns a map containing all the footers present in the HTTP request (only for http 1.1 chunked encodings). * _**const std::map** get_args() **const**:_ Returns all the arguments present in the HTTP request. Arguments can be (1) querystring parameters, (2) path argument (in case of parametric endpoint, (3) parameters parsed from the HTTP request body if the body is in `application/x-www-form-urlencoded` or `multipart/form-data` formats and the postprocessor is enabled in the webserver (enabled by default). -* _**const std::map** get_files() **const**:_ Returns information about all the uploaded files (if the files are stored to disk). This information includes the original file name, the size of the file and the path to the file in the file system. * _**const std::map>** get_files() **const**:_ Returns information about all the uploaded files (if the files are stored to disk). This information includes the key (as identifier of the outer map), the original file name (as identifier of the inner map) and a class `file_info`, which includes the size of the file and the path to the file in the file system. * _**const std::string&** get_content() **const**:_ Returns the body of the HTTP request. * _**bool** content_too_large() **const**:_ Returns `true` if the body length of the HTTP request sent by the client is longer than the max allowed on the server. @@ -575,6 +574,13 @@ The `http_request` class has a set of methods you will have access to when imple * _**gnutls_session_t** get_tls_session() **const**:_ Tests if there is am underlying TLS state of the current request. * _**gnutls_session_t** get_tls_session() **const**:_ Returns the underlying TLS state of the current request for inspection. (It is an error to call this if the state does not exist.) +Details on the `http::file_info` structure. + +* _**size_t** get_file_size() **const**:_ Returns the size of the file uploaded through the HTTP request. +* _**const std::string** get_file_system_file_name() **const**:_ Returns the name of the file uploaded through the HTTP request as stored on the filesystem. +* _**const std::string** get_content_type() **const**:_ Returns the content type of the file uploaded through the HTTP request. +* _**const std::string** get_transfer_encoding() **const**:_ Returns the transfer encoding of the file uploaded through the HTTP request. + #### Example of handler reading arguments from a request #include From 31dfed733325b631c561706145e5d78c9f489ae9 Mon Sep 17 00:00:00 2001 From: Sebastiano Merlino Date: Thu, 3 Mar 2022 15:09:12 -0800 Subject: [PATCH 542/623] Minor changes to doc annotations --- src/httpserver/http_request.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/httpserver/http_request.hpp b/src/httpserver/http_request.hpp index bea32079..739de326 100644 --- a/src/httpserver/http_request.hpp +++ b/src/httpserver/http_request.hpp @@ -140,13 +140,13 @@ class http_request { * Method to get or create a file info struct in the map if the provided filename is already in the map * return the exiting file info struct, otherwise create one in the map and return it. * @param upload_file_name the file name the user uploaded (this is the identifier for the map entry) - * @result a file info struct file_info_s + * @result a http::file_info **/ http::file_info& get_or_create_file_info(const std::string& key, const std::string& upload_file_name); /** * Method used to get all files passed with the request. - * @result result a map > that will be filled with all files + * @result result a map > that will be filled with all files **/ const std::map> get_files() const { return files; From acc3e6a3ad0b6193286886015109839d642f1ef6 Mon Sep 17 00:00:00 2001 From: Alexander Dahl Date: Tue, 12 Apr 2022 22:33:36 +0200 Subject: [PATCH 543/623] Cleanup files uploaded on request deletion (#266) * http_request: Remove uploaded files in destructor If the user needs those files, she should copy/move theme in the request handler. References: #264 Signed-off-by: Alexander Dahl * test: integ: file_upload: Remove redundant file delete Not needed anymore. http_request destructor does cleanup now. * test: integ: file_upload: Check uploaded files are deleted * test: integ: file_upload: Stop webserver earlier The webserver runs in a different thread than the test, therefor deleting the uploaded files and checking if they are deleted might lead into a race condition, and thus into tests failing sometimes, but not always. Moving the webserver stop and destructor behind the curl action should be safe, every information needed for the test is copied to or in the resource handler. --- src/http_request.cpp | 9 ++++++++ src/httpserver/http_request.hpp | 5 ++++ test/integ/file_upload.cpp | 41 ++++++++++++++++++--------------- 3 files changed, 37 insertions(+), 18 deletions(-) diff --git a/src/http_request.cpp b/src/http_request.cpp index 3d88a382..195dddbc 100644 --- a/src/http_request.cpp +++ b/src/http_request.cpp @@ -256,4 +256,13 @@ std::ostream &operator<< (std::ostream &os, const http_request &r) { return os; } +http_request::~http_request() { + for ( const auto &file_key : this->get_files() ) { + for ( const auto &files : file_key.second ) { + // C++17 has std::filesystem::remove() + remove(files.second.get_file_system_file_name().c_str()); + } + } +} + } // namespace httpserver diff --git a/src/httpserver/http_request.hpp b/src/httpserver/http_request.hpp index 739de326..560f76c5 100644 --- a/src/httpserver/http_request.hpp +++ b/src/httpserver/http_request.hpp @@ -46,6 +46,8 @@ struct MHD_Connection; namespace httpserver { +namespace details { struct modded_request; } + /** * Class representing an abstraction for an Http Request. It is used from classes using these apis to receive information through http protocol. **/ @@ -254,6 +256,8 @@ class http_request { http_request& operator=(const http_request& b) = default; http_request& operator=(http_request&& b) = default; + ~http_request(); + std::string path; std::string method; std::map args; @@ -356,6 +360,7 @@ class http_request { const std::map get_headerlike_values(enum MHD_ValueKind kind) const; friend class webserver; + friend struct details::modded_request; }; std::ostream &operator<< (std::ostream &os, const http_request &r); diff --git a/test/integ/file_upload.cpp b/test/integ/file_upload.cpp index 00cbd9ab..ca506d97 100644 --- a/test/integ/file_upload.cpp +++ b/test/integ/file_upload.cpp @@ -59,6 +59,12 @@ static size_t TEST_CONTENT_SIZE_2 = 28; static const char* TEST_PARAM_KEY = "param_key"; static const char* TEST_PARAM_VALUE = "Value of test param"; +static bool file_exists(const string &path) { + struct stat sb; + + return (stat(path.c_str(), &sb) == 0); +} + static CURLcode send_file_to_webserver(bool add_second_file, bool append_parameters) { curl_global_init(CURL_GLOBAL_ALL); @@ -191,6 +197,9 @@ LT_BEGIN_AUTO_TEST(file_upload_suite, file_upload_memory_and_disk) CURLcode res = send_file_to_webserver(false, false); LT_ASSERT_EQ(res, 0); + ws->stop(); + delete ws; + string actual_content = resource.get_content(); LT_CHECK_EQ(actual_content.find(FILENAME_IN_GET_CONTENT) != string::npos, true); LT_CHECK_EQ(actual_content.find(TEST_CONTENT) != string::npos, true); @@ -216,10 +225,7 @@ LT_BEGIN_AUTO_TEST(file_upload_suite, file_upload_memory_and_disk) httpserver::http::http_utils::upload_filename_template; LT_CHECK_EQ(file->second.get_file_system_file_name().substr(0, file->second.get_file_system_file_name().size() - 6), expected_filename.substr(0, expected_filename.size() - 6)); - unlink(file->second.get_file_system_file_name().c_str()); - - ws->stop(); - delete ws; + LT_CHECK_EQ(file_exists(file->second.get_file_system_file_name()), false); LT_END_AUTO_TEST(file_upload_memory_and_disk) LT_BEGIN_AUTO_TEST(file_upload_suite, file_upload_memory_and_disk_via_put) @@ -271,6 +277,9 @@ LT_BEGIN_AUTO_TEST(file_upload_suite, file_upload_memory_and_disk_additional_par CURLcode res = send_file_to_webserver(false, true); LT_ASSERT_EQ(res, 0); + ws->stop(); + delete ws; + string actual_content = resource.get_content(); LT_CHECK_EQ(actual_content.find(FILENAME_IN_GET_CONTENT) != string::npos, true); LT_CHECK_EQ(actual_content.find(TEST_CONTENT) != string::npos, true); @@ -301,10 +310,7 @@ LT_BEGIN_AUTO_TEST(file_upload_suite, file_upload_memory_and_disk_additional_par httpserver::http::http_utils::upload_filename_template; LT_CHECK_EQ(file->second.get_file_system_file_name().substr(0, file->second.get_file_system_file_name().size() - 6), expected_filename.substr(0, expected_filename.size() - 6)); - unlink(file->second.get_file_system_file_name().c_str()); - - ws->stop(); - delete ws; + LT_CHECK_EQ(file_exists(file->second.get_file_system_file_name()), false); LT_END_AUTO_TEST(file_upload_memory_and_disk_additional_params) LT_BEGIN_AUTO_TEST(file_upload_suite, file_upload_memory_and_disk_two_files) @@ -325,6 +331,9 @@ LT_BEGIN_AUTO_TEST(file_upload_suite, file_upload_memory_and_disk_two_files) CURLcode res = send_file_to_webserver(true, false); LT_ASSERT_EQ(res, 0); + ws->stop(); + delete ws; + string actual_content = resource.get_content(); LT_CHECK_EQ(actual_content.find(FILENAME_IN_GET_CONTENT) != string::npos, true); LT_CHECK_EQ(actual_content.find(TEST_CONTENT) != string::npos, true); @@ -355,7 +364,7 @@ LT_BEGIN_AUTO_TEST(file_upload_suite, file_upload_memory_and_disk_two_files) httpserver::http::http_utils::upload_filename_template; LT_CHECK_EQ(file->second.get_file_system_file_name().substr(0, file->second.get_file_system_file_name().size() - 6), expected_filename.substr(0, expected_filename.size() - 6)); - unlink(file->second.get_file_system_file_name().c_str()); + LT_CHECK_EQ(file_exists(file->second.get_file_system_file_name()), false); file_key++; LT_CHECK_EQ(file_key->first, TEST_KEY_2); @@ -370,11 +379,7 @@ LT_BEGIN_AUTO_TEST(file_upload_suite, file_upload_memory_and_disk_two_files) httpserver::http::http_utils::upload_filename_template; LT_CHECK_EQ(file->second.get_file_system_file_name().substr(0, file->second.get_file_system_file_name().size() - 6), expected_filename.substr(0, expected_filename.size() - 6)); - unlink(file->second.get_file_system_file_name().c_str()); - - - ws->stop(); - delete ws; + LT_CHECK_EQ(file_exists(file->second.get_file_system_file_name()), false); LT_END_AUTO_TEST(file_upload_memory_and_disk_two_files) LT_BEGIN_AUTO_TEST(file_upload_suite, file_upload_disk_only) @@ -395,6 +400,9 @@ LT_BEGIN_AUTO_TEST(file_upload_suite, file_upload_disk_only) CURLcode res = send_file_to_webserver(false, false); LT_ASSERT_EQ(res, 0); + ws->stop(); + delete ws; + string actual_content = resource.get_content(); LT_CHECK_EQ(actual_content.size(), 0); @@ -416,10 +424,7 @@ LT_BEGIN_AUTO_TEST(file_upload_suite, file_upload_disk_only) httpserver::http::http_utils::upload_filename_template; LT_CHECK_EQ(file->second.get_file_system_file_name().substr(0, file->second.get_file_system_file_name().size() - 6), expected_filename.substr(0, expected_filename.size() - 6)); - unlink(file->second.get_file_system_file_name().c_str()); - - ws->stop(); - delete ws; + LT_CHECK_EQ(file_exists(file->second.get_file_system_file_name()), false); LT_END_AUTO_TEST(file_upload_disk_only) LT_BEGIN_AUTO_TEST(file_upload_suite, file_upload_memory_only_incl_content) From bbdf033be9f06e3120143442b771b7a1231bb42e Mon Sep 17 00:00:00 2001 From: Ilya Brin <464157+ilyabrin@users.noreply.github.com> Date: Sun, 14 Aug 2022 03:18:40 +0300 Subject: [PATCH 544/623] Update README.md (#277) fixed little typos and syntax highlighting has been added --- README.md | 65 ++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 40 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 07021ed9..2b662f27 100644 --- a/README.md +++ b/README.md @@ -120,7 +120,7 @@ Here are listed the libhttpserver specific options (the canonical configure opti ## Getting Started The most basic example of creating a server and handling a requests for the path `/hello`: - +```cpp #include using namespace httpserver; @@ -141,7 +141,7 @@ The most basic example of creating a server and handling a requests for the path return 0; } - +``` To test the above example, you could run the following command from a terminal: curl -XGET -v http://localhost:8080/hello @@ -166,9 +166,9 @@ You can also check this example on [github](https://github.com/etr/libhttpserver ## Create and work with a webserver As you can see from the example above, creating a webserver with standard configuration is quite simple: - +```cpp webserver ws = create_webserver(8080); - +``` The `create_webserver` class is a supporting _builder_ class that eases the building of a webserver through chained syntax. ### Basic Startup Options @@ -214,6 +214,7 @@ In all these 3 cases libhttpserver would provide a standard HTTP response to the * _.internal_error_resource(**const shared_ptr(*render_ptr)(const http_request&)** resource):_ Specifies a function to handle a request that is causing an uncaught exception during its execution. **REMEMBER:** is this callback is causing an exception itself, the standard default response from libhttpserver will be reported to the HTTP client. #### Example of custom errors: +```cpp #include using namespace httpserver; @@ -246,7 +247,7 @@ In all these 3 cases libhttpserver would provide a standard HTTP response to the return 0; } - +``` To test the above example, you can run the following command from a terminal: curl -XGET -v http://localhost:8080/hello @@ -262,6 +263,7 @@ You can also check this example on [github](https://github.com/etr/libhttpserver * _.log_error(**void(*log_error_ptr)(const std::string&)** functor):_ Specifies a function used to log errors generating from the server. #### Example of custom logging callback +```cpp #include #include @@ -288,7 +290,7 @@ You can also check this example on [github](https://github.com/etr/libhttpserver return 0; } - +``` To test the above example, you can run the following command from a terminal: curl -XGET -v http://localhost:8080/hello @@ -312,6 +314,7 @@ You can also check this example on [github](https://github.com/etr/libhttpserver * _.https_priorities(**const std::string&** priority_string):_ SSL/TLS protocol version and ciphers. Must be followed by a string specifying the SSL/TLS protocol versions and ciphers that are acceptable for the application. The string is passed unchanged to gnutls_priority_init. If this option is not specified, `"NORMAL"` is used. #### Minimal example using HTTPS +```cpp #include using namespace httpserver; @@ -335,7 +338,7 @@ You can also check this example on [github](https://github.com/etr/libhttpserver return 0; } - +``` To test the above example, you can run the following command from a terminal: curl -XGET -v -k 'https://localhost:8080/hello' @@ -355,10 +358,10 @@ You should calculate the value of NC_SIZE based on the number of connections per * _.digest_auth_random(**const std::string&** nonce_seed):_ Digest Authentication nonce’s seed. For security, you SHOULD provide a fresh random nonce when actually using Digest Authentication with libhttpserver in production. ### Examples of chaining syntax to create a webserver - +```cpp webserver ws = create_webserver(8080) - .no_ssl() - .no_ipv6() + .no_ssl() + .no_ipv6() .no_debug() .no_pedantic() .no_basic_auth() @@ -367,12 +370,14 @@ You should calculate the value of NC_SIZE based on the number of connections per .no_regex_checking() .no_ban_system() .no_post_process(); +``` ## +```cpp webserver ws = create_webserver(8080) .use_ssl() .https_mem_key("key.pem") .https_mem_cert("cert.pem"); - +``` ### Starting and stopping a webserver Once a webserver is created, you can manage its execution through the following methods on the `webserver` class: * _**void** webserver::start(**bool** blocking):_ Allows to start a server. If the `blocking` flag is passed as `true`, it will block the execution of the current thread until a call to stop on the same webserver object is performed. @@ -397,6 +402,7 @@ Given this, the `http_resource` class contains the following extensible methods * _**const std::shared_ptr** http_resource::render(**const http_request&** req):_ Invoked as a backup method if the matching method is not implemented. It can be used whenever you want all the invocations on a URL to activate the same behavior regardless of the HTTP method requested. The default implementation of the `render` method returns an empty response with a `404`. #### Example of implementation of render methods +```cpp #include using namespace httpserver; @@ -421,7 +427,7 @@ Given this, the `http_resource` class contains the following extensible methods return 0; } - +``` To test the above example, you can run the following commands from a terminal: * `curl -XGET -v http://localhost:8080/hello`: will return `GET: Hello, World!`. * `curl -XPOST -v http://localhost:8080/hello`: will return `OTHER: Hello, World!`. You can try requesting other methods beside `POST` to verify how the same message will be returned. @@ -436,6 +442,7 @@ The base `http_resource` class has a set of methods that can be used to allow an * _**void** http_resource::disallow_all():_ Marks all HTTP methods as not allowed. #### Example of methods allowed/disallowed +```cpp #include using namespace httpserver; @@ -458,7 +465,7 @@ The base `http_resource` class has a set of methods that can be used to allow an return 0; } - +``` To test the above example, you can run the following command from a terminal: curl -XGET -v http://localhost:8080/hello @@ -482,7 +489,7 @@ There are essentially four ways to specify an endpoint string: * **A parametrized path. (e.g. `"/path/to/resource/with/{arg1}/{arg2}/in/url"`)**. In this case, the webserver will match the argument with any value passed. In addition to this, the arguments will be passed to the resource as part of the arguments (readable from the `http_request::get_arg` method - see [here](#parsing-requests)). For example, if passing `"/path/to/resource/with/{arg1}/{arg2}/in/url"` will match any request on URL with any value in place of `{arg1}` and `{arg2}`. * **A parametrized path with custom parameters.** This is the same of a normal parametrized path, but allows to specify a regular expression for the argument (e.g. `"/path/to/resource/with/{arg1|[0-9]+}/{arg2|[a-z]+}/in/url"`. In this case, the webserver will match the arguments with any value passed that satisfies the regex. In addition to this, as above, the arguments will be passed to the resource as part of the arguments (readable from the `http_request::get_arg` method - see [here](#parsing-requests)). For example, if passing `"/path/to/resource/with/{arg1|[0-9]+}/{arg2|[a-z]+}/in/url"` will match requests on URLs like `"/path/to/resource/with/10/AA/in/url"` but not like `""/path/to/resource/with/BB/10/in/url""` * Any of the above marked as `family`. Will match any request on URLs having path that is prefixed by the path passed. For example, if family is set to `true` and endpoint is set to `"/path"`, the webserver will route to the resource not only the requests against `"/path"` but also everything in its nested path `"/path/on/the/previous/one"`. - +```cpp #include using namespace httpserver; @@ -526,7 +533,7 @@ There are essentially four ways to specify an endpoint string: return 0; } - +``` To test the above example, you can run the following commands from a terminal: * `curl -XGET -v http://localhost:8080/hello`: will return the `Hello, World!` message. @@ -582,6 +589,7 @@ Details on the `http::file_info` structure. * _**const std::string** get_transfer_encoding() **const**:_ Returns the transfer encoding of the file uploaded through the HTTP request. #### Example of handler reading arguments from a request +```cpp #include using namespace httpserver; @@ -602,7 +610,7 @@ Details on the `http::file_info` structure. return 0; } - +``` To test the above example, you can run the following command from a terminal: curl -XGET -v "http://localhost:8080/hello?name=John" @@ -634,6 +642,7 @@ The `http_response` class offers an additional set of methods to "decorate" your * _**void** shoutCAST():_ Mark the response as a `shoutCAST` one. ### Example of response setting headers +```cpp #include using namespace httpserver; @@ -656,7 +665,7 @@ The `http_response` class offers an additional set of methods to "decorate" your return 0; } - +``` To test the above example, you could run the following command from a terminal: curl -XGET -v "http://localhost:8080/hello" @@ -691,6 +700,7 @@ Examples of valid IPs include: * `"::ffff:192.0.*.*"`: ranges of IPV4 IPs nested into IPV6. #### Example of IP Whitelisting/Blacklisting +```cpp #include using namespace httpserver; @@ -714,7 +724,7 @@ Examples of valid IPs include: return 0; } - +``` To test the above example, you could run the following command from a terminal: curl -XGET -v "http://localhost:8080/hello" @@ -733,6 +743,7 @@ Digest authentication uses a one-way authentication method based on MD5 hash alg Client certificate authentication uses a X.509 certificate from the client. This is the strongest authentication mechanism but it requires the use of HTTPS. Client certificate authentication can be used simultaneously with Basic or Digest Authentication in order to provide a two levels authentication (like for instance separate machine and user authentication). You can enable/disable support for Certificate authentication through the `use_ssl` and `no_ssl` methods of the `create_webserver` class. ### Using Basic Authentication +```cpp #include using namespace httpserver; @@ -756,7 +767,7 @@ Client certificate authentication uses a X.509 certificate from the client. This return 0; } - +``` To test the above example, you can run the following command from a terminal: curl -XGET -v -u myuser:mypass "http://localhost:8080/hello" @@ -766,6 +777,7 @@ You will receive back the user and password you passed in input. Try to pass the You can also check this example on [github](https://github.com/etr/libhttpserver/blob/master/examples/basic_authentication.cpp). ### Using Digest Authentication +```cpp #include #define MY_OPAQUE "11733b200778ce33060f31c9af70a870ba96ddd4" @@ -797,25 +809,26 @@ You can also check this example on [github](https://github.com/etr/libhttpserver return 0; } - +``` To test the above example, you can run the following command from a terminal: curl -XGET -v --digest --user myuser:mypass localhost:8080/hello -You will receive a `SUCCESS` in response (observe the response message from the server in detail and you'll see the full interaction). Try to pass the wrong credentials or send a request without `digest` active to see the failure. +You will receive a `SUCCESS` in response (observe the response message from the server in detail and you'll see the full interaction). Try to pass the wrong credentials or send a request without `digest` active to see the failure. You can also check this example on [github](https://github.com/etr/libhttpserver/blob/master/examples/digest_authentication.cpp). [Back to TOC](#table-of-contents) ## HTTP Utils -libhttpserver provides a set of constants to help you develop your HTTP server. It would be redudant to list them here; so, please, consult the list directly [here](https://github.com/etr/libhttpserver/blob/master/src/httpserver/http_utils.hpp). +libhttpserver provides a set of constants to help you develop your HTTP server. It would be redundant to list them here; so, please, consult the list directly [here](https://github.com/etr/libhttpserver/blob/master/src/httpserver/http_utils.hpp). [Back to TOC](#table-of-contents) ## Other Examples #### Example of returning a response from a file +```cpp #include using namespace httpserver; @@ -836,7 +849,7 @@ libhttpserver provides a set of constants to help you develop your HTTP server. return 0; } - +``` To test the above example, you can run the following command from a terminal: curl -XGET -v localhost:8080/hello @@ -844,6 +857,7 @@ To test the above example, you can run the following command from a terminal: You can also check this example on [github](https://github.com/etr/libhttpserver/blob/master/examples/minimal_file_response.cpp). #### Example of a deferred response through callback +```cpp #include using namespace httpserver; @@ -878,7 +892,7 @@ You can also check this example on [github](https://github.com/etr/libhttpserver return 0; } - +``` To test the above example, you can run the following command from a terminal: curl -XGET -v localhost:8080/hello @@ -886,6 +900,7 @@ To test the above example, you can run the following command from a terminal: You can also check this example on [github](https://github.com/etr/libhttpserver/blob/master/examples/minimal_deferred.cpp). #### Example of a deferred response through callback (passing additional data along) +```cpp #include #include @@ -935,7 +950,7 @@ You can also check this example on [github](https://github.com/etr/libhttpserver return 0; } - +``` To test the above example, you can run the following command from a terminal: curl -XGET -v localhost:8080/hello From 415e02f01fb8cb97c91d6a615479531bae279b7c Mon Sep 17 00:00:00 2001 From: Jim Phillips Date: Thu, 1 Sep 2022 12:42:05 -0500 Subject: [PATCH 545/623] Fix unban_ip doc (#279) For use when default policy is ACCEPT, not REJECT. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2b662f27..df7429f4 100644 --- a/README.md +++ b/README.md @@ -684,7 +684,7 @@ The system supports both IPV4 and IPV6 and manages them transparently. The only You can explicitly ban or allow an IP address using the following methods on the `webserver` class: * _**void** ban_ip(**const std::string&** ip):_ Adds one IP (or a range of IPs) to the list of the banned ones. Takes in input a `string` that contains the IP (or range of IPs) to ban. To use when the `default_policy` is `ACCEPT`. * _**void** allow_ip(**const std::string&** ip):_ Adds one IP (or a range of IPs) to the list of the allowed ones. Takes in input a `string` that contains the IP (or range of IPs) to allow. To use when the `default_policy` is `REJECT`. -* _**void** unban_ip(**const std::string&** ip):_ Removes one IP (or a range of IPs) from the list of the banned ones. Takes in input a `string` that contains the IP (or range of IPs) to remove from the list. To use when the `default_policy` is `REJECT`. +* _**void** unban_ip(**const std::string&** ip):_ Removes one IP (or a range of IPs) from the list of the banned ones. Takes in input a `string` that contains the IP (or range of IPs) to remove from the list. To use when the `default_policy` is `ACCEPT`. * _**void** disallow_ip(**const std::string&** ip):_ Removes one IP (or a range of IPs) from the list of the allowed ones. Takes in input a `string` that contains the IP (or range of IPs) to remove from the list. To use when the `default_policy` is `REJECT`. ### IP String Format From 8ab27253c8d61cf665457870ec75db1e61ce11e8 Mon Sep 17 00:00:00 2001 From: stuart-byma <113421362+stuart-byma@users.noreply.github.com> Date: Wed, 28 Sep 2022 17:28:41 +0200 Subject: [PATCH 546/623] switch callback function pointers to std::function types (#283) --- src/httpserver/create_webserver.hpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/httpserver/create_webserver.hpp b/src/httpserver/create_webserver.hpp index 99369f60..d1503399 100644 --- a/src/httpserver/create_webserver.hpp +++ b/src/httpserver/create_webserver.hpp @@ -27,6 +27,7 @@ #include #include +#include #include #include "httpserver/http_response.hpp" @@ -40,10 +41,10 @@ namespace httpserver { class webserver; class http_request; -typedef const std::shared_ptr(*render_ptr)(const http_request&); -typedef bool(*validator_ptr)(const std::string&); -typedef void(*log_access_ptr)(const std::string&); -typedef void(*log_error_ptr)(const std::string&); +typedef std::function(const http_request&)> render_ptr; +typedef std::function validator_ptr; +typedef std::function log_access_ptr; +typedef std::function log_error_ptr; class create_webserver { public: From f33553c26bc59c6123383d57c07058cbaa7bc899 Mon Sep 17 00:00:00 2001 From: stuart-byma <113421362+stuart-byma@users.noreply.github.com> Date: Fri, 30 Sep 2022 21:51:33 +0200 Subject: [PATCH 547/623] Remove ineffective/unneeded const from shared_ptr return types (#285) * remove ineffective/unneeded const qualifier on shared_ptr return types * remove ineffective/unneeded const from shared_ptr instances in tests * revert accidental removal of const on pass by reference shared_ptrs --- README.md | 58 ++++++++++----------- examples/allowing_disallowing_methods.cpp | 2 +- examples/basic_authentication.cpp | 2 +- examples/benchmark_nodelay.cpp | 2 +- examples/benchmark_select.cpp | 2 +- examples/benchmark_threads.cpp | 2 +- examples/custom_access_log.cpp | 2 +- examples/custom_error.cpp | 6 +-- examples/deferred_with_accumulator.cpp | 2 +- examples/digest_authentication.cpp | 2 +- examples/file_upload.cpp | 4 +- examples/handlers.cpp | 4 +- examples/hello_with_get_arg.cpp | 2 +- examples/hello_world.cpp | 4 +- examples/minimal_deferred.cpp | 2 +- examples/minimal_file_response.cpp | 2 +- examples/minimal_hello_world.cpp | 2 +- examples/minimal_https.cpp | 2 +- examples/minimal_ip_ban.cpp | 2 +- examples/service.cpp | 32 ++++++------ examples/setting_headers.cpp | 2 +- examples/url_registration.cpp | 6 +-- src/httpserver/create_webserver.hpp | 2 +- src/httpserver/details/modded_request.hpp | 2 +- src/httpserver/http_resource.hpp | 20 ++++---- src/httpserver/webserver.hpp | 6 +-- src/webserver.cpp | 6 +-- test/integ/authentication.cpp | 4 +- test/integ/ban_system.cpp | 2 +- test/integ/basic.cpp | 62 +++++++++++------------ test/integ/deferred.cpp | 4 +- test/integ/file_upload.cpp | 4 +- test/integ/nodelay.cpp | 2 +- test/integ/threaded.cpp | 2 +- test/integ/ws_start_stop.cpp | 6 +-- test/unit/http_resource_test.cpp | 2 +- 36 files changed, 134 insertions(+), 134 deletions(-) diff --git a/README.md b/README.md index df7429f4..ca1ac442 100644 --- a/README.md +++ b/README.md @@ -127,7 +127,7 @@ The most basic example of creating a server and handling a requests for the path class hello_world_resource : public http_resource { public: - const std::shared_ptr render(const http_request&) { + std::shared_ptr render(const http_request&) { return std::shared_ptr(new string_response("Hello, World!")); } }; @@ -219,17 +219,17 @@ In all these 3 cases libhttpserver would provide a standard HTTP response to the using namespace httpserver; - const std::shared_ptr not_found_custom(const http_request& req) { + std::shared_ptr not_found_custom(const http_request& req) { return std::shared_ptr(new string_response("Not found custom", 404, "text/plain")); } - const std::shared_ptr not_allowed_custom(const http_request& req) { + std::shared_ptr not_allowed_custom(const http_request& req) { return std::shared_ptr(new string_response("Not allowed custom", 405, "text/plain")); } class hello_world_resource : public http_resource { public: - const std::shared_ptr render(const http_request&) { + std::shared_ptr render(const http_request&) { return std::shared_ptr(new string_response("Hello, World!")); } }; @@ -275,7 +275,7 @@ You can also check this example on [github](https://github.com/etr/libhttpserver class hello_world_resource : public http_resource { public: - const std::shared_ptr render(const http_request&) { + std::shared_ptr render(const http_request&) { return std::shared_ptr(new string_response("Hello, World!")); } }; @@ -321,7 +321,7 @@ You can also check this example on [github](https://github.com/etr/libhttpserver class hello_world_resource : public http_resource { public: - const std::shared_ptr render(const http_request&) { + std::shared_ptr render(const http_request&) { return std::shared_ptr(new string_response("Hello, World!")); } }; @@ -391,15 +391,15 @@ Once a webserver is created, you can manage its execution through the following The `http_resource` class represents a logical collection of HTTP methods that will be associated to a URL when registered on the webserver. The class is **designed for extension** and it is where most of your code should ideally live. When the webserver matches a request against a resource (see: [resource registration](#registering-resources)), the method correspondent to the one in the request (GET, POST, etc..) (see below) is called on the resource. Given this, the `http_resource` class contains the following extensible methods (also called `handlers` or `render methods`): -* _**const std::shared_ptr** http_resource::render_GET(**const http_request&** req):_ Invoked on an HTTP GET request. -* _**const std::shared_ptr** http_resource::render_POST(**const http_request&** req):_ Invoked on an HTTP POST request. -* _**const std::shared_ptr** http_resource::render_PUT(**const http_request&** req):_ Invoked on an HTTP PUT request. -* _**const std::shared_ptr** http_resource::render_HEAD(**const http_request&** req):_ Invoked on an HTTP HEAD request. -* _**const std::shared_ptr** http_resource::render_DELETE(**const http_request&** req):_ Invoked on an HTTP DELETE request. -* _**const std::shared_ptr** http_resource::render_TRACE(**const http_request&** req):_ Invoked on an HTTP TRACE request. -* _**const std::shared_ptr** http_resource::render_OPTIONS(**const http_request&** req):_ Invoked on an HTTP OPTIONS request. -* _**const std::shared_ptr** http_resource::render_CONNECT(**const http_request&** req):_ Invoked on an HTTP CONNECT request. -* _**const std::shared_ptr** http_resource::render(**const http_request&** req):_ Invoked as a backup method if the matching method is not implemented. It can be used whenever you want all the invocations on a URL to activate the same behavior regardless of the HTTP method requested. The default implementation of the `render` method returns an empty response with a `404`. +* _**std::shared_ptr** http_resource::render_GET(**const http_request&** req):_ Invoked on an HTTP GET request. +* _**std::shared_ptr** http_resource::render_POST(**const http_request&** req):_ Invoked on an HTTP POST request. +* _**std::shared_ptr** http_resource::render_PUT(**const http_request&** req):_ Invoked on an HTTP PUT request. +* _**std::shared_ptr** http_resource::render_HEAD(**const http_request&** req):_ Invoked on an HTTP HEAD request. +* _**std::shared_ptr** http_resource::render_DELETE(**const http_request&** req):_ Invoked on an HTTP DELETE request. +* _**std::shared_ptr** http_resource::render_TRACE(**const http_request&** req):_ Invoked on an HTTP TRACE request. +* _**std::shared_ptr** http_resource::render_OPTIONS(**const http_request&** req):_ Invoked on an HTTP OPTIONS request. +* _**std::shared_ptr** http_resource::render_CONNECT(**const http_request&** req):_ Invoked on an HTTP CONNECT request. +* _**std::shared_ptr** http_resource::render(**const http_request&** req):_ Invoked as a backup method if the matching method is not implemented. It can be used whenever you want all the invocations on a URL to activate the same behavior regardless of the HTTP method requested. The default implementation of the `render` method returns an empty response with a `404`. #### Example of implementation of render methods ```cpp @@ -409,11 +409,11 @@ Given this, the `http_resource` class contains the following extensible methods class hello_world_resource : public http_resource { public: - const std::shared_ptr render_GET(const http_request&) { + std::shared_ptr render_GET(const http_request&) { return std::shared_ptr(new string_response("GET: Hello, World!")); } - const std::shared_ptr render(const http_request&) { + std::shared_ptr render(const http_request&) { return std::shared_ptr(new string_response("OTHER: Hello, World!")); } }; @@ -449,7 +449,7 @@ The base `http_resource` class has a set of methods that can be used to allow an class hello_world_resource : public http_resource { public: - const std::shared_ptr render(const http_request&) { + std::shared_ptr render(const http_request&) { return std::shared_ptr(new string_response("Hello, World!")); } }; @@ -496,21 +496,21 @@ There are essentially four ways to specify an endpoint string: class hello_world_resource : public http_resource { public: - const std::shared_ptr render(const http_request&) { + std::shared_ptr render(const http_request&) { return std::shared_ptr(new string_response("Hello, World!")); } }; class handling_multiple_resource : public http_resource { public: - const std::shared_ptr render(const http_request& req) { + std::shared_ptr render(const http_request& req) { return std::shared_ptr(new string_response("Your URL: " + req.get_path())); } }; class url_args_resource : public http_resource { public: - const std::shared_ptr render(const http_request& req) { + std::shared_ptr render(const http_request& req) { return std::shared_ptr(new string_response("ARGS: " + req.get_arg("arg1") + " and " + req.get_arg("arg2"))); } }; @@ -596,7 +596,7 @@ Details on the `http::file_info` structure. class hello_world_resource : public http_resource { public: - const std::shared_ptr render(const http_request& req) { + std::shared_ptr render(const http_request& req) { return std::shared_ptr(new string_response("Hello: " + req.get_arg("name"))); } }; @@ -649,7 +649,7 @@ The `http_response` class offers an additional set of methods to "decorate" your class hello_world_resource : public http_resource { public: - const std::shared_ptr render(const http_request&) { + std::shared_ptr render(const http_request&) { std::shared_ptr response = std::shared_ptr(new string_response("Hello, World!")); response->with_header("MyHeader", "MyValue"); return response; @@ -707,7 +707,7 @@ Examples of valid IPs include: class hello_world_resource : public http_resource { public: - const std::shared_ptr render(const http_request&) { + std::shared_ptr render(const http_request&) { return std::shared_ptr(new string_response("Hello, World!")); } }; @@ -750,7 +750,7 @@ Client certificate authentication uses a X.509 certificate from the client. This class user_pass_resource : public httpserver::http_resource { public: - const std::shared_ptr render_GET(const http_request& req) { + std::shared_ptr render_GET(const http_request& req) { if (req.get_user() != "myuser" || req.get_pass() != "mypass") { return std::shared_ptr(new basic_auth_fail_response("FAIL", "test@example.com")); } @@ -786,7 +786,7 @@ You can also check this example on [github](https://github.com/etr/libhttpserver class digest_resource : public httpserver::http_resource { public: - const std::shared_ptr render_GET(const http_request& req) { + std::shared_ptr render_GET(const http_request& req) { if (req.get_digested_user() == "") { return std::shared_ptr(new digest_auth_fail_response("FAIL", "test@example.com", MY_OPAQUE, true)); } @@ -835,7 +835,7 @@ libhttpserver provides a set of constants to help you develop your HTTP server. class file_response_resource : public http_resource { public: - const std::shared_ptr render_GET(const http_request& req) { + std::shared_ptr render_GET(const http_request& req) { return std::shared_ptr(new file_response("test_content", 200, "text/plain")); } }; @@ -878,7 +878,7 @@ You can also check this example on [github](https://github.com/etr/libhttpserver class deferred_resource : public http_resource { public: - const std::shared_ptr render_GET(const http_request& req) { + std::shared_ptr render_GET(const http_request& req) { return std::shared_ptr >(new deferred_response(test_callback, nullptr, "cycle callback response")); } }; @@ -935,7 +935,7 @@ You can also check this example on [github](https://github.com/etr/libhttpserver class deferred_resource : public http_resource { public: - const std::shared_ptr render_GET(const http_request& req) { + std::shared_ptr render_GET(const http_request& req) { std::shared_ptr > closure_data(new std::atomic(counter++)); return std::shared_ptr > >(new deferred_response >(test_callback, closure_data, "cycle callback response")); } diff --git a/examples/allowing_disallowing_methods.cpp b/examples/allowing_disallowing_methods.cpp index a0578f5e..73389142 100644 --- a/examples/allowing_disallowing_methods.cpp +++ b/examples/allowing_disallowing_methods.cpp @@ -22,7 +22,7 @@ class hello_world_resource : public httpserver::http_resource { public: - const std::shared_ptr render(const httpserver::http_request&) { + std::shared_ptr render(const httpserver::http_request&) { return std::shared_ptr(new httpserver::string_response("Hello, World!")); } }; diff --git a/examples/basic_authentication.cpp b/examples/basic_authentication.cpp index cc90f55b..2661806f 100644 --- a/examples/basic_authentication.cpp +++ b/examples/basic_authentication.cpp @@ -22,7 +22,7 @@ class user_pass_resource : public httpserver::http_resource { public: - const std::shared_ptr render_GET(const httpserver::http_request& req) { + std::shared_ptr render_GET(const httpserver::http_request& req) { if (req.get_user() != "myuser" || req.get_pass() != "mypass") { return std::shared_ptr(new httpserver::basic_auth_fail_response("FAIL", "test@example.com")); } diff --git a/examples/benchmark_nodelay.cpp b/examples/benchmark_nodelay.cpp index c1a6c1e7..96c2f570 100755 --- a/examples/benchmark_nodelay.cpp +++ b/examples/benchmark_nodelay.cpp @@ -32,7 +32,7 @@ class hello_world_resource : public httpserver::http_resource { resp(resp) { } - const std::shared_ptr render(const httpserver::http_request&) { + std::shared_ptr render(const httpserver::http_request&) { return resp; } diff --git a/examples/benchmark_select.cpp b/examples/benchmark_select.cpp index 1edc1c00..ef5cd089 100755 --- a/examples/benchmark_select.cpp +++ b/examples/benchmark_select.cpp @@ -32,7 +32,7 @@ class hello_world_resource : public httpserver::http_resource { resp(resp) { } - const std::shared_ptr render(const httpserver::http_request&) { + std::shared_ptr render(const httpserver::http_request&) { return resp; } diff --git a/examples/benchmark_threads.cpp b/examples/benchmark_threads.cpp index 1afe4dfb..db376168 100755 --- a/examples/benchmark_threads.cpp +++ b/examples/benchmark_threads.cpp @@ -32,7 +32,7 @@ class hello_world_resource : public httpserver::http_resource { resp(resp) { } - const std::shared_ptr render(const httpserver::http_request&) { + std::shared_ptr render(const httpserver::http_request&) { return resp; } diff --git a/examples/custom_access_log.cpp b/examples/custom_access_log.cpp index dd2e4aa8..f1a59d53 100644 --- a/examples/custom_access_log.cpp +++ b/examples/custom_access_log.cpp @@ -28,7 +28,7 @@ void custom_access_log(const std::string& url) { class hello_world_resource : public httpserver::http_resource { public: - const std::shared_ptr render(const httpserver::http_request&) { + std::shared_ptr render(const httpserver::http_request&) { return std::shared_ptr(new httpserver::string_response("Hello, World!")); } }; diff --git a/examples/custom_error.cpp b/examples/custom_error.cpp index 8e720835..a82d5972 100644 --- a/examples/custom_error.cpp +++ b/examples/custom_error.cpp @@ -20,17 +20,17 @@ #include -const std::shared_ptr not_found_custom(const httpserver::http_request&) { +std::shared_ptr not_found_custom(const httpserver::http_request&) { return std::shared_ptr(new httpserver::string_response("Not found custom", 404, "text/plain")); } -const std::shared_ptr not_allowed_custom(const httpserver::http_request&) { +std::shared_ptr not_allowed_custom(const httpserver::http_request&) { return std::shared_ptr(new httpserver::string_response("Not allowed custom", 405, "text/plain")); } class hello_world_resource : public httpserver::http_resource { public: - const std::shared_ptr render(const httpserver::http_request&) { + std::shared_ptr render(const httpserver::http_request&) { return std::shared_ptr(new httpserver::string_response("Hello, World!")); } }; diff --git a/examples/deferred_with_accumulator.cpp b/examples/deferred_with_accumulator.cpp index a55e4cd8..5e60f3e7 100644 --- a/examples/deferred_with_accumulator.cpp +++ b/examples/deferred_with_accumulator.cpp @@ -56,7 +56,7 @@ ssize_t test_callback(std::shared_ptr > closure_data, char* buf class deferred_resource : public httpserver::http_resource { public: - const std::shared_ptr render_GET(const httpserver::http_request&) { + std::shared_ptr render_GET(const httpserver::http_request&) { std::shared_ptr > closure_data(new std::atomic(counter++)); return std::shared_ptr > >(new httpserver::deferred_response >(test_callback, closure_data, "cycle callback response")); } diff --git a/examples/digest_authentication.cpp b/examples/digest_authentication.cpp index c919cf0e..40767dc2 100644 --- a/examples/digest_authentication.cpp +++ b/examples/digest_authentication.cpp @@ -24,7 +24,7 @@ class digest_resource : public httpserver::http_resource { public: - const std::shared_ptr render_GET(const httpserver::http_request& req) { + std::shared_ptr render_GET(const httpserver::http_request& req) { if (req.get_digested_user() == "") { return std::shared_ptr(new httpserver::digest_auth_fail_response("FAIL", "test@example.com", MY_OPAQUE, true)); } else { diff --git a/examples/file_upload.cpp b/examples/file_upload.cpp index 2c82e2a5..7e1afd5a 100644 --- a/examples/file_upload.cpp +++ b/examples/file_upload.cpp @@ -23,7 +23,7 @@ class file_upload_resource : public httpserver::http_resource { public: - const std::shared_ptr render_GET(const httpserver::http_request&) { + std::shared_ptr render_GET(const httpserver::http_request&) { std::string get_response = "\n"; get_response += " \n"; get_response += "
\n"; @@ -40,7 +40,7 @@ class file_upload_resource : public httpserver::http_resource { return std::shared_ptr(new httpserver::string_response(get_response, 200, "text/html")); } - const std::shared_ptr render_POST(const httpserver::http_request& req) { + std::shared_ptr render_POST(const httpserver::http_request& req) { std::string post_response = "\n"; post_response += "\n"; post_response += "