[
  {
    "path": ".clang-format",
    "content": "Language: Cpp\nBasedOnStyle: Google"
  },
  {
    "path": ".gitignore",
    "content": "# Prerequisites\n*.d\n\n# Compiled Object files\n*.slo\n*.lo\n*.o\n*.obj\n\n# Precompiled Headers\n*.gch\n*.pch\n\n# Compiled Dynamic libraries\n*.so\n*.dylib\n*.dll\n\n# Fortran module files\n*.mod\n*.smod\n\n# Compiled Static libraries\n*.lai\n*.la\n*.a\n*.lib\n\n# Executables\n*.exe\n*.out\n*.app\n\n# Otheres\ncore\n.vscode\n"
  },
  {
    "path": ".travis.yml",
    "content": "language: cpp\nsudo: required\ndist: trusty\ncompiler:\n  - g++\nos:\n  - linux\ninstall:\n  - sudo apt-get install cmake\nenv:\n  - BUILD_TYPE=debug\n  - BUILD_TYPE=release\nscript:\n  - ./build.sh\n\n"
  },
  {
    "path": "CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 2.8)\n\nproject(WebServer CXX)\n\nif(NOT CMAKE_BUILD_TYPE)\n    set(CMAKE_BUILD_TYPE \"Debug\")\nendif()\n\nset(CXX_FLAGS\n    -g\n    -Wall\n    -std=c++11\n    -D_PTHREADS\n    -Wno-unused-parameter\n)\n\n\nset(CMAKE_CXX_COMPILER \"g++\")\nset(CMAKE_CXX_FLAGS_DEBUG \"-O0\")\nset(CMAKE_CXX_FLAGS_RELEASE \"-O0\")\n\nstring(REPLACE \";\" \" \" CMAKE_CXX_FLAGS \"${CXX_FLAGS}\")\n\n\nstring(TOUPPER ${CMAKE_BUILD_TYPE} BUILD_TYPE)\nmessage(STATUS \"CXX_FLAGS = \" ${CMAKE_CXX_FLAGS} \" \" ${CMAKE_CXX_FLAGS_${BUILD_TYPE}})\n\nadd_subdirectory(WebServer)"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2018 林亚\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# A C++ High Performance Web Server\r\n\r\n[![Build Status](https://travis-ci.org/linyacool/WebServer.svg?branch=master)](https://travis-ci.org/linyacool/WebServer)\r\n[![license](https://img.shields.io/github/license/mashape/apistatus.svg)](https://opensource.org/licenses/MIT)\r\n\r\n  \r\n## Introduction  \r\n\r\n本项目为C++11编写的Web服务器，解析了get、head请求，可处理静态资源，支持HTTP长连接，支持管线化请求，并实现了异步日志，记录服务器运行状态。  \r\n\r\n\r\n\r\n| Part Ⅰ | Part Ⅱ | Part Ⅲ | Part Ⅳ | Part Ⅴ | Part Ⅵ |\r\n| :--------: | :---------: | :---------: | :---------: | :---------: | :---------: |\r\n| [并发模型](https://github.com/linyacool/WebServer/blob/master/并发模型.md)|[连接的维护](https://github.com/linyacool/WebServer/blob/master/连接的维护.md)|[版本历史](https://github.com/linyacool/WebServer/blob/master/%E7%89%88%E6%9C%AC%E5%8E%86%E5%8F%B2.md) | [测试及改进](https://github.com/linyacool/WebServer/blob/master/测试及改进.md) | [项目目的](https://github.com/linyacool/WebServer/blob/master/%E9%A1%B9%E7%9B%AE%E7%9B%AE%E7%9A%84.md) | [面试问题](https://github.com/linyacool/WebServer/blob/master/%E9%9D%A2%E8%AF%95%E9%97%AE%E9%A2%98.md)\r\n\r\n## Envoirment  \r\n* OS: Ubuntu 14.04\r\n* Complier: g++ 4.8\r\n\r\n## Build\r\n\r\n\t./build.sh\r\n\r\n## Usage\r\n\r\n\t./WebServer [-t thread_numbers] [-p port] [-l log_file_path(should begin with '/')]\r\n\r\n## Technical points\r\n* 使用Epoll边沿触发的IO多路复用技术，非阻塞IO，使用Reactor模式\r\n* 使用多线程充分利用多核CPU，并使用线程池避免线程频繁创建销毁的开销\r\n* 使用基于小根堆的定时器关闭超时请求\r\n* 主线程只负责accept请求，并以Round Robin的方式分发给其它IO线程(兼计算线程)，锁的争用只会出现在主线程和某一特定线程中\r\n* 使用eventfd实现了线程的异步唤醒\r\n* 使用双缓冲区技术实现了简单的异步日志系统\r\n* 为减少内存泄漏的可能，使用智能指针等RAII机制\r\n* 使用状态机解析了HTTP请求,支持管线化\r\n* 支持优雅关闭连接\r\n \r\n## Model\r\n\r\n并发模型为Reactor+非阻塞IO+线程池，新连接Round Robin分配，详细介绍请参考[并发模型](https://github.com/linyacool/WebServer/blob/master/并发模型.md)\r\n![并发模型](https://github.com/linyacool/WebServer/blob/master/datum/model.png)\r\n\r\n## 代码统计\r\n\r\n![cloc](https://github.com/linyacool/WebServer/blob/master/datum/cloc.png)\r\n\r\n\r\n## Others\r\n除了项目基本的代码，进服务器进行压测时，对开源测试工具Webbench增加了Keep-Alive选项和测试功能: 改写后的[Webbench](https://github.com/linyacool/WebBench)\r\n\r\n"
  },
  {
    "path": "WebBench/LICENSE",
    "content": "                    GNU GENERAL PUBLIC LICENSE\n                       Version 3, 29 June 2007\n\n Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>\n Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed.\n\n                            Preamble\n\n  The GNU General Public License is a free, copyleft license for\nsoftware and other kinds of works.\n\n  The licenses for most software and other practical works are designed\nto take away your freedom to share and change the works.  By contrast,\nthe GNU General Public License is intended to guarantee your freedom to\nshare and change all versions of a program--to make sure it remains free\nsoftware for all its users.  We, the Free Software Foundation, use the\nGNU General Public License for most of our software; it applies also to\nany other work released this way by its authors.  You can apply it to\nyour programs, too.\n\n  When we speak of free software, we are referring to freedom, not\nprice.  Our General Public Licenses are designed to make sure that you\nhave the freedom to distribute copies of free software (and charge for\nthem if you wish), that you receive source code or can get it if you\nwant it, that you can change the software or use pieces of it in new\nfree programs, and that you know you can do these things.\n\n  To protect your rights, we need to prevent others from denying you\nthese rights or asking you to surrender the rights.  Therefore, you have\ncertain responsibilities if you distribute copies of the software, or if\nyou modify it: responsibilities to respect the freedom of others.\n\n  For example, if you distribute copies of such a program, whether\ngratis or for a fee, you must pass on to the recipients the same\nfreedoms that you received.  You must make sure that they, too, receive\nor can get the source code.  And you must show them these terms so they\nknow their rights.\n\n  Developers that use the GNU GPL protect your rights with two steps:\n(1) assert copyright on the software, and (2) offer you this License\ngiving you legal permission to copy, distribute and/or modify it.\n\n  For the developers' and authors' protection, the GPL clearly explains\nthat there is no warranty for this free software.  For both users' and\nauthors' sake, the GPL requires that modified versions be marked as\nchanged, so that their problems will not be attributed erroneously to\nauthors of previous versions.\n\n  Some devices are designed to deny users access to install or run\nmodified versions of the software inside them, although the manufacturer\ncan do so.  This is fundamentally incompatible with the aim of\nprotecting users' freedom to change the software.  The systematic\npattern of such abuse occurs in the area of products for individuals to\nuse, which is precisely where it is most unacceptable.  Therefore, we\nhave designed this version of the GPL to prohibit the practice for those\nproducts.  If such problems arise substantially in other domains, we\nstand ready to extend this provision to those domains in future versions\nof the GPL, as needed to protect the freedom of users.\n\n  Finally, every program is threatened constantly by software patents.\nStates should not allow patents to restrict development and use of\nsoftware on general-purpose computers, but in those that do, we wish to\navoid the special danger that patents applied to a free program could\nmake it effectively proprietary.  To prevent this, the GPL assures that\npatents cannot be used to render the program non-free.\n\n  The precise terms and conditions for copying, distribution and\nmodification follow.\n\n                       TERMS AND CONDITIONS\n\n  0. Definitions.\n\n  \"This License\" refers to version 3 of the GNU General Public License.\n\n  \"Copyright\" also means copyright-like laws that apply to other kinds of\nworks, such as semiconductor masks.\n\n  \"The Program\" refers to any copyrightable work licensed under this\nLicense.  Each licensee is addressed as \"you\".  \"Licensees\" and\n\"recipients\" may be individuals or organizations.\n\n  To \"modify\" a work means to copy from or adapt all or part of the work\nin a fashion requiring copyright permission, other than the making of an\nexact copy.  The resulting work is called a \"modified version\" of the\nearlier work or a work \"based on\" the earlier work.\n\n  A \"covered work\" means either the unmodified Program or a work based\non the Program.\n\n  To \"propagate\" a work means to do anything with it that, without\npermission, would make you directly or secondarily liable for\ninfringement under applicable copyright law, except executing it on a\ncomputer or modifying a private copy.  Propagation includes copying,\ndistribution (with or without modification), making available to the\npublic, and in some countries other activities as well.\n\n  To \"convey\" a work means any kind of propagation that enables other\nparties to make or receive copies.  Mere interaction with a user through\na computer network, with no transfer of a copy, is not conveying.\n\n  An interactive user interface displays \"Appropriate Legal Notices\"\nto the extent that it includes a convenient and prominently visible\nfeature that (1) displays an appropriate copyright notice, and (2)\ntells the user that there is no warranty for the work (except to the\nextent that warranties are provided), that licensees may convey the\nwork under this License, and how to view a copy of this License.  If\nthe interface presents a list of user commands or options, such as a\nmenu, a prominent item in the list meets this criterion.\n\n  1. Source Code.\n\n  The \"source code\" for a work means the preferred form of the work\nfor making modifications to it.  \"Object code\" means any non-source\nform of a work.\n\n  A \"Standard Interface\" means an interface that either is an official\nstandard defined by a recognized standards body, or, in the case of\ninterfaces specified for a particular programming language, one that\nis widely used among developers working in that language.\n\n  The \"System Libraries\" of an executable work include anything, other\nthan the work as a whole, that (a) is included in the normal form of\npackaging a Major Component, but which is not part of that Major\nComponent, and (b) serves only to enable use of the work with that\nMajor Component, or to implement a Standard Interface for which an\nimplementation is available to the public in source code form.  A\n\"Major Component\", in this context, means a major essential component\n(kernel, window system, and so on) of the specific operating system\n(if any) on which the executable work runs, or a compiler used to\nproduce the work, or an object code interpreter used to run it.\n\n  The \"Corresponding Source\" for a work in object code form means all\nthe source code needed to generate, install, and (for an executable\nwork) run the object code and to modify the work, including scripts to\ncontrol those activities.  However, it does not include the work's\nSystem Libraries, or general-purpose tools or generally available free\nprograms which are used unmodified in performing those activities but\nwhich are not part of the work.  For example, Corresponding Source\nincludes interface definition files associated with source files for\nthe work, and the source code for shared libraries and dynamically\nlinked subprograms that the work is specifically designed to require,\nsuch as by intimate data communication or control flow between those\nsubprograms and other parts of the work.\n\n  The Corresponding Source need not include anything that users\ncan regenerate automatically from other parts of the Corresponding\nSource.\n\n  The Corresponding Source for a work in source code form is that\nsame work.\n\n  2. Basic Permissions.\n\n  All rights granted under this License are granted for the term of\ncopyright on the Program, and are irrevocable provided the stated\nconditions are met.  This License explicitly affirms your unlimited\npermission to run the unmodified Program.  The output from running a\ncovered work is covered by this License only if the output, given its\ncontent, constitutes a covered work.  This License acknowledges your\nrights of fair use or other equivalent, as provided by copyright law.\n\n  You may make, run and propagate covered works that you do not\nconvey, without conditions so long as your license otherwise remains\nin force.  You may convey covered works to others for the sole purpose\nof having them make modifications exclusively for you, or provide you\nwith facilities for running those works, provided that you comply with\nthe terms of this License in conveying all material for which you do\nnot control copyright.  Those thus making or running the covered works\nfor you must do so exclusively on your behalf, under your direction\nand control, on terms that prohibit them from making any copies of\nyour copyrighted material outside their relationship with you.\n\n  Conveying under any other circumstances is permitted solely under\nthe conditions stated below.  Sublicensing is not allowed; section 10\nmakes it unnecessary.\n\n  3. Protecting Users' Legal Rights From Anti-Circumvention Law.\n\n  No covered work shall be deemed part of an effective technological\nmeasure under any applicable law fulfilling obligations under article\n11 of the WIPO copyright treaty adopted on 20 December 1996, or\nsimilar laws prohibiting or restricting circumvention of such\nmeasures.\n\n  When you convey a covered work, you waive any legal power to forbid\ncircumvention of technological measures to the extent such circumvention\nis effected by exercising rights under this License with respect to\nthe covered work, and you disclaim any intention to limit operation or\nmodification of the work as a means of enforcing, against the work's\nusers, your or third parties' legal rights to forbid circumvention of\ntechnological measures.\n\n  4. Conveying Verbatim Copies.\n\n  You may convey verbatim copies of the Program's source code as you\nreceive it, in any medium, provided that you conspicuously and\nappropriately publish on each copy an appropriate copyright notice;\nkeep intact all notices stating that this License and any\nnon-permissive terms added in accord with section 7 apply to the code;\nkeep intact all notices of the absence of any warranty; and give all\nrecipients a copy of this License along with the Program.\n\n  You may charge any price or no price for each copy that you convey,\nand you may offer support or warranty protection for a fee.\n\n  5. Conveying Modified Source Versions.\n\n  You may convey a work based on the Program, or the modifications to\nproduce it from the Program, in the form of source code under the\nterms of section 4, provided that you also meet all of these conditions:\n\n    a) The work must carry prominent notices stating that you modified\n    it, and giving a relevant date.\n\n    b) The work must carry prominent notices stating that it is\n    released under this License and any conditions added under section\n    7.  This requirement modifies the requirement in section 4 to\n    \"keep intact all notices\".\n\n    c) You must license the entire work, as a whole, under this\n    License to anyone who comes into possession of a copy.  This\n    License will therefore apply, along with any applicable section 7\n    additional terms, to the whole of the work, and all its parts,\n    regardless of how they are packaged.  This License gives no\n    permission to license the work in any other way, but it does not\n    invalidate such permission if you have separately received it.\n\n    d) If the work has interactive user interfaces, each must display\n    Appropriate Legal Notices; however, if the Program has interactive\n    interfaces that do not display Appropriate Legal Notices, your\n    work need not make them do so.\n\n  A compilation of a covered work with other separate and independent\nworks, which are not by their nature extensions of the covered work,\nand which are not combined with it such as to form a larger program,\nin or on a volume of a storage or distribution medium, is called an\n\"aggregate\" if the compilation and its resulting copyright are not\nused to limit the access or legal rights of the compilation's users\nbeyond what the individual works permit.  Inclusion of a covered work\nin an aggregate does not cause this License to apply to the other\nparts of the aggregate.\n\n  6. Conveying Non-Source Forms.\n\n  You may convey a covered work in object code form under the terms\nof sections 4 and 5, provided that you also convey the\nmachine-readable Corresponding Source under the terms of this License,\nin one of these ways:\n\n    a) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by the\n    Corresponding Source fixed on a durable physical medium\n    customarily used for software interchange.\n\n    b) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by a\n    written offer, valid for at least three years and valid for as\n    long as you offer spare parts or customer support for that product\n    model, to give anyone who possesses the object code either (1) a\n    copy of the Corresponding Source for all the software in the\n    product that is covered by this License, on a durable physical\n    medium customarily used for software interchange, for a price no\n    more than your reasonable cost of physically performing this\n    conveying of source, or (2) access to copy the\n    Corresponding Source from a network server at no charge.\n\n    c) Convey individual copies of the object code with a copy of the\n    written offer to provide the Corresponding Source.  This\n    alternative is allowed only occasionally and noncommercially, and\n    only if you received the object code with such an offer, in accord\n    with subsection 6b.\n\n    d) Convey the object code by offering access from a designated\n    place (gratis or for a charge), and offer equivalent access to the\n    Corresponding Source in the same way through the same place at no\n    further charge.  You need not require recipients to copy the\n    Corresponding Source along with the object code.  If the place to\n    copy the object code is a network server, the Corresponding Source\n    may be on a different server (operated by you or a third party)\n    that supports equivalent copying facilities, provided you maintain\n    clear directions next to the object code saying where to find the\n    Corresponding Source.  Regardless of what server hosts the\n    Corresponding Source, you remain obligated to ensure that it is\n    available for as long as needed to satisfy these requirements.\n\n    e) Convey the object code using peer-to-peer transmission, provided\n    you inform other peers where the object code and Corresponding\n    Source of the work are being offered to the general public at no\n    charge under subsection 6d.\n\n  A separable portion of the object code, whose source code is excluded\nfrom the Corresponding Source as a System Library, need not be\nincluded in conveying the object code work.\n\n  A \"User Product\" is either (1) a \"consumer product\", which means any\ntangible personal property which is normally used for personal, family,\nor household purposes, or (2) anything designed or sold for incorporation\ninto a dwelling.  In determining whether a product is a consumer product,\ndoubtful cases shall be resolved in favor of coverage.  For a particular\nproduct received by a particular user, \"normally used\" refers to a\ntypical or common use of that class of product, regardless of the status\nof the particular user or of the way in which the particular user\nactually uses, or expects or is expected to use, the product.  A product\nis a consumer product regardless of whether the product has substantial\ncommercial, industrial or non-consumer uses, unless such uses represent\nthe only significant mode of use of the product.\n\n  \"Installation Information\" for a User Product means any methods,\nprocedures, authorization keys, or other information required to install\nand execute modified versions of a covered work in that User Product from\na modified version of its Corresponding Source.  The information must\nsuffice to ensure that the continued functioning of the modified object\ncode is in no case prevented or interfered with solely because\nmodification has been made.\n\n  If you convey an object code work under this section in, or with, or\nspecifically for use in, a User Product, and the conveying occurs as\npart of a transaction in which the right of possession and use of the\nUser Product is transferred to the recipient in perpetuity or for a\nfixed term (regardless of how the transaction is characterized), the\nCorresponding Source conveyed under this section must be accompanied\nby the Installation Information.  But this requirement does not apply\nif neither you nor any third party retains the ability to install\nmodified object code on the User Product (for example, the work has\nbeen installed in ROM).\n\n  The requirement to provide Installation Information does not include a\nrequirement to continue to provide support service, warranty, or updates\nfor a work that has been modified or installed by the recipient, or for\nthe User Product in which it has been modified or installed.  Access to a\nnetwork may be denied when the modification itself materially and\nadversely affects the operation of the network or violates the rules and\nprotocols for communication across the network.\n\n  Corresponding Source conveyed, and Installation Information provided,\nin accord with this section must be in a format that is publicly\ndocumented (and with an implementation available to the public in\nsource code form), and must require no special password or key for\nunpacking, reading or copying.\n\n  7. Additional Terms.\n\n  \"Additional permissions\" are terms that supplement the terms of this\nLicense by making exceptions from one or more of its conditions.\nAdditional permissions that are applicable to the entire Program shall\nbe treated as though they were included in this License, to the extent\nthat they are valid under applicable law.  If additional permissions\napply only to part of the Program, that part may be used separately\nunder those permissions, but the entire Program remains governed by\nthis License without regard to the additional permissions.\n\n  When you convey a copy of a covered work, you may at your option\nremove any additional permissions from that copy, or from any part of\nit.  (Additional permissions may be written to require their own\nremoval in certain cases when you modify the work.)  You may place\nadditional permissions on material, added by you to a covered work,\nfor which you have or can give appropriate copyright permission.\n\n  Notwithstanding any other provision of this License, for material you\nadd to a covered work, you may (if authorized by the copyright holders of\nthat material) supplement the terms of this License with terms:\n\n    a) Disclaiming warranty or limiting liability differently from the\n    terms of sections 15 and 16 of this License; or\n\n    b) Requiring preservation of specified reasonable legal notices or\n    author attributions in that material or in the Appropriate Legal\n    Notices displayed by works containing it; or\n\n    c) Prohibiting misrepresentation of the origin of that material, or\n    requiring that modified versions of such material be marked in\n    reasonable ways as different from the original version; or\n\n    d) Limiting the use for publicity purposes of names of licensors or\n    authors of the material; or\n\n    e) Declining to grant rights under trademark law for use of some\n    trade names, trademarks, or service marks; or\n\n    f) Requiring indemnification of licensors and authors of that\n    material by anyone who conveys the material (or modified versions of\n    it) with contractual assumptions of liability to the recipient, for\n    any liability that these contractual assumptions directly impose on\n    those licensors and authors.\n\n  All other non-permissive additional terms are considered \"further\nrestrictions\" within the meaning of section 10.  If the Program as you\nreceived it, or any part of it, contains a notice stating that it is\ngoverned by this License along with a term that is a further\nrestriction, you may remove that term.  If a license document contains\na further restriction but permits relicensing or conveying under this\nLicense, you may add to a covered work material governed by the terms\nof that license document, provided that the further restriction does\nnot survive such relicensing or conveying.\n\n  If you add terms to a covered work in accord with this section, you\nmust place, in the relevant source files, a statement of the\nadditional terms that apply to those files, or a notice indicating\nwhere to find the applicable terms.\n\n  Additional terms, permissive or non-permissive, may be stated in the\nform of a separately written license, or stated as exceptions;\nthe above requirements apply either way.\n\n  8. Termination.\n\n  You may not propagate or modify a covered work except as expressly\nprovided under this License.  Any attempt otherwise to propagate or\nmodify it is void, and will automatically terminate your rights under\nthis License (including any patent licenses granted under the third\nparagraph of section 11).\n\n  However, if you cease all violation of this License, then your\nlicense from a particular copyright holder is reinstated (a)\nprovisionally, unless and until the copyright holder explicitly and\nfinally terminates your license, and (b) permanently, if the copyright\nholder fails to notify you of the violation by some reasonable means\nprior to 60 days after the cessation.\n\n  Moreover, your license from a particular copyright holder is\nreinstated permanently if the copyright holder notifies you of the\nviolation by some reasonable means, this is the first time you have\nreceived notice of violation of this License (for any work) from that\ncopyright holder, and you cure the violation prior to 30 days after\nyour receipt of the notice.\n\n  Termination of your rights under this section does not terminate the\nlicenses of parties who have received copies or rights from you under\nthis License.  If your rights have been terminated and not permanently\nreinstated, you do not qualify to receive new licenses for the same\nmaterial under section 10.\n\n  9. Acceptance Not Required for Having Copies.\n\n  You are not required to accept this License in order to receive or\nrun a copy of the Program.  Ancillary propagation of a covered work\noccurring solely as a consequence of using peer-to-peer transmission\nto receive a copy likewise does not require acceptance.  However,\nnothing other than this License grants you permission to propagate or\nmodify any covered work.  These actions infringe copyright if you do\nnot accept this License.  Therefore, by modifying or propagating a\ncovered work, you indicate your acceptance of this License to do so.\n\n  10. Automatic Licensing of Downstream Recipients.\n\n  Each time you convey a covered work, the recipient automatically\nreceives a license from the original licensors, to run, modify and\npropagate that work, subject to this License.  You are not responsible\nfor enforcing compliance by third parties with this License.\n\n  An \"entity transaction\" is a transaction transferring control of an\norganization, or substantially all assets of one, or subdividing an\norganization, or merging organizations.  If propagation of a covered\nwork results from an entity transaction, each party to that\ntransaction who receives a copy of the work also receives whatever\nlicenses to the work the party's predecessor in interest had or could\ngive under the previous paragraph, plus a right to possession of the\nCorresponding Source of the work from the predecessor in interest, if\nthe predecessor has it or can get it with reasonable efforts.\n\n  You may not impose any further restrictions on the exercise of the\nrights granted or affirmed under this License.  For example, you may\nnot impose a license fee, royalty, or other charge for exercise of\nrights granted under this License, and you may not initiate litigation\n(including a cross-claim or counterclaim in a lawsuit) alleging that\nany patent claim is infringed by making, using, selling, offering for\nsale, or importing the Program or any portion of it.\n\n  11. Patents.\n\n  A \"contributor\" is a copyright holder who authorizes use under this\nLicense of the Program or a work on which the Program is based.  The\nwork thus licensed is called the contributor's \"contributor version\".\n\n  A contributor's \"essential patent claims\" are all patent claims\nowned or controlled by the contributor, whether already acquired or\nhereafter acquired, that would be infringed by some manner, permitted\nby this License, of making, using, or selling its contributor version,\nbut do not include claims that would be infringed only as a\nconsequence of further modification of the contributor version.  For\npurposes of this definition, \"control\" includes the right to grant\npatent sublicenses in a manner consistent with the requirements of\nthis License.\n\n  Each contributor grants you a non-exclusive, worldwide, royalty-free\npatent license under the contributor's essential patent claims, to\nmake, use, sell, offer for sale, import and otherwise run, modify and\npropagate the contents of its contributor version.\n\n  In the following three paragraphs, a \"patent license\" is any express\nagreement or commitment, however denominated, not to enforce a patent\n(such as an express permission to practice a patent or covenant not to\nsue for patent infringement).  To \"grant\" such a patent license to a\nparty means to make such an agreement or commitment not to enforce a\npatent against the party.\n\n  If you convey a covered work, knowingly relying on a patent license,\nand the Corresponding Source of the work is not available for anyone\nto copy, free of charge and under the terms of this License, through a\npublicly available network server or other readily accessible means,\nthen you must either (1) cause the Corresponding Source to be so\navailable, or (2) arrange to deprive yourself of the benefit of the\npatent license for this particular work, or (3) arrange, in a manner\nconsistent with the requirements of this License, to extend the patent\nlicense to downstream recipients.  \"Knowingly relying\" means you have\nactual knowledge that, but for the patent license, your conveying the\ncovered work in a country, or your recipient's use of the covered work\nin a country, would infringe one or more identifiable patents in that\ncountry that you have reason to believe are valid.\n\n  If, pursuant to or in connection with a single transaction or\narrangement, you convey, or propagate by procuring conveyance of, a\ncovered work, and grant a patent license to some of the parties\nreceiving the covered work authorizing them to use, propagate, modify\nor convey a specific copy of the covered work, then the patent license\nyou grant is automatically extended to all recipients of the covered\nwork and works based on it.\n\n  A patent license is \"discriminatory\" if it does not include within\nthe scope of its coverage, prohibits the exercise of, or is\nconditioned on the non-exercise of one or more of the rights that are\nspecifically granted under this License.  You may not convey a covered\nwork if you are a party to an arrangement with a third party that is\nin the business of distributing software, under which you make payment\nto the third party based on the extent of your activity of conveying\nthe work, and under which the third party grants, to any of the\nparties who would receive the covered work from you, a discriminatory\npatent license (a) in connection with copies of the covered work\nconveyed by you (or copies made from those copies), or (b) primarily\nfor and in connection with specific products or compilations that\ncontain the covered work, unless you entered into that arrangement,\nor that patent license was granted, prior to 28 March 2007.\n\n  Nothing in this License shall be construed as excluding or limiting\nany implied license or other defenses to infringement that may\notherwise be available to you under applicable patent law.\n\n  12. No Surrender of Others' Freedom.\n\n  If conditions are imposed on you (whether by court order, agreement or\notherwise) that contradict the conditions of this License, they do not\nexcuse you from the conditions of this License.  If you cannot convey a\ncovered work so as to satisfy simultaneously your obligations under this\nLicense and any other pertinent obligations, then as a consequence you may\nnot convey it at all.  For example, if you agree to terms that obligate you\nto collect a royalty for further conveying from those to whom you convey\nthe Program, the only way you could satisfy both those terms and this\nLicense would be to refrain entirely from conveying the Program.\n\n  13. Use with the GNU Affero General Public License.\n\n  Notwithstanding any other provision of this License, you have\npermission to link or combine any covered work with a work licensed\nunder version 3 of the GNU Affero General Public License into a single\ncombined work, and to convey the resulting work.  The terms of this\nLicense will continue to apply to the part which is the covered work,\nbut the special requirements of the GNU Affero General Public License,\nsection 13, concerning interaction through a network will apply to the\ncombination as such.\n\n  14. Revised Versions of this License.\n\n  The Free Software Foundation may publish revised and/or new versions of\nthe GNU General Public License from time to time.  Such new versions will\nbe similar in spirit to the present version, but may differ in detail to\naddress new problems or concerns.\n\n  Each version is given a distinguishing version number.  If the\nProgram specifies that a certain numbered version of the GNU General\nPublic License \"or any later version\" applies to it, you have the\noption of following the terms and conditions either of that numbered\nversion or of any later version published by the Free Software\nFoundation.  If the Program does not specify a version number of the\nGNU General Public License, you may choose any version ever published\nby the Free Software Foundation.\n\n  If the Program specifies that a proxy can decide which future\nversions of the GNU General Public License can be used, that proxy's\npublic statement of acceptance of a version permanently authorizes you\nto choose that version for the Program.\n\n  Later license versions may give you additional or different\npermissions.  However, no additional obligations are imposed on any\nauthor or copyright holder as a result of your choosing to follow a\nlater version.\n\n  15. Disclaimer of Warranty.\n\n  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY\nAPPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT\nHOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY\nOF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,\nTHE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\nPURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM\nIS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF\nALL NECESSARY SERVICING, REPAIR OR CORRECTION.\n\n  16. Limitation of Liability.\n\n  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\nWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS\nTHE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY\nGENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE\nUSE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF\nDATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD\nPARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),\nEVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF\nSUCH DAMAGES.\n\n  17. Interpretation of Sections 15 and 16.\n\n  If the disclaimer of warranty and limitation of liability provided\nabove cannot be given local legal effect according to their terms,\nreviewing courts shall apply local law that most closely approximates\nan absolute waiver of all civil liability in connection with the\nProgram, unless a warranty or assumption of liability accompanies a\ncopy of the Program in return for a fee.\n\n                     END OF TERMS AND CONDITIONS\n\n            How to Apply These Terms to Your New Programs\n\n  If you develop a new program, and you want it to be of the greatest\npossible use to the public, the best way to achieve this is to make it\nfree software which everyone can redistribute and change under these terms.\n\n  To do so, attach the following notices to the program.  It is safest\nto attach them to the start of each source file to most effectively\nstate the exclusion of warranty; and each file should have at least\nthe \"copyright\" line and a pointer to where the full notice is found.\n\n    <one line to give the program's name and a brief idea of what it does.>\n    Copyright (C) <year>  <name of author>\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 3 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\nAlso add information on how to contact you by electronic and paper mail.\n\n  If the program does terminal interaction, make it output a short\nnotice like this when it starts in an interactive mode:\n\n    <program>  Copyright (C) <year>  <name of author>\n    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.\n    This is free software, and you are welcome to redistribute it\n    under certain conditions; type `show c' for details.\n\nThe hypothetical commands `show w' and `show c' should show the appropriate\nparts of the General Public License.  Of course, your program's commands\nmight be different; for a GUI interface, you would use an \"about box\".\n\n  You should also get your employer (if you work as a programmer) or school,\nif any, to sign a \"copyright disclaimer\" for the program, if necessary.\nFor more information on this, and how to apply and follow the GNU GPL, see\n<http://www.gnu.org/licenses/>.\n\n  The GNU General Public License does not permit incorporating your program\ninto proprietary programs.  If your program is a subroutine library, you\nmay consider it more useful to permit linking proprietary applications with\nthe library.  If this is what you want to do, use the GNU Lesser General\nPublic License instead of this License.  But first, please read\n<http://www.gnu.org/philosophy/why-not-lgpl.html>.\n"
  },
  {
    "path": "WebBench/Makefile",
    "content": "CFLAGS?=\t-Wall -ggdb -W -O\nCC?=\t\tgcc\nLIBS?=\nLDFLAGS?=\nPREFIX?=\t/usr/local/webbench\nVERSION=1.5\nTMPDIR=/tmp/webbench-$(VERSION)\n\nall:   webbench tags\n\ntags:  *.c\n\t-ctags *.c\n\ninstall: webbench\n\tinstall -d $(DESTDIR)$(PREFIX)/bin\n\tinstall -s webbench $(DESTDIR)$(PREFIX)/bin\n\tln -sf $(DESTDIR)$(PREFIX)/bin/webbench $(DESTDIR)/usr/local/bin/webbench\n\n\tinstall -d $(DESTDIR)/usr/local/man/man1\n\tinstall -d $(DESTDIR)$(PREFIX)/man/man1\n\tinstall -m 644 webbench.1 $(DESTDIR)$(PREFIX)/man/man1\n\tln -sf $(DESTDIR)$(PREFIX)/man/man1/webbench.1 $(DESTDIR)/usr/local/man/man1/webbench.1\n\n\tinstall -d $(DESTDIR)$(PREFIX)/share/doc/webbench\n\tinstall -m 644 debian/copyright $(DESTDIR)$(PREFIX)/share/doc/webbench\n\tinstall -m 644 debian/changelog $(DESTDIR)$(PREFIX)/share/doc/webbench\n\nwebbench: webbench.o Makefile\n\t$(CC) $(CFLAGS) $(LDFLAGS) -o webbench webbench.o $(LIBS) \n\nclean:\n\t-rm -f *.o webbench *~ core *.core tags\n\t\ntar:   clean\n\t-debian/rules clean\n\trm -rf $(TMPDIR)\n\tinstall -d $(TMPDIR)\n\tcp -p Makefile webbench.c socket.c webbench.1 $(TMPDIR)\n\tinstall -d $(TMPDIR)/debian\n\t-cp -p debian/* $(TMPDIR)/debian\n\tln -sf debian/copyright $(TMPDIR)/COPYRIGHT\n\tln -sf debian/changelog $(TMPDIR)/ChangeLog\n\t-cd $(TMPDIR) && cd .. && tar cozf webbench-$(VERSION).tar.gz webbench-$(VERSION)\n\nwebbench.o:\twebbench.c socket.c Makefile\n\n.PHONY: clean install all tar\n"
  },
  {
    "path": "WebBench/README.md",
    "content": "# WebBench\n\nWebbench是一个在linux下使用的非常简单的网站压测工具。它使用fork()模拟多个客户端同时访问我们设定的URL，测试网站在压力下工作的性能，最多可以模拟3万个并发连接去测试网站的负载能力。\n\n## 依赖\nctags\n\n## 使用：\n\n\tsudo make && sudo make install PREFIX=your_path_to_webbench\n  \n## 命令行选项：\n\n\n\n\n| 短参        | 长参数           | 作用   |\n| ------------- |:-------------:| -----:|\n|-f     |--force                |不需要等待服务器响应               | \n|-r     |--reload               |发送重新加载请求                   |\n|-t     |--time <sec>           |运行多长时间，单位：秒\"            |\n|-p     |--proxy <server:port>  |使用代理服务器来发送请求\t    |\n|-c     |--clients <n>          |创建多少个客户端，默认1个\"         |\n|-9     |--http09               |使用 HTTP/0.9                      |\n|-1     |--http10               |使用 HTTP/1.0 协议                 |\n|-2     |--http11               |使用 HTTP/1.1 协议                 |\n|       |--get                  |使用 GET请求方法                   |\n|       |--head                 |使用 HEAD请求方法                    |\n|       |--options              |使用 OPTIONS请求方法               |\n|       |--trace                |使用 TRACE请求方法                 |\n|-?/-h  |--help                 |打印帮助信息                       |\n|-V     |--version              |显示版本号                         |\n"
  },
  {
    "path": "WebBench/build.sh",
    "content": "sudo make && sudo make install PREFIX=."
  },
  {
    "path": "WebBench/debian/changelog",
    "content": "webbench (1.5) unstable; urgency=low\n\n  * allow building with both Gnu and BSD make\n\n -- Radim Kolar <hsn@home>  Fri, Jun 25 12:00:20 CEST 2004\n\nwebbench (1.4) unstable; urgency=low\n\n  * check if url is not too long\n  * report correct program version number\n  * use yield() when waiting for test start\n  * corrected error codes\n  * check availability of test server first\n  * do not abort test if first request failed\n  * report when some childrens are dead.\n  * use alarm, not time() for lower syscal use by bench\n  * use mode 644 for installed doc\n  * makefile cleaned for better freebsd ports integration\n\n -- Radim Kolar <hsn@home>  Thu, 15 Jan 2004 11:15:52 +0100\n\nwebbench (1.3) unstable; urgency=low\n\n  * Build fixes for freeBSD\n  * Default benchmark time 60 -> 30\n  * generate tar with subdirectory\n  * added to freeBSD ports collection\n\n -- Radim Kolar <hsn@home>  Mon, 12 Jan 2004 17:00:24 +0100\n\nwebbench (1.2) unstable; urgency=low\n\n  * Only debian-related bugfixes\n  * Updated Debian/rules\n  * Adapted to fit new directory system\n  * moved from debstd to dh_*\n\n -- Radim Kolar <hsn@home>  Fri, 18 Jan 2002 12:33:04 +0100\n\nwebbench (1.1) unstable; urgency=medium\n \n  * Program debianized\n  * added support for multiple methods (GET, HEAD, OPTIONS, TRACE)\n  * added support for multiple HTTP versions (0.9 -- 1.1)\n  * added long options\n  * added multiple clients\n  * wait for start of second before test\n  * test time can be specified\n  * better error checking when reading reply from server\n  * FIX: tests was one second longer than expected\n\n -- Radim Kolar <hsn@home>  Thu, 16 Sep 1999 18:48:00 +0200\n\nLocal variables:\nmode: debian-changelog\nEnd:\n"
  },
  {
    "path": "WebBench/debian/control",
    "content": "Source: webbench\nSection: web\nPriority: extra\nMaintainer: Radim Kolar <hsn@cybermail.net>\nBuild-Depends: debhelper (>> 3.0.0)\nStandards-Version: 3.5.2\n\nPackage: webbench\nArchitecture: any\nDepends: ${shlibs:Depends}\nDescription: Simple forking Web benchmark\n webbench is very simple program for benchmarking WWW or Proxy servers.\n Uses fork() for simulating multiple clients load. Can use HTTP 0.9 - 1.1\n requests, but Keep-Alive connections are not supported.\n"
  },
  {
    "path": "WebBench/debian/copyright",
    "content": "Webbench was written by Radim Kolar 1997-2004 (hsn@netmag.cz).\n\nUNIX sockets code (socket.c) taken from popclient 1.5 4/1/94 \npublic domain code, created by Virginia Tech Computing Center.\n\nCopyright: GPL (see /usr/share/common-licenses/GPL)\n"
  },
  {
    "path": "WebBench/debian/dirs",
    "content": "usr/bin"
  },
  {
    "path": "WebBench/debian/rules",
    "content": "#!/usr/bin/make -f\n# Sample debian/rules that uses debhelper.\n# GNU copyright 1997 to 1999 by Joey Hess.\n\n# Uncomment this to turn on verbose mode.\n#export DH_VERBOSE=1\n\n# This is the debhelper compatability version to use.\nexport DH_COMPAT=3\n\nconfigure: configure-stamp\nconfigure-stamp:\n\tdh_testdir\n\ttouch configure-stamp\n\nbuild: configure-stamp build-stamp\nbuild-stamp:\n\tdh_testdir\n\t$(MAKE)\n\ttouch build-stamp\n\nclean:\n\tdh_testdir\n\trm -f build-stamp configure-stamp\n\n\t# Add here commands to clean up after the build process.\n\t-$(MAKE) clean\n\n\tdh_clean\n\ninstall: build\n\tdh_testdir\n\tdh_testroot\n\tdh_clean -k\n\tdh_installdirs\n\n\t# Add here commands to install the package into debian/webbench.\n\t$(MAKE) install DESTDIR=$(CURDIR)/debian/webbench\n\n\n# Build architecture-independent files here.\nbinary-indep: build install\n# We have nothing to do by default.\n\n# Build architecture-dependent files here.\nbinary-arch: build install\n\tdh_testdir\n\tdh_testroot\n\tdh_installdocs\n\tdh_installman webbench.1\n\tdh_installchangelogs \n\tdh_link\n\tdh_strip\n\tdh_compress\n\tdh_fixperms\n#\tdh_makeshlibs\n\tdh_installdeb\n\tdh_shlibdeps\n\tdh_gencontrol\n\tdh_md5sums\n\tdh_builddeb\n\nbinary: binary-indep binary-arch\n.PHONY: build clean binary-indep binary-arch binary install configure\n"
  },
  {
    "path": "WebBench/man/man1/webbench.1",
    "content": "-.TH WEBBENCH 1 \"14 Jan 2004\"\n-.\\\" NAME should be all caps, SECTION should be 1-8, maybe w/ subsection\n-.\\\" other parms are allowed: see man(7), man(1)\n-.SH NAME\n-webbench \\- simple forking web benchmark\n-.SH SYNOPSIS\n-.B webbench\n-.I \"[options] URL\"\n-.br\n-.SH \"AUTHOR\"\n-This program and manual page was written by Radim Kolar,\n-for the\n-.B Supreme Personality of Godhead\n-(but may be used by others).\n-.SH \"DESCRIPTION\"\n-.B webbench\n-is simple program for benchmarking HTTP servers or any\n-other servers, which can be accessed via HTTP proxy. Unlike others\n-benchmarks,\n-.B webbench\n-uses multiple processes for simulating traffic\n-generated by multiple users. This allows better operating\n-on SMP systems and on systems with slow or buggy implementation\n-of select().\n-.SH OPTIONS\n-The programs follow the usual GNU command line syntax, with long\n-options starting with two dashes (`-').\n-A summary of options are included below.\n-.TP\n-.B \\-?, \\-h, \\-\\-help\n-Show summary of options.\n-.TP\n-.B \\-v, \\-\\-version\n-Show version of program.\n-.TP\n-.B \\-f, \\-\\-force\n-Do not wait for any response from server. Close connection after\n-request is send. This option produce quite a good denial of service\n-attack.\n-.TP\n-.B \\-9, \\-\\-http09\n-Use HTTP/0.9 protocol, if possible.\n-.TP\n-.B \\-1, \\-\\-http10\n-Use HTTP/1.0 protocol, if possible.\n-.TP\n-.B \\-2, \\-\\-http11\n-Use HTTP/1.1 protocol (without\n-.I Keep-Alive\n-), if possible.\n-.TP\n-.B \\-r, \\-\\-reload\n-Forces proxy to reload document. If proxy is not\n-set, option has no effect.\n-.TP\n-.B \\-t, \\-\\-time <n>\n-Run benchmark for\n-.I <n>\n-seconds. Default value is 30.\n-.TP\n-.B \\-p, \\-\\-proxy <server:port>\n-Send request via proxy server. Needed for supporting others protocols\n-than HTTP.\n-.TP\n-.B \\-\\-get\n-Use GET request method.\n-.TP\n-.B \\-\\-head\n-Use HEAD request method.\n-.TP\n-.B \\-\\-options\n-Use OPTIONS request method.\n-.TP\n-.B \\-\\-trace\n-Use TRACE request method.\n-.TP\n-.B \\-c, \\-\\-clients <n>\n-Use\n-.I <n>\n-multiple clients for benchmark. Default value\n-is 1.\n-.SH \"EXIT STATUS\"\n-.TP\n-0 - sucess\n-.TP\n-1 - benchmark failed, can not connect to server\n-.TP\n-2 - bad command line argument(s)\n-.TP\n-3 - internal error, i.e. fork failed\n-.SH \"TODO\"\n-Include support for using\n-.I Keep-Alive\n-HTTP/1.1 connections.\n-.SH \"COPYING\"\n-Webbench is distributed under GPL. Copyright 1997-2004\n-Radim Kolar (hsn@netmag.cz). \n-UNIX sockets code taken from popclient 1.5 4/1/94 \n-public domain code, created by Virginia Tech Computing Center.\n-.BR\n-This man page is public domain.\n"
  },
  {
    "path": "WebBench/share/doc/webbench/changelog",
    "content": "webbench (1.5) unstable; urgency=low\n\n  * allow building with both Gnu and BSD make\n\n -- Radim Kolar <hsn@home>  Fri, Jun 25 12:00:20 CEST 2004\n\nwebbench (1.4) unstable; urgency=low\n\n  * check if url is not too long\n  * report correct program version number\n  * use yield() when waiting for test start\n  * corrected error codes\n  * check availability of test server first\n  * do not abort test if first request failed\n  * report when some childrens are dead.\n  * use alarm, not time() for lower syscal use by bench\n  * use mode 644 for installed doc\n  * makefile cleaned for better freebsd ports integration\n\n -- Radim Kolar <hsn@home>  Thu, 15 Jan 2004 11:15:52 +0100\n\nwebbench (1.3) unstable; urgency=low\n\n  * Build fixes for freeBSD\n  * Default benchmark time 60 -> 30\n  * generate tar with subdirectory\n  * added to freeBSD ports collection\n\n -- Radim Kolar <hsn@home>  Mon, 12 Jan 2004 17:00:24 +0100\n\nwebbench (1.2) unstable; urgency=low\n\n  * Only debian-related bugfixes\n  * Updated Debian/rules\n  * Adapted to fit new directory system\n  * moved from debstd to dh_*\n\n -- Radim Kolar <hsn@home>  Fri, 18 Jan 2002 12:33:04 +0100\n\nwebbench (1.1) unstable; urgency=medium\n \n  * Program debianized\n  * added support for multiple methods (GET, HEAD, OPTIONS, TRACE)\n  * added support for multiple HTTP versions (0.9 -- 1.1)\n  * added long options\n  * added multiple clients\n  * wait for start of second before test\n  * test time can be specified\n  * better error checking when reading reply from server\n  * FIX: tests was one second longer than expected\n\n -- Radim Kolar <hsn@home>  Thu, 16 Sep 1999 18:48:00 +0200\n\nLocal variables:\nmode: debian-changelog\nEnd:\n"
  },
  {
    "path": "WebBench/share/doc/webbench/copyright",
    "content": "Webbench was written by Radim Kolar 1997-2004 (hsn@netmag.cz).\n\nUNIX sockets code (socket.c) taken from popclient 1.5 4/1/94 \npublic domain code, created by Virginia Tech Computing Center.\n\nCopyright: GPL (see /usr/share/common-licenses/GPL)\n"
  },
  {
    "path": "WebBench/socket.c",
    "content": "/* $Id: socket.c 1.1 1995/01/01 07:11:14 cthuang Exp $\n *\n * This module has been modified by Radim Kolar for OS/2 emx\n */\n\n/***********************************************************************\n  module:       socket.c\n  program:      popclient\n  SCCS ID:      @(#)socket.c    1.5  4/1/94\n  programmer:   Virginia Tech Computing Center\n  compiler:     DEC RISC C compiler (Ultrix 4.1)\n  environment:  DEC Ultrix 4.3 \n  description:  UNIX sockets code.\n ***********************************************************************/\n \n#include <sys/types.h>\n#include <sys/socket.h>\n#include <fcntl.h>\n#include <netinet/in.h>\n#include <arpa/inet.h>\n#include <netdb.h>\n#include <sys/time.h>\n#include <string.h>\n#include <unistd.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <stdarg.h>\n\nint Socket(const char *host, int clientPort)\n{\n    int sock;\n    unsigned long inaddr;\n    struct sockaddr_in ad;\n    struct hostent *hp;\n    \n    memset(&ad, 0, sizeof(ad));\n    ad.sin_family = AF_INET;\n\n    inaddr = inet_addr(host);\n    if (inaddr != INADDR_NONE)\n        memcpy(&ad.sin_addr, &inaddr, sizeof(inaddr));\n    else\n    {\n        hp = gethostbyname(host);\n        if (hp == NULL)\n            return -1;\n        memcpy(&ad.sin_addr, hp->h_addr, hp->h_length);\n    }\n    ad.sin_port = htons(clientPort);\n    \n    sock = socket(AF_INET, SOCK_STREAM, 0);\n\n    \n    if (sock < 0)\n        return sock;\n\n    // int optval = 1;\n    // if(setsockopt(sock, SOL_SOCKET,  SO_REUSEPORT, &optval, sizeof(optval)) == -1)\n    //     return -1;\n    if (connect(sock, (struct sockaddr *)&ad, sizeof(ad)) < 0)\n    {\n        close(sock);\n        return -1;\n    }\n\n    return sock;\n}\n\n"
  },
  {
    "path": "WebBench/tags",
    "content": "!_TAG_FILE_FORMAT\t2\t/extended format; --format=1 will not append ;\" to lines/\n!_TAG_FILE_SORTED\t1\t/0=unsorted, 1=sorted, 2=foldcase/\n!_TAG_PROGRAM_AUTHOR\tDarren Hiebert\t/dhiebert@users.sourceforge.net/\n!_TAG_PROGRAM_NAME\tExuberant Ctags\t//\n!_TAG_PROGRAM_URL\thttp://ctags.sourceforge.net\t/official site/\n!_TAG_PROGRAM_VERSION\t5.9~svn20110310\t//\nMETHOD_GET\twebbench.c\t38;\"\td\tfile:\nMETHOD_GET\twebbench1.c\t38;\"\td\tfile:\nMETHOD_GET\twebbench2.c\t38;\"\td\tfile:\nMETHOD_HEAD\twebbench.c\t39;\"\td\tfile:\nMETHOD_HEAD\twebbench1.c\t39;\"\td\tfile:\nMETHOD_HEAD\twebbench2.c\t39;\"\td\tfile:\nMETHOD_OPTIONS\twebbench.c\t40;\"\td\tfile:\nMETHOD_OPTIONS\twebbench1.c\t40;\"\td\tfile:\nMETHOD_OPTIONS\twebbench2.c\t40;\"\td\tfile:\nMETHOD_TRACE\twebbench.c\t41;\"\td\tfile:\nMETHOD_TRACE\twebbench1.c\t41;\"\td\tfile:\nMETHOD_TRACE\twebbench2.c\t41;\"\td\tfile:\nPROGRAM_VERSION\twebbench.c\t42;\"\td\tfile:\nPROGRAM_VERSION\twebbench1.c\t42;\"\td\tfile:\nPROGRAM_VERSION\twebbench2.c\t42;\"\td\tfile:\nREQUEST_SIZE\twebbench.c\t56;\"\td\tfile:\nREQUEST_SIZE\twebbench1.c\t56;\"\td\tfile:\nREQUEST_SIZE\twebbench2.c\t56;\"\td\tfile:\nSocket\tsocket.c\t/^int Socket(const char *host, int clientPort)$/;\"\tf\nalarm_handler\twebbench.c\t/^static void alarm_handler(int signal)$/;\"\tf\tfile:\nalarm_handler\twebbench1.c\t/^static void alarm_handler(int signal)$/;\"\tf\tfile:\nalarm_handler\twebbench2.c\t/^static void alarm_handler(int signal)$/;\"\tf\tfile:\nbench\twebbench.c\t/^static int bench(void)$/;\"\tf\tfile:\nbench\twebbench1.c\t/^static int bench(void)$/;\"\tf\tfile:\nbench\twebbench2.c\t/^static int bench(void)$/;\"\tf\tfile:\nbenchcore\twebbench.c\t/^void benchcore(const char *host,const int port,const char *req)$/;\"\tf\nbenchcore\twebbench1.c\t/^void benchcore(const char *host,const int port,const char *req)$/;\"\tf\nbenchcore\twebbench2.c\t/^void benchcore(const char *host,const int port,const char *req)$/;\"\tf\nbenchtime\twebbench.c\t/^int benchtime=30;$/;\"\tv\nbenchtime\twebbench1.c\t/^int benchtime=30;$/;\"\tv\nbenchtime\twebbench2.c\t/^int benchtime=30;$/;\"\tv\nbuild_request\twebbench.c\t/^void build_request(const char *url)$/;\"\tf\nbuild_request\twebbench1.c\t/^void build_request(const char *url)$/;\"\tf\nbuild_request\twebbench2.c\t/^void build_request(const char *url)$/;\"\tf\nbytes\twebbench.c\t/^int bytes=0;$/;\"\tv\nbytes\twebbench1.c\t/^int bytes=0;$/;\"\tv\nbytes\twebbench2.c\t/^int bytes=0;$/;\"\tv\nclients\twebbench.c\t/^int clients=1;$/;\"\tv\nclients\twebbench1.c\t/^int clients=1;$/;\"\tv\nclients\twebbench2.c\t/^int clients=1;$/;\"\tv\nfailed\twebbench.c\t/^int failed=0;$/;\"\tv\nfailed\twebbench1.c\t/^int failed=0;$/;\"\tv\nfailed\twebbench2.c\t/^int failed=0;$/;\"\tv\nforce\twebbench.c\t/^int force=0;$/;\"\tv\nforce\twebbench1.c\t/^int force=0;$/;\"\tv\nforce\twebbench2.c\t/^int force=0;$/;\"\tv\nforce_reload\twebbench.c\t/^int force_reload=0;$/;\"\tv\nforce_reload\twebbench1.c\t/^int force_reload=0;$/;\"\tv\nforce_reload\twebbench2.c\t/^int force_reload=0;$/;\"\tv\nhost\twebbench.c\t/^char host[MAXHOSTNAMELEN];$/;\"\tv\nhost\twebbench1.c\t/^char host[MAXHOSTNAMELEN];$/;\"\tv\nhost\twebbench2.c\t/^char host[MAXHOSTNAMELEN];$/;\"\tv\nhttp10\twebbench.c\t/^int http10=1; \\/* 0 - http\\/0.9, 1 - http\\/1.0, 2 - http\\/1.1 *\\/$/;\"\tv\nhttp10\twebbench1.c\t/^int http10=1; \\/* 0 - http\\/0.9, 1 - http\\/1.0, 2 - http\\/1.1 *\\/$/;\"\tv\nhttp10\twebbench2.c\t/^int http10=1; \\/* 0 - http\\/0.9, 1 - http\\/1.0, 2 - http\\/1.1 *\\/$/;\"\tv\nkeep_alive\twebbench.c\t/^bool keep_alive = false;$/;\"\tv\nkeep_alive\twebbench1.c\t/^bool keep_alive = false;$/;\"\tv\nkeep_alive\twebbench2.c\t/^bool keep_alive = false;$/;\"\tv\nlong_options\twebbench.c\t/^static const struct option long_options[]=$/;\"\tv\ttyperef:struct:option\tfile:\nlong_options\twebbench1.c\t/^static const struct option long_options[]=$/;\"\tv\ttyperef:struct:option\tfile:\nlong_options\twebbench2.c\t/^static const struct option long_options[]=$/;\"\tv\ttyperef:struct:option\tfile:\nmain\twebbench.c\t/^int main(int argc, char *argv[])$/;\"\tf\nmain\twebbench1.c\t/^int main(int argc, char *argv[])$/;\"\tf\nmain\twebbench2.c\t/^int main(int argc, char *argv[])$/;\"\tf\nmethod\twebbench.c\t/^int method=METHOD_GET;$/;\"\tv\nmethod\twebbench1.c\t/^int method=METHOD_GET;$/;\"\tv\nmethod\twebbench2.c\t/^int method=METHOD_GET;$/;\"\tv\nmypipe\twebbench.c\t/^int mypipe[2];$/;\"\tv\nmypipe\twebbench1.c\t/^int mypipe[2];$/;\"\tv\nmypipe\twebbench2.c\t/^int mypipe[2];$/;\"\tv\nproxyhost\twebbench.c\t/^char *proxyhost=NULL;$/;\"\tv\nproxyhost\twebbench1.c\t/^char *proxyhost=NULL;$/;\"\tv\nproxyhost\twebbench2.c\t/^char *proxyhost=NULL;$/;\"\tv\nproxyport\twebbench.c\t/^int proxyport=80;$/;\"\tv\nproxyport\twebbench1.c\t/^int proxyport=80;$/;\"\tv\nproxyport\twebbench2.c\t/^int proxyport=80;$/;\"\tv\nrequest\twebbench.c\t/^char request[REQUEST_SIZE];$/;\"\tv\nrequest\twebbench1.c\t/^char request[REQUEST_SIZE];$/;\"\tv\nrequest\twebbench2.c\t/^char request[REQUEST_SIZE];$/;\"\tv\nspeed\twebbench.c\t/^int speed=0;$/;\"\tv\nspeed\twebbench1.c\t/^int speed=0;$/;\"\tv\nspeed\twebbench2.c\t/^int speed=0;$/;\"\tv\ntimerexpired\twebbench.c\t/^volatile int timerexpired=0;$/;\"\tv\ntimerexpired\twebbench1.c\t/^volatile int timerexpired=0;$/;\"\tv\ntimerexpired\twebbench2.c\t/^volatile int timerexpired=0;$/;\"\tv\nusage\twebbench.c\t/^static void usage(void)$/;\"\tf\tfile:\nusage\twebbench1.c\t/^static void usage(void)$/;\"\tf\tfile:\nusage\twebbench2.c\t/^static void usage(void)$/;\"\tf\tfile:\n"
  },
  {
    "path": "WebBench/test.sh",
    "content": "./bin/webbench -t 60 -c 1000 -2 --get  http://127.0.0.1:80/hello"
  },
  {
    "path": "WebBench/webbench.1",
    "content": "-.TH WEBBENCH 1 \"14 Jan 2004\"\n-.\\\" NAME should be all caps, SECTION should be 1-8, maybe w/ subsection\n-.\\\" other parms are allowed: see man(7), man(1)\n-.SH NAME\n-webbench \\- simple forking web benchmark\n-.SH SYNOPSIS\n-.B webbench\n-.I \"[options] URL\"\n-.br\n-.SH \"AUTHOR\"\n-This program and manual page was written by Radim Kolar,\n-for the\n-.B Supreme Personality of Godhead\n-(but may be used by others).\n-.SH \"DESCRIPTION\"\n-.B webbench\n-is simple program for benchmarking HTTP servers or any\n-other servers, which can be accessed via HTTP proxy. Unlike others\n-benchmarks,\n-.B webbench\n-uses multiple processes for simulating traffic\n-generated by multiple users. This allows better operating\n-on SMP systems and on systems with slow or buggy implementation\n-of select().\n-.SH OPTIONS\n-The programs follow the usual GNU command line syntax, with long\n-options starting with two dashes (`-').\n-A summary of options are included below.\n-.TP\n-.B \\-?, \\-h, \\-\\-help\n-Show summary of options.\n-.TP\n-.B \\-v, \\-\\-version\n-Show version of program.\n-.TP\n-.B \\-f, \\-\\-force\n-Do not wait for any response from server. Close connection after\n-request is send. This option produce quite a good denial of service\n-attack.\n-.TP\n-.B \\-9, \\-\\-http09\n-Use HTTP/0.9 protocol, if possible.\n-.TP\n-.B \\-1, \\-\\-http10\n-Use HTTP/1.0 protocol, if possible.\n-.TP\n-.B \\-2, \\-\\-http11\n-Use HTTP/1.1 protocol (without\n-.I Keep-Alive\n-), if possible.\n-.TP\n-.B \\-r, \\-\\-reload\n-Forces proxy to reload document. If proxy is not\n-set, option has no effect.\n-.TP\n-.B \\-t, \\-\\-time <n>\n-Run benchmark for\n-.I <n>\n-seconds. Default value is 30.\n-.TP\n-.B \\-p, \\-\\-proxy <server:port>\n-Send request via proxy server. Needed for supporting others protocols\n-than HTTP.\n-.TP\n-.B \\-\\-get\n-Use GET request method.\n-.TP\n-.B \\-\\-head\n-Use HEAD request method.\n-.TP\n-.B \\-\\-options\n-Use OPTIONS request method.\n-.TP\n-.B \\-\\-trace\n-Use TRACE request method.\n-.TP\n-.B \\-c, \\-\\-clients <n>\n-Use\n-.I <n>\n-multiple clients for benchmark. Default value\n-is 1.\n-.SH \"EXIT STATUS\"\n-.TP\n-0 - sucess\n-.TP\n-1 - benchmark failed, can not connect to server\n-.TP\n-2 - bad command line argument(s)\n-.TP\n-3 - internal error, i.e. fork failed\n-.SH \"TODO\"\n-Include support for using\n-.I Keep-Alive\n-HTTP/1.1 connections.\n-.SH \"COPYING\"\n-Webbench is distributed under GPL. Copyright 1997-2004\n-Radim Kolar (hsn@netmag.cz). \n-UNIX sockets code taken from popclient 1.5 4/1/94 \n-public domain code, created by Virginia Tech Computing Center.\n-.BR\n-This man page is public domain.\n"
  },
  {
    "path": "WebBench/webbench.c",
    "content": "/*\n* (C) Radim Kolar 1997-2004\n* This is free software, see GNU Public License version 2 for\n* details.\n*\n* Simple forking WWW Server benchmark:\n*\n* Usage:\n*   webbench --help\n*\n* Return codes:\n*    0 - sucess\n*    1 - benchmark failed (server is not on-line)\n*    2 - bad param\n*    3 - internal error, fork failed\n* \n*/ \n\n#include \"socket.c\"\n#include <unistd.h>\n#include <sys/param.h>\n#include <rpc/types.h>\n#include <getopt.h>\n#include <strings.h>\n#include <time.h>\n#include <signal.h>\n#include <errno.h>\n#include <stdbool.h>\n/* values */\nvolatile int timerexpired=0;\nint speed=0;\nint failed=0;\nint bytes=0;\n\n/* globals */\nint http10=1; /* 0 - http/0.9, 1 - http/1.0, 2 - http/1.1 */\n/* Allow: GET, HEAD, OPTIONS, TRACE */\n#define METHOD_GET 0\n#define METHOD_HEAD 1\n#define METHOD_OPTIONS 2\n#define METHOD_TRACE 3\n#define PROGRAM_VERSION \"1.5\"\nint method=METHOD_GET;\nint clients=1;\nint force=0;\nint force_reload=0;\nint proxyport=80;\nchar *proxyhost=NULL;\nint benchtime=30;\n\nbool keep_alive = false;\n\n/* internal */\nint mypipe[2];\nchar host[MAXHOSTNAMELEN];\n#define REQUEST_SIZE 2048\nchar request[REQUEST_SIZE];\n\nstatic const struct option long_options[]=\n{\n    {\"force\",no_argument,&force,1},\n    {\"reload\",no_argument,&force_reload,1},\n    {\"time\",required_argument,NULL,'t'},\n    {\"help\",no_argument,NULL,'?'},\n    {\"http09\",no_argument,NULL,'9'},\n    {\"http10\",no_argument,NULL,'1'},\n    {\"http11\",no_argument,NULL,'2'},\n    {\"get\",no_argument,&method,METHOD_GET},\n    {\"head\",no_argument,&method,METHOD_HEAD},\n    {\"options\",no_argument,&method,METHOD_OPTIONS},\n    {\"trace\",no_argument,&method,METHOD_TRACE},\n    {\"version\",no_argument,NULL,'V'},\n    {\"proxy\",required_argument,NULL,'p'},\n    {\"clients\",required_argument,NULL,'c'},\n    {NULL,0,NULL,0}\n};\n\n/* prototypes */\nstatic void benchcore(const char* host,const int port, const char *request);\nstatic int bench(void);\nstatic void build_request(const char *url);\n\nstatic void alarm_handler(int signal)\n{\n    timerexpired=1;\n}\t\n\nstatic void usage(void)\n{\n    fprintf(stderr,\n            \"webbench [option]... URL\\n\"\n            \"  -f|--force               Don't wait for reply from server.\\n\"\n            \"  -r|--reload              Send reload request - Pragma: no-cache.\\n\"\n            \"  -t|--time <sec>          Run benchmark for <sec> seconds. Default 30.\\n\"\n            \"  -p|--proxy <server:port> Use proxy server for request.\\n\"\n            \"  -c|--clients <n>         Run <n> HTTP clients at once. Default one.\\n\"\n            \"  -k|--keep                Keep-Alive\\n\"\n            \"  -9|--http09              Use HTTP/0.9 style requests.\\n\"\n            \"  -1|--http10              Use HTTP/1.0 protocol.\\n\"\n            \"  -2|--http11              Use HTTP/1.1 protocol.\\n\"\n            \"  --get                    Use GET request method.\\n\"\n            \"  --head                   Use HEAD request method.\\n\"\n            \"  --options                Use OPTIONS request method.\\n\"\n            \"  --trace                  Use TRACE request method.\\n\"\n            \"  -?|-h|--help             This information.\\n\"\n            \"  -V|--version             Display program version.\\n\"\n           );\n}\n\nint main(int argc, char *argv[])\n{\n    int opt=0;\n    int options_index=0;\n    char *tmp=NULL;\n\n    if(argc==1)\n    {\n        usage();\n        return 2;\n    } \n\n    while((opt=getopt_long(argc,argv,\"912Vfrt:p:c:?hk\",long_options,&options_index))!=EOF )\n    {\n        switch(opt)\n        {\n            case  0 : break;\n            case 'f': force=1;break;\n            case 'r': force_reload=1;break; \n            case '9': http10=0;break;\n            case '1': http10=1;break;\n            case '2': http10=2;break;\n            case 'V': printf(PROGRAM_VERSION\"\\n\");exit(0);\n            case 't': benchtime=atoi(optarg);break;\t\n            case 'k': keep_alive = true;break;     \n            case 'p': \n            /* proxy server parsing server:port */\n            tmp=strrchr(optarg,':');\n            proxyhost=optarg;\n            if(tmp==NULL)\n            {\n                break;\n            }\n            if(tmp==optarg)\n            {\n                fprintf(stderr,\"Error in option --proxy %s: Missing hostname.\\n\",optarg);\n                return 2;\n            }\n            if(tmp==optarg+strlen(optarg)-1)\n            {\n                fprintf(stderr,\"Error in option --proxy %s Port number is missing.\\n\",optarg);\n                return 2;\n            }\n            *tmp='\\0';\n            proxyport=atoi(tmp+1);break;\n            case ':':\n            case 'h':\n            case '?': usage();return 2;break;\n            case 'c': clients=atoi(optarg);break;\n        }\n    }\n\n    if(optind==argc) {\n        fprintf(stderr,\"webbench: Missing URL!\\n\");\n        usage();\n        return 2;\n    }\n\n    if(clients==0) clients=1;\n    if(benchtime==0) benchtime=30;\n \n    /* Copyright */\n    fprintf(stderr,\"Webbench - Simple Web Benchmark \"PROGRAM_VERSION\"\\n\"\n            \"Copyright (c) Radim Kolar 1997-2004, GPL Open Source Software.\\n\"\n            );\n \n    build_request(argv[optind]);\n \n    // print request info ,do it in function build_request\n    /*printf(\"Benchmarking: \");\n \n    switch(method)\n    {\n        case METHOD_GET:\n        default:\n        printf(\"GET\");break;\n        case METHOD_OPTIONS:\n        printf(\"OPTIONS\");break;\n        case METHOD_HEAD:\n        printf(\"HEAD\");break;\n        case METHOD_TRACE:\n        printf(\"TRACE\");break;\n    }\n    \n    printf(\" %s\",argv[optind]);\n    \n    switch(http10)\n    {\n        case 0: printf(\" (using HTTP/0.9)\");break;\n        case 2: printf(\" (using HTTP/1.1)\");break;\n    }\n \n    printf(\"\\n\");\n    */\n\n    printf(\"Runing info: \");\n\n    if(clients==1) \n        printf(\"1 client\");\n    else\n        printf(\"%d clients\",clients);\n\n    printf(\", running %d sec\", benchtime);\n    \n    if(force) printf(\", early socket close\");\n    if(proxyhost!=NULL) printf(\", via proxy server %s:%d\",proxyhost,proxyport);\n    if(force_reload) printf(\", forcing reload\");\n    \n    printf(\".\\n\");\n    \n    return bench();\n}\n\nvoid build_request(const char *url)\n{\n    char tmp[10];\n    int i;\n\n    //bzero(host,MAXHOSTNAMELEN);\n    //bzero(request,REQUEST_SIZE);\n    memset(host,0,MAXHOSTNAMELEN);\n    memset(request,0,REQUEST_SIZE);\n\n    if(force_reload && proxyhost!=NULL && http10<1) http10=1;\n    if(method==METHOD_HEAD && http10<1) http10=1;\n    if(method==METHOD_OPTIONS && http10<2) http10=2;\n    if(method==METHOD_TRACE && http10<2) http10=2;\n\n    switch(method)\n    {\n        default:\n        case METHOD_GET: strcpy(request,\"GET\");break;\n        case METHOD_HEAD: strcpy(request,\"HEAD\");break;\n        case METHOD_OPTIONS: strcpy(request,\"OPTIONS\");break;\n        case METHOD_TRACE: strcpy(request,\"TRACE\");break;\n    }\n\n    strcat(request,\" \");\n\n    if(NULL==strstr(url,\"://\"))\n    {\n        fprintf(stderr, \"\\n%s: is not a valid URL.\\n\",url);\n        exit(2);\n    }\n    if(strlen(url)>1500)\n    {\n        fprintf(stderr,\"URL is too long.\\n\");\n        exit(2);\n    }\n    if (0!=strncasecmp(\"http://\",url,7)) \n    { \n        fprintf(stderr,\"\\nOnly HTTP protocol is directly supported, set --proxy for others.\\n\");\n        exit(2);\n    }\n    \n    /* protocol/host delimiter */\n    i=strstr(url,\"://\")-url+3;\n\n    if(strchr(url+i,'/')==NULL) {\n        fprintf(stderr,\"\\nInvalid URL syntax - hostname don't ends with '/'.\\n\");\n        exit(2);\n    }\n    \n    if(proxyhost==NULL)\n    {\n        /* get port from hostname */\n        if(index(url+i,':')!=NULL && index(url+i,':')<index(url+i,'/'))\n        {\n            strncpy(host,url+i,strchr(url+i,':')-url-i);\n            //bzero(tmp,10);\n            memset(tmp,0,10);\n            strncpy(tmp,index(url+i,':')+1,strchr(url+i,'/')-index(url+i,':')-1);\n            /* printf(\"tmp=%s\\n\",tmp); */\n            proxyport=atoi(tmp);\n            if(proxyport==0) proxyport=80;\n        } \n        else\n        {\n            strncpy(host,url+i,strcspn(url+i,\"/\"));\n        }\n        // printf(\"Host=%s\\n\",host);\n        strcat(request+strlen(request),url+i+strcspn(url+i,\"/\"));\n    } \n    else\n    {\n        // printf(\"ProxyHost=%s\\nProxyPort=%d\\n\",proxyhost,proxyport);\n        strcat(request,url);\n    }\n\n    if(http10==1)\n        strcat(request,\" HTTP/1.0\");\n    else if (http10==2)\n        strcat(request,\" HTTP/1.1\");\n  \n    strcat(request,\"\\r\\n\");\n  \n    if(http10>0)\n        strcat(request,\"User-Agent: WebBench \"PROGRAM_VERSION\"\\r\\n\");\n    if(proxyhost==NULL && http10>0)\n    {\n        strcat(request,\"Host: \");\n        strcat(request,host);\n        strcat(request,\"\\r\\n\");\n    }\n \n    if(force_reload && proxyhost!=NULL)\n    {\n        strcat(request,\"Pragma: no-cache\\r\\n\");\n    }\n  \n    if(http10>1)\n    {\n        if (!keep_alive)\n            strcat(request,\"Connection: close\\r\\n\");\n        else\n            strcat(request,\"Connection: Keep-Alive\\r\\n\");\n    }\n        \n    \n    /* add empty line at end */\n    if(http10>0) strcat(request,\"\\r\\n\"); \n    \n    printf(\"\\nRequest:\\n%s\\n\",request);\n}\n\n/* vraci system rc error kod */\nstatic int bench(void)\n{\n    int i,j,k;\t\n    pid_t pid=0;\n    FILE *f;\n\n    /* check avaibility of target server */\n    i=Socket(proxyhost==NULL?host:proxyhost,proxyport);\n    if(i<0) { \n        fprintf(stderr,\"\\nConnect to server failed. Aborting benchmark.\\n\");\n        return 1;\n    }\n    close(i);\n    \n    /* create pipe */\n    if(pipe(mypipe))\n    {\n        perror(\"pipe failed.\");\n        return 3;\n    }\n\n    /* not needed, since we have alarm() in childrens */\n    /* wait 4 next system clock tick */\n    /*\n    cas=time(NULL);\n    while(time(NULL)==cas)\n    sched_yield();\n    */\n\n    /* fork childs */\n    for(i=0;i<clients;i++)\n    {\n        pid=fork();\n        if(pid <= (pid_t) 0)\n        {\n                // if (errno == 11 && pid != 0)\n                // {\n                //     printf(\"here i = %d\\n\", i);\n                //     while (fork() < 0);\n                //     continue;\n                // }\n            /* child process or error*/\n            sleep(1); /* make childs faster */\n            break;\n        }\n    }\n\n    if( pid < (pid_t) 0)\n    {\n        fprintf(stderr,\"problems forking worker no. %d\\n\",i);\n        perror(\"fork failed.\");\n        return 3;\n    }\n\n    if(pid == (pid_t) 0)\n    {\n        /* I am a child */\n        if(proxyhost==NULL)\n            benchcore(host,proxyport,request);\n        else\n            benchcore(proxyhost,proxyport,request);\n\n        /* write results to pipe */\n        f=fdopen(mypipe[1],\"w\");\n        if(f==NULL)\n        {\n            perror(\"open pipe for writing failed.\");\n            return 3;\n        }\n        /* fprintf(stderr,\"Child - %d %d\\n\",speed,failed); */\n        fprintf(f,\"%d %d %d\\n\",speed,failed,bytes);\n        fclose(f);\n\n        return 0;\n    } \n    else\n    {\n        f=fdopen(mypipe[0],\"r\");\n        if(f==NULL) \n        {\n            perror(\"open pipe for reading failed.\");\n            return 3;\n        }\n        \n        setvbuf(f,NULL,_IONBF,0);\n        \n        speed=0;\n        failed=0;\n        bytes=0;\n    \n        while(1)\n        {\n            pid=fscanf(f,\"%d %d %d\",&i,&j,&k);\n            if(pid<2)\n            {\n                fprintf(stderr,\"Some of our childrens died.\\n\");\n                break;\n            }\n            \n            speed+=i;\n            failed+=j;\n            bytes+=k;\n        \n            /* fprintf(stderr,\"*Knock* %d %d read=%d\\n\",speed,failed,pid); */\n            if(--clients==0) break;\n        }\n    \n        fclose(f);\n\n        printf(\"\\nSpeed=%d pages/min, %d bytes/sec.\\nRequests: %d susceed, %d failed.\\n\",\n            (int)((speed+failed)/(benchtime/60.0f)),\n            (int)(bytes/(float)benchtime),\n            speed,\n            failed);\n    }\n    \n    return i;\n}\n\nvoid benchcore(const char *host,const int port,const char *req)\n{\n    int rlen;\n    char buf[1500];\n    int s,i;\n    struct sigaction sa;\n\n    /* setup alarm signal handler */\n    sa.sa_handler=alarm_handler;\n    sa.sa_flags=0;\n    if(sigaction(SIGALRM,&sa,NULL))\n        exit(3);\n    \n    alarm(benchtime); // after benchtime,then exit\n\n    rlen=strlen(req);\n\n    //printf(\"keep_alive\\n\");\n\n\n    if (keep_alive)\n    {\n        while ((s = Socket(host,port)) == -1);  \n        nexttry1:while(1)\n        {\n            if(timerexpired)\n            {\n                if(failed>0)\n                {\n                    /* fprintf(stderr,\"Correcting failed by signal\\n\"); */\n                    failed--;\n                }\n                return;\n            }\n           \n            if(s<0) { failed++;continue;} \n            if(rlen!=write(s,req,rlen)) {\n                failed++;\n                close(s);\n                while ((s = Socket(host,port)) == -1);\n                continue;\n            }\n            if(force==0) \n            {\n                /* read all available data from socket */\n                while(1)\n                {\n                    if(timerexpired) break; \n                    i=read(s,buf,1500);\n                    /* fprintf(stderr,\"%d\\n\",i); */\n                    if(i<0) \n                    { \n                        failed++;\n                        close(s);\n                        //while ((s = Socket(host,port)) == -1);\n                        goto nexttry1;\n                    }\n                    else\n                    if(i==0) break;\n                    else\n                    bytes+=i;\n                    // Supposed reveived bytes were less than 1500\n                    //if (i < 1500) \n                        break;\n                }\n            }\n            speed++;\n        }\n    }\n    else \n    {\n        nexttry:while(1)\n        {\n            if(timerexpired)\n            {\n                if(failed>0)\n                {\n                    /* fprintf(stderr,\"Correcting failed by signal\\n\"); */\n                    failed--;\n                }\n                return;\n            }\n            s=Socket(host,port);        \n            if(s<0) { failed++;continue;} \n            if(rlen!=write(s,req,rlen)) {failed++;close(s);continue;}\n            if(http10==0) \n            if(shutdown(s,1)) { failed++;close(s);continue;}\n            if(force==0) \n            {\n                /* read all available data from socket */\n                while(1)\n                {\n                    if(timerexpired) break; \n                    i=read(s,buf,1500);\n                    if(i<0) \n                    { \n                        failed++;\n                        close(s);\n                        goto nexttry;\n                    }\n                    else\n                    if(i==0) break;\n                    else\n                    bytes+=i;\n                }\n            }\n            if(close(s)) {failed++; continue;}\n            speed++;\n        }\n    }\n        \n    \n}"
  },
  {
    "path": "WebServer/CMakeLists.txt",
    "content": "set(SRCS\n    Channel.cpp\n    Epoll.cpp\n    EventLoop.cpp\n    EventLoopThread.cpp\n    EventLoopThreadPool.cpp\n    HttpData.cpp\n    Main.cpp\n    Server.cpp\n    #ThreadPool.cpp\n    Timer.cpp\n    Util.cpp\n)\ninclude_directories(${PROJECT_SOURCE_DIR}/base)\n\n\nadd_executable(WebServer ${SRCS})\ntarget_link_libraries(WebServer libserver_base)\n\n\nadd_subdirectory(base)\nadd_subdirectory(tests)"
  },
  {
    "path": "WebServer/Channel.cpp",
    "content": "// @Author Lin Ya\r\n// @Email xxbbb@vip.qq.com\r\n#include \"Channel.h\"\r\n\r\n#include <unistd.h>\r\n#include <cstdlib>\r\n#include <iostream>\r\n\r\n#include <queue>\r\n\r\n#include \"Epoll.h\"\r\n#include \"EventLoop.h\"\r\n#include \"Util.h\"\r\n\r\nusing namespace std;\r\n\r\nChannel::Channel(EventLoop *loop)\r\n    : loop_(loop), events_(0), lastEvents_(0), fd_(0) {}\r\n\r\nChannel::Channel(EventLoop *loop, int fd)\r\n    : loop_(loop), fd_(fd), events_(0), lastEvents_(0) {}\r\n\r\nChannel::~Channel() {\r\n  // loop_->poller_->epoll_del(fd, events_);\r\n  // close(fd_);\r\n}\r\n\r\nint Channel::getFd() { return fd_; }\r\nvoid Channel::setFd(int fd) { fd_ = fd; }\r\n\r\nvoid Channel::handleRead() {\r\n  if (readHandler_) {\r\n    readHandler_();\r\n  }\r\n}\r\n\r\nvoid Channel::handleWrite() {\r\n  if (writeHandler_) {\r\n    writeHandler_();\r\n  }\r\n}\r\n\r\nvoid Channel::handleConn() {\r\n  if (connHandler_) {\r\n    connHandler_();\r\n  }\r\n}"
  },
  {
    "path": "WebServer/Channel.h",
    "content": "// @Author Lin Ya\r\n// @Email xxbbb@vip.qq.com\r\n#pragma once\r\n#include <sys/epoll.h>\r\n#include <sys/epoll.h>\r\n#include <functional>\r\n#include <memory>\r\n#include <string>\r\n#include <unordered_map>\r\n#include \"Timer.h\"\r\n\r\nclass EventLoop;\r\nclass HttpData;\r\n\r\nclass Channel {\r\n private:\r\n  typedef std::function<void()> CallBack;\r\n  EventLoop *loop_;\r\n  int fd_;\r\n  __uint32_t events_;\r\n  __uint32_t revents_;\r\n  __uint32_t lastEvents_;\r\n\r\n  // 方便找到上层持有该Channel的对象\r\n  std::weak_ptr<HttpData> holder_;\r\n\r\n private:\r\n  int parse_URI();\r\n  int parse_Headers();\r\n  int analysisRequest();\r\n\r\n  CallBack readHandler_;\r\n  CallBack writeHandler_;\r\n  CallBack errorHandler_;\r\n  CallBack connHandler_;\r\n\r\n public:\r\n  Channel(EventLoop *loop);\r\n  Channel(EventLoop *loop, int fd);\r\n  ~Channel();\r\n  int getFd();\r\n  void setFd(int fd);\r\n\r\n  void setHolder(std::shared_ptr<HttpData> holder) { holder_ = holder; }\r\n  std::shared_ptr<HttpData> getHolder() {\r\n    std::shared_ptr<HttpData> ret(holder_.lock());\r\n    return ret;\r\n  }\r\n\r\n  void setReadHandler(CallBack &&readHandler) { readHandler_ = readHandler; }\r\n  void setWriteHandler(CallBack &&writeHandler) {\r\n    writeHandler_ = writeHandler;\r\n  }\r\n  void setErrorHandler(CallBack &&errorHandler) {\r\n    errorHandler_ = errorHandler;\r\n  }\r\n  void setConnHandler(CallBack &&connHandler) { connHandler_ = connHandler; }\r\n\r\n  void handleEvents() {\r\n    events_ = 0;\r\n    if ((revents_ & EPOLLHUP) && !(revents_ & EPOLLIN)) {\r\n      events_ = 0;\r\n      return;\r\n    }\r\n    if (revents_ & EPOLLERR) {\r\n      if (errorHandler_) errorHandler_();\r\n      events_ = 0;\r\n      return;\r\n    }\r\n    if (revents_ & (EPOLLIN | EPOLLPRI | EPOLLRDHUP)) {\r\n      handleRead();\r\n    }\r\n    if (revents_ & EPOLLOUT) {\r\n      handleWrite();\r\n    }\r\n    handleConn();\r\n  }\r\n  void handleRead();\r\n  void handleWrite();\r\n  void handleError(int fd, int err_num, std::string short_msg);\r\n  void handleConn();\r\n\r\n  void setRevents(__uint32_t ev) { revents_ = ev; }\r\n\r\n  void setEvents(__uint32_t ev) { events_ = ev; }\r\n  __uint32_t &getEvents() { return events_; }\r\n\r\n  bool EqualAndUpdateLastEvents() {\r\n    bool ret = (lastEvents_ == events_);\r\n    lastEvents_ = events_;\r\n    return ret;\r\n  }\r\n\r\n  __uint32_t getLastEvents() { return lastEvents_; }\r\n};\r\n\r\ntypedef std::shared_ptr<Channel> SP_Channel;"
  },
  {
    "path": "WebServer/Epoll.cpp",
    "content": "// @Author Lin Ya\r\n// @Email xxbbb@vip.qq.com\r\n#include \"Epoll.h\"\r\n#include <assert.h>\r\n#include <errno.h>\r\n#include <netinet/in.h>\r\n#include <string.h>\r\n#include <sys/epoll.h>\r\n#include <sys/socket.h>\r\n#include <deque>\r\n#include <queue>\r\n#include \"Util.h\"\r\n#include \"base/Logging.h\"\r\n\r\n\r\n#include <arpa/inet.h>\r\n#include <iostream>\r\nusing namespace std;\r\n\r\nconst int EVENTSNUM = 4096;\r\nconst int EPOLLWAIT_TIME = 10000;\r\n\r\ntypedef shared_ptr<Channel> SP_Channel;\r\n\r\nEpoll::Epoll() : epollFd_(epoll_create1(EPOLL_CLOEXEC)), events_(EVENTSNUM) {\r\n  assert(epollFd_ > 0);\r\n}\r\nEpoll::~Epoll() {}\r\n\r\n// 注册新描述符\r\nvoid Epoll::epoll_add(SP_Channel request, int timeout) {\r\n  int fd = request->getFd();\r\n  if (timeout > 0) {\r\n    add_timer(request, timeout);\r\n    fd2http_[fd] = request->getHolder();\r\n  }\r\n  struct epoll_event event;\r\n  event.data.fd = fd;\r\n  event.events = request->getEvents();\r\n\r\n  request->EqualAndUpdateLastEvents();\r\n\r\n  fd2chan_[fd] = request;\r\n  if (epoll_ctl(epollFd_, EPOLL_CTL_ADD, fd, &event) < 0) {\r\n    perror(\"epoll_add error\");\r\n    fd2chan_[fd].reset();\r\n  }\r\n}\r\n\r\n// 修改描述符状态\r\nvoid Epoll::epoll_mod(SP_Channel request, int timeout) {\r\n  if (timeout > 0) add_timer(request, timeout);\r\n  int fd = request->getFd();\r\n  if (!request->EqualAndUpdateLastEvents()) {\r\n    struct epoll_event event;\r\n    event.data.fd = fd;\r\n    event.events = request->getEvents();\r\n    if (epoll_ctl(epollFd_, EPOLL_CTL_MOD, fd, &event) < 0) {\r\n      perror(\"epoll_mod error\");\r\n      fd2chan_[fd].reset();\r\n    }\r\n  }\r\n}\r\n\r\n// 从epoll中删除描述符\r\nvoid Epoll::epoll_del(SP_Channel request) {\r\n  int fd = request->getFd();\r\n  struct epoll_event event;\r\n  event.data.fd = fd;\r\n  event.events = request->getLastEvents();\r\n  // event.events = 0;\r\n  // request->EqualAndUpdateLastEvents()\r\n  if (epoll_ctl(epollFd_, EPOLL_CTL_DEL, fd, &event) < 0) {\r\n    perror(\"epoll_del error\");\r\n  }\r\n  fd2chan_[fd].reset();\r\n  fd2http_[fd].reset();\r\n}\r\n\r\n// 返回活跃事件数\r\nstd::vector<SP_Channel> Epoll::poll() {\r\n  while (true) {\r\n    int event_count =\r\n        epoll_wait(epollFd_, &*events_.begin(), events_.size(), EPOLLWAIT_TIME);\r\n    if (event_count < 0) perror(\"epoll wait error\");\r\n    std::vector<SP_Channel> req_data = getEventsRequest(event_count);\r\n    if (req_data.size() > 0) return req_data;\r\n  }\r\n}\r\n\r\nvoid Epoll::handleExpired() { timerManager_.handleExpiredEvent(); }\r\n\r\n// 分发处理函数\r\nstd::vector<SP_Channel> Epoll::getEventsRequest(int events_num) {\r\n  std::vector<SP_Channel> req_data;\r\n  for (int i = 0; i < events_num; ++i) {\r\n    // 获取有事件产生的描述符\r\n    int fd = events_[i].data.fd;\r\n\r\n    SP_Channel cur_req = fd2chan_[fd];\r\n\r\n    if (cur_req) {\r\n      cur_req->setRevents(events_[i].events);\r\n      cur_req->setEvents(0);\r\n      // 加入线程池之前将Timer和request分离\r\n      // cur_req->seperateTimer();\r\n      req_data.push_back(cur_req);\r\n    } else {\r\n      LOG << \"SP cur_req is invalid\";\r\n    }\r\n  }\r\n  return req_data;\r\n}\r\n\r\nvoid Epoll::add_timer(SP_Channel request_data, int timeout) {\r\n  shared_ptr<HttpData> t = request_data->getHolder();\r\n  if (t)\r\n    timerManager_.addTimer(t, timeout);\r\n  else\r\n    LOG << \"timer add fail\";\r\n}"
  },
  {
    "path": "WebServer/Epoll.h",
    "content": "// @Author Lin Ya\r\n// @Email xxbbb@vip.qq.com\r\n#pragma once\r\n#include <sys/epoll.h>\r\n#include <memory>\r\n#include <unordered_map>\r\n#include <vector>\r\n#include \"Channel.h\"\r\n#include \"HttpData.h\"\r\n#include \"Timer.h\"\r\n\r\n\r\nclass Epoll {\r\n public:\r\n  Epoll();\r\n  ~Epoll();\r\n  void epoll_add(SP_Channel request, int timeout);\r\n  void epoll_mod(SP_Channel request, int timeout);\r\n  void epoll_del(SP_Channel request);\r\n  std::vector<std::shared_ptr<Channel>> poll();\r\n  std::vector<std::shared_ptr<Channel>> getEventsRequest(int events_num);\r\n  void add_timer(std::shared_ptr<Channel> request_data, int timeout);\r\n  int getEpollFd() { return epollFd_; }\r\n  void handleExpired();\r\n\r\n private:\r\n  static const int MAXFDS = 100000;\r\n  int epollFd_;\r\n  std::vector<epoll_event> events_;\r\n  std::shared_ptr<Channel> fd2chan_[MAXFDS];\r\n  std::shared_ptr<HttpData> fd2http_[MAXFDS];\r\n  TimerManager timerManager_;\r\n};"
  },
  {
    "path": "WebServer/EventLoop.cpp",
    "content": "// @Author Lin Ya\r\n// @Email xxbbb@vip.qq.com\r\n#include \"EventLoop.h\"\r\n#include <sys/epoll.h>\r\n#include <sys/eventfd.h>\r\n#include <iostream>\r\n#include \"Util.h\"\r\n#include \"base/Logging.h\"\r\n\r\nusing namespace std;\r\n\r\n__thread EventLoop* t_loopInThisThread = 0;\r\n\r\nint createEventfd() {\r\n  int evtfd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);\r\n  if (evtfd < 0) {\r\n    LOG << \"Failed in eventfd\";\r\n    abort();\r\n  }\r\n  return evtfd;\r\n}\r\n\r\nEventLoop::EventLoop()\r\n    : looping_(false),\r\n      poller_(new Epoll()),\r\n      wakeupFd_(createEventfd()),\r\n      quit_(false),\r\n      eventHandling_(false),\r\n      callingPendingFunctors_(false),\r\n      threadId_(CurrentThread::tid()),\r\n      pwakeupChannel_(new Channel(this, wakeupFd_)) {\r\n  if (t_loopInThisThread) {\r\n    // LOG << \"Another EventLoop \" << t_loopInThisThread << \" exists in this\r\n    // thread \" << threadId_;\r\n  } else {\r\n    t_loopInThisThread = this;\r\n  }\r\n  // pwakeupChannel_->setEvents(EPOLLIN | EPOLLET | EPOLLONESHOT);\r\n  pwakeupChannel_->setEvents(EPOLLIN | EPOLLET);\r\n  pwakeupChannel_->setReadHandler(bind(&EventLoop::handleRead, this));\r\n  pwakeupChannel_->setConnHandler(bind(&EventLoop::handleConn, this));\r\n  poller_->epoll_add(pwakeupChannel_, 0);\r\n}\r\n\r\nvoid EventLoop::handleConn() {\r\n  // poller_->epoll_mod(wakeupFd_, pwakeupChannel_, (EPOLLIN | EPOLLET |\r\n  // EPOLLONESHOT), 0);\r\n  updatePoller(pwakeupChannel_, 0);\r\n}\r\n\r\nEventLoop::~EventLoop() {\r\n  // wakeupChannel_->disableAll();\r\n  // wakeupChannel_->remove();\r\n  close(wakeupFd_);\r\n  t_loopInThisThread = NULL;\r\n}\r\n\r\nvoid EventLoop::wakeup() {\r\n  uint64_t one = 1;\r\n  ssize_t n = writen(wakeupFd_, (char*)(&one), sizeof one);\r\n  if (n != sizeof one) {\r\n    LOG << \"EventLoop::wakeup() writes \" << n << \" bytes instead of 8\";\r\n  }\r\n}\r\n\r\nvoid EventLoop::handleRead() {\r\n  uint64_t one = 1;\r\n  ssize_t n = readn(wakeupFd_, &one, sizeof one);\r\n  if (n != sizeof one) {\r\n    LOG << \"EventLoop::handleRead() reads \" << n << \" bytes instead of 8\";\r\n  }\r\n  // pwakeupChannel_->setEvents(EPOLLIN | EPOLLET | EPOLLONESHOT);\r\n  pwakeupChannel_->setEvents(EPOLLIN | EPOLLET);\r\n}\r\n\r\nvoid EventLoop::runInLoop(Functor&& cb) {\r\n  if (isInLoopThread())\r\n    cb();\r\n  else\r\n    queueInLoop(std::move(cb));\r\n}\r\n\r\nvoid EventLoop::queueInLoop(Functor&& cb) {\r\n  {\r\n    MutexLockGuard lock(mutex_);\r\n    pendingFunctors_.emplace_back(std::move(cb));\r\n  }\r\n\r\n  if (!isInLoopThread() || callingPendingFunctors_) wakeup();\r\n}\r\n\r\nvoid EventLoop::loop() {\r\n  assert(!looping_);\r\n  assert(isInLoopThread());\r\n  looping_ = true;\r\n  quit_ = false;\r\n  // LOG_TRACE << \"EventLoop \" << this << \" start looping\";\r\n  std::vector<SP_Channel> ret;\r\n  while (!quit_) {\r\n    // cout << \"doing\" << endl;\r\n    ret.clear();\r\n    ret = poller_->poll();\r\n    eventHandling_ = true;\r\n    for (auto& it : ret) it->handleEvents();\r\n    eventHandling_ = false;\r\n    doPendingFunctors();\r\n    poller_->handleExpired();\r\n  }\r\n  looping_ = false;\r\n}\r\n\r\nvoid EventLoop::doPendingFunctors() {\r\n  std::vector<Functor> functors;\r\n  callingPendingFunctors_ = true;\r\n\r\n  {\r\n    MutexLockGuard lock(mutex_);\r\n    functors.swap(pendingFunctors_);\r\n  }\r\n\r\n  for (size_t i = 0; i < functors.size(); ++i) functors[i]();\r\n  callingPendingFunctors_ = false;\r\n}\r\n\r\nvoid EventLoop::quit() {\r\n  quit_ = true;\r\n  if (!isInLoopThread()) {\r\n    wakeup();\r\n  }\r\n}"
  },
  {
    "path": "WebServer/EventLoop.h",
    "content": "// @Author Lin Ya\r\n// @Email xxbbb@vip.qq.com\r\n#pragma once\r\n#include <functional>\r\n#include <memory>\r\n#include <vector>\r\n#include \"Channel.h\"\r\n#include \"Epoll.h\"\r\n#include \"Util.h\"\r\n#include \"base/CurrentThread.h\"\r\n#include \"base/Logging.h\"\r\n#include \"base/Thread.h\"\r\n\r\n\r\n#include <iostream>\r\nusing namespace std;\r\n\r\nclass EventLoop {\r\n public:\r\n  typedef std::function<void()> Functor;\r\n  EventLoop();\r\n  ~EventLoop();\r\n  void loop();\r\n  void quit();\r\n  void runInLoop(Functor&& cb);\r\n  void queueInLoop(Functor&& cb);\r\n  bool isInLoopThread() const { return threadId_ == CurrentThread::tid(); }\r\n  void assertInLoopThread() { assert(isInLoopThread()); }\r\n  void shutdown(shared_ptr<Channel> channel) { shutDownWR(channel->getFd()); }\r\n  void removeFromPoller(shared_ptr<Channel> channel) {\r\n    // shutDownWR(channel->getFd());\r\n    poller_->epoll_del(channel);\r\n  }\r\n  void updatePoller(shared_ptr<Channel> channel, int timeout = 0) {\r\n    poller_->epoll_mod(channel, timeout);\r\n  }\r\n  void addToPoller(shared_ptr<Channel> channel, int timeout = 0) {\r\n    poller_->epoll_add(channel, timeout);\r\n  }\r\n\r\n private:\r\n  // 声明顺序 wakeupFd_ > pwakeupChannel_\r\n  bool looping_;\r\n  shared_ptr<Epoll> poller_;\r\n  int wakeupFd_;\r\n  bool quit_;\r\n  bool eventHandling_;\r\n  mutable MutexLock mutex_;\r\n  std::vector<Functor> pendingFunctors_;\r\n  bool callingPendingFunctors_;\r\n  const pid_t threadId_;\r\n  shared_ptr<Channel> pwakeupChannel_;\r\n\r\n  void wakeup();\r\n  void handleRead();\r\n  void doPendingFunctors();\r\n  void handleConn();\r\n};\r\n"
  },
  {
    "path": "WebServer/EventLoopThread.cpp",
    "content": "// @Author Lin Ya\r\n// @Email xxbbb@vip.qq.com\r\n#include \"EventLoopThread.h\"\r\n#include <functional>\r\n\r\nEventLoopThread::EventLoopThread()\r\n    : loop_(NULL),\r\n      exiting_(false),\r\n      thread_(bind(&EventLoopThread::threadFunc, this), \"EventLoopThread\"),\r\n      mutex_(),\r\n      cond_(mutex_) {}\r\n\r\nEventLoopThread::~EventLoopThread() {\r\n  exiting_ = true;\r\n  if (loop_ != NULL) {\r\n    loop_->quit();\r\n    thread_.join();\r\n  }\r\n}\r\n\r\nEventLoop* EventLoopThread::startLoop() {\r\n  assert(!thread_.started());\r\n  thread_.start();\r\n  {\r\n    MutexLockGuard lock(mutex_);\r\n    // 一直等到threadFun在Thread里真正跑起来\r\n    while (loop_ == NULL) cond_.wait();\r\n  }\r\n  return loop_;\r\n}\r\n\r\nvoid EventLoopThread::threadFunc() {\r\n  EventLoop loop;\r\n\r\n  {\r\n    MutexLockGuard lock(mutex_);\r\n    loop_ = &loop;\r\n    cond_.notify();\r\n  }\r\n\r\n  loop.loop();\r\n  // assert(exiting_);\r\n  loop_ = NULL;\r\n}"
  },
  {
    "path": "WebServer/EventLoopThread.h",
    "content": "// @Author Lin Ya\r\n// @Email xxbbb@vip.qq.com\r\n#pragma once\r\n#include \"EventLoop.h\"\r\n#include \"base/Condition.h\"\r\n#include \"base/MutexLock.h\"\r\n#include \"base/Thread.h\"\r\n#include \"base/noncopyable.h\"\r\n\r\n\r\nclass EventLoopThread : noncopyable {\r\n public:\r\n  EventLoopThread();\r\n  ~EventLoopThread();\r\n  EventLoop* startLoop();\r\n\r\n private:\r\n  void threadFunc();\r\n  EventLoop* loop_;\r\n  bool exiting_;\r\n  Thread thread_;\r\n  MutexLock mutex_;\r\n  Condition cond_;\r\n};"
  },
  {
    "path": "WebServer/EventLoopThreadPool.cpp",
    "content": "// @Author Lin Ya\r\n// @Email xxbbb@vip.qq.com\r\n#include \"EventLoopThreadPool.h\"\r\n\r\nEventLoopThreadPool::EventLoopThreadPool(EventLoop *baseLoop, int numThreads)\r\n    : baseLoop_(baseLoop), started_(false), numThreads_(numThreads), next_(0) {\r\n  if (numThreads_ <= 0) {\r\n    LOG << \"numThreads_ <= 0\";\r\n    abort();\r\n  }\r\n}\r\n\r\nvoid EventLoopThreadPool::start() {\r\n  baseLoop_->assertInLoopThread();\r\n  started_ = true;\r\n  for (int i = 0; i < numThreads_; ++i) {\r\n    std::shared_ptr<EventLoopThread> t(new EventLoopThread());\r\n    threads_.push_back(t);\r\n    loops_.push_back(t->startLoop());\r\n  }\r\n}\r\n\r\nEventLoop *EventLoopThreadPool::getNextLoop() {\r\n  baseLoop_->assertInLoopThread();\r\n  assert(started_);\r\n  EventLoop *loop = baseLoop_;\r\n  if (!loops_.empty()) {\r\n    loop = loops_[next_];\r\n    next_ = (next_ + 1) % numThreads_;\r\n  }\r\n  return loop;\r\n}"
  },
  {
    "path": "WebServer/EventLoopThreadPool.h",
    "content": "// @Author Lin Ya\r\n// @Email xxbbb@vip.qq.com\r\n#pragma once\r\n#include <memory>\r\n#include <vector>\r\n#include \"EventLoopThread.h\"\r\n#include \"base/Logging.h\"\r\n#include \"base/noncopyable.h\"\r\n\r\n\r\nclass EventLoopThreadPool : noncopyable {\r\n public:\r\n  EventLoopThreadPool(EventLoop* baseLoop, int numThreads);\r\n\r\n  ~EventLoopThreadPool() { LOG << \"~EventLoopThreadPool()\"; }\r\n  void start();\r\n\r\n  EventLoop* getNextLoop();\r\n\r\n private:\r\n  EventLoop* baseLoop_;\r\n  bool started_;\r\n  int numThreads_;\r\n  int next_;\r\n  std::vector<std::shared_ptr<EventLoopThread>> threads_;\r\n  std::vector<EventLoop*> loops_;\r\n};"
  },
  {
    "path": "WebServer/HttpData.cpp",
    "content": "// @Author Lin Ya\r\n// @Email xxbbb@vip.qq.com\r\n#include \"HttpData.h\"\r\n#include <fcntl.h>\r\n#include <sys/mman.h>\r\n#include <sys/stat.h>\r\n#include <iostream>\r\n#include \"Channel.h\"\r\n#include \"EventLoop.h\"\r\n#include \"Util.h\"\r\n#include \"time.h\"\r\n\r\nusing namespace std;\r\n\r\npthread_once_t MimeType::once_control = PTHREAD_ONCE_INIT;\r\nstd::unordered_map<std::string, std::string> MimeType::mime;\r\n\r\nconst __uint32_t DEFAULT_EVENT = EPOLLIN | EPOLLET | EPOLLONESHOT;\r\nconst int DEFAULT_EXPIRED_TIME = 2000;              // ms\r\nconst int DEFAULT_KEEP_ALIVE_TIME = 5 * 60 * 1000;  // ms\r\n\r\nchar favicon[555] = {\r\n    '\\x89', 'P',    'N',    'G',    '\\xD',  '\\xA',  '\\x1A', '\\xA',  '\\x0',\r\n    '\\x0',  '\\x0',  '\\xD',  'I',    'H',    'D',    'R',    '\\x0',  '\\x0',\r\n    '\\x0',  '\\x10', '\\x0',  '\\x0',  '\\x0',  '\\x10', '\\x8',  '\\x6',  '\\x0',\r\n    '\\x0',  '\\x0',  '\\x1F', '\\xF3', '\\xFF', 'a',    '\\x0',  '\\x0',  '\\x0',\r\n    '\\x19', 't',    'E',    'X',    't',    'S',    'o',    'f',    't',\r\n    'w',    'a',    'r',    'e',    '\\x0',  'A',    'd',    'o',    'b',\r\n    'e',    '\\x20', 'I',    'm',    'a',    'g',    'e',    'R',    'e',\r\n    'a',    'd',    'y',    'q',    '\\xC9', 'e',    '\\x3C', '\\x0',  '\\x0',\r\n    '\\x1',  '\\xCD', 'I',    'D',    'A',    'T',    'x',    '\\xDA', '\\x94',\r\n    '\\x93', '9',    'H',    '\\x3',  'A',    '\\x14', '\\x86', '\\xFF', '\\x5D',\r\n    'b',    '\\xA7', '\\x4',  'R',    '\\xC4', 'm',    '\\x22', '\\x1E', '\\xA0',\r\n    'F',    '\\x24', '\\x8',  '\\x16', '\\x16', 'v',    '\\xA',  '6',    '\\xBA',\r\n    'J',    '\\x9A', '\\x80', '\\x8',  'A',    '\\xB4', 'q',    '\\x85', 'X',\r\n    '\\x89', 'G',    '\\xB0', 'I',    '\\xA9', 'Q',    '\\x24', '\\xCD', '\\xA6',\r\n    '\\x8',  '\\xA4', 'H',    'c',    '\\x91', 'B',    '\\xB',  '\\xAF', 'V',\r\n    '\\xC1', 'F',    '\\xB4', '\\x15', '\\xCF', '\\x22', 'X',    '\\x98', '\\xB',\r\n    'T',    'H',    '\\x8A', 'd',    '\\x93', '\\x8D', '\\xFB', 'F',    'g',\r\n    '\\xC9', '\\x1A', '\\x14', '\\x7D', '\\xF0', 'f',    'v',    'f',    '\\xDF',\r\n    '\\x7C', '\\xEF', '\\xE7', 'g',    'F',    '\\xA8', '\\xD5', 'j',    'H',\r\n    '\\x24', '\\x12', '\\x2A', '\\x0',  '\\x5',  '\\xBF', 'G',    '\\xD4', '\\xEF',\r\n    '\\xF7', '\\x2F', '6',    '\\xEC', '\\x12', '\\x20', '\\x1E', '\\x8F', '\\xD7',\r\n    '\\xAA', '\\xD5', '\\xEA', '\\xAF', 'I',    '5',    'F',    '\\xAA', 'T',\r\n    '\\x5F', '\\x9F', '\\x22', 'A',    '\\x2A', '\\x95', '\\xA',  '\\x83', '\\xE5',\r\n    'r',    '9',    'd',    '\\xB3', 'Y',    '\\x96', '\\x99', 'L',    '\\x6',\r\n    '\\xE9', 't',    '\\x9A', '\\x25', '\\x85', '\\x2C', '\\xCB', 'T',    '\\xA7',\r\n    '\\xC4', 'b',    '1',    '\\xB5', '\\x5E', '\\x0',  '\\x3',  'h',    '\\x9A',\r\n    '\\xC6', '\\x16', '\\x82', '\\x20', 'X',    'R',    '\\x14', 'E',    '6',\r\n    'S',    '\\x94', '\\xCB', 'e',    'x',    '\\xBD', '\\x5E', '\\xAA', 'U',\r\n    'T',    '\\x23', 'L',    '\\xC0', '\\xE0', '\\xE2', '\\xC1', '\\x8F', '\\x0',\r\n    '\\x9E', '\\xBC', '\\x9',  'A',    '\\x7C', '\\x3E', '\\x1F', '\\x83', 'D',\r\n    '\\x22', '\\x11', '\\xD5', 'T',    '\\x40', '\\x3F', '8',    '\\x80', 'w',\r\n    '\\xE5', '3',    '\\x7',  '\\xB8', '\\x5C', '\\x2E', 'H',    '\\x92', '\\x4',\r\n    '\\x87', '\\xC3', '\\x81', '\\x40', '\\x20', '\\x40', 'g',    '\\x98', '\\xE9',\r\n    '6',    '\\x1A', '\\xA6', 'g',    '\\x15', '\\x4',  '\\xE3', '\\xD7', '\\xC8',\r\n    '\\xBD', '\\x15', '\\xE1', 'i',    '\\xB7', 'C',    '\\xAB', '\\xEA', 'x',\r\n    '\\x2F', 'j',    'X',    '\\x92', '\\xBB', '\\x18', '\\x20', '\\x9F', '\\xCF',\r\n    '3',    '\\xC3', '\\xB8', '\\xE9', 'N',    '\\xA7', '\\xD3', 'l',    'J',\r\n    '\\x0',  'i',    '6',    '\\x7C', '\\x8E', '\\xE1', '\\xFE', 'V',    '\\x84',\r\n    '\\xE7', '\\x3C', '\\x9F', 'r',    '\\x2B', '\\x3A', 'B',    '\\x7B', '7',\r\n    'f',    'w',    '\\xAE', '\\x8E', '\\xE',  '\\xF3', '\\xBD', 'R',    '\\xA9',\r\n    'd',    '\\x2',  'B',    '\\xAF', '\\x85', '2',    'f',    'F',    '\\xBA',\r\n    '\\xC',  '\\xD9', '\\x9F', '\\x1D', '\\x9A', 'l',    '\\x22', '\\xE6', '\\xC7',\r\n    '\\x3A', '\\x2C', '\\x80', '\\xEF', '\\xC1', '\\x15', '\\x90', '\\x7',  '\\x93',\r\n    '\\xA2', '\\x28', '\\xA0', 'S',    'j',    '\\xB1', '\\xB8', '\\xDF', '\\x29',\r\n    '5',    'C',    '\\xE',  '\\x3F', 'X',    '\\xFC', '\\x98', '\\xDA', 'y',\r\n    'j',    'P',    '\\x40', '\\x0',  '\\x87', '\\xAE', '\\x1B', '\\x17', 'B',\r\n    '\\xB4', '\\x3A', '\\x3F', '\\xBE', 'y',    '\\xC7', '\\xA',  '\\x26', '\\xB6',\r\n    '\\xEE', '\\xD9', '\\x9A', '\\x60', '\\x14', '\\x93', '\\xDB', '\\x8F', '\\xD',\r\n    '\\xA',  '\\x2E', '\\xE9', '\\x23', '\\x95', '\\x29', 'X',    '\\x0',  '\\x27',\r\n    '\\xEB', 'n',    'V',    'p',    '\\xBC', '\\xD6', '\\xCB', '\\xD6', 'G',\r\n    '\\xAB', '\\x3D', 'l',    '\\x7D', '\\xB8', '\\xD2', '\\xDD', '\\xA0', '\\x60',\r\n    '\\x83', '\\xBA', '\\xEF', '\\x5F', '\\xA4', '\\xEA', '\\xCC', '\\x2',  'N',\r\n    '\\xAE', '\\x5E', 'p',    '\\x1A', '\\xEC', '\\xB3', '\\x40', '9',    '\\xAC',\r\n    '\\xFE', '\\xF2', '\\x91', '\\x89', 'g',    '\\x91', '\\x85', '\\x21', '\\xA8',\r\n    '\\x87', '\\xB7', 'X',    '\\x7E', '\\x7E', '\\x85', '\\xBB', '\\xCD', 'N',\r\n    'N',    'b',    't',    '\\x40', '\\xFA', '\\x93', '\\x89', '\\xEC', '\\x1E',\r\n    '\\xEC', '\\x86', '\\x2',  'H',    '\\x26', '\\x93', '\\xD0', 'u',    '\\x1D',\r\n    '\\x7F', '\\x9',  '2',    '\\x95', '\\xBF', '\\x1F', '\\xDB', '\\xD7', 'c',\r\n    '\\x8A', '\\x1A', '\\xF7', '\\x5C', '\\xC1', '\\xFF', '\\x22', 'J',    '\\xC3',\r\n    '\\x87', '\\x0',  '\\x3',  '\\x0',  'K',    '\\xBB', '\\xF8', '\\xD6', '\\x2A',\r\n    'v',    '\\x98', 'I',    '\\x0',  '\\x0',  '\\x0',  '\\x0',  'I',    'E',\r\n    'N',    'D',    '\\xAE', 'B',    '\\x60', '\\x82',\r\n};\r\n\r\nvoid MimeType::init() {\r\n  mime[\".html\"] = \"text/html\";\r\n  mime[\".avi\"] = \"video/x-msvideo\";\r\n  mime[\".bmp\"] = \"image/bmp\";\r\n  mime[\".c\"] = \"text/plain\";\r\n  mime[\".doc\"] = \"application/msword\";\r\n  mime[\".gif\"] = \"image/gif\";\r\n  mime[\".gz\"] = \"application/x-gzip\";\r\n  mime[\".htm\"] = \"text/html\";\r\n  mime[\".ico\"] = \"image/x-icon\";\r\n  mime[\".jpg\"] = \"image/jpeg\";\r\n  mime[\".png\"] = \"image/png\";\r\n  mime[\".txt\"] = \"text/plain\";\r\n  mime[\".mp3\"] = \"audio/mp3\";\r\n  mime[\"default\"] = \"text/html\";\r\n}\r\n\r\nstd::string MimeType::getMime(const std::string &suffix) {\r\n  pthread_once(&once_control, MimeType::init);\r\n  if (mime.find(suffix) == mime.end())\r\n    return mime[\"default\"];\r\n  else\r\n    return mime[suffix];\r\n}\r\n\r\nHttpData::HttpData(EventLoop *loop, int connfd)\r\n    : loop_(loop),\r\n      channel_(new Channel(loop, connfd)),\r\n      fd_(connfd),\r\n      error_(false),\r\n      connectionState_(H_CONNECTED),\r\n      method_(METHOD_GET),\r\n      HTTPVersion_(HTTP_11),\r\n      nowReadPos_(0),\r\n      state_(STATE_PARSE_URI),\r\n      hState_(H_START),\r\n      keepAlive_(false) {\r\n  // loop_->queueInLoop(bind(&HttpData::setHandlers, this));\r\n  channel_->setReadHandler(bind(&HttpData::handleRead, this));\r\n  channel_->setWriteHandler(bind(&HttpData::handleWrite, this));\r\n  channel_->setConnHandler(bind(&HttpData::handleConn, this));\r\n}\r\n\r\nvoid HttpData::reset() {\r\n  // inBuffer_.clear();\r\n  fileName_.clear();\r\n  path_.clear();\r\n  nowReadPos_ = 0;\r\n  state_ = STATE_PARSE_URI;\r\n  hState_ = H_START;\r\n  headers_.clear();\r\n  // keepAlive_ = false;\r\n  if (timer_.lock()) {\r\n    shared_ptr<TimerNode> my_timer(timer_.lock());\r\n    my_timer->clearReq();\r\n    timer_.reset();\r\n  }\r\n}\r\n\r\nvoid HttpData::seperateTimer() {\r\n  // cout << \"seperateTimer\" << endl;\r\n  if (timer_.lock()) {\r\n    shared_ptr<TimerNode> my_timer(timer_.lock());\r\n    my_timer->clearReq();\r\n    timer_.reset();\r\n  }\r\n}\r\n\r\nvoid HttpData::handleRead() {\r\n  __uint32_t &events_ = channel_->getEvents();\r\n  do {\r\n    bool zero = false;\r\n    int read_num = readn(fd_, inBuffer_, zero);\r\n    LOG << \"Request: \" << inBuffer_;\r\n    if (connectionState_ == H_DISCONNECTING) {\r\n      inBuffer_.clear();\r\n      break;\r\n    }\r\n    // cout << inBuffer_ << endl;\r\n    if (read_num < 0) {\r\n      perror(\"1\");\r\n      error_ = true;\r\n      handleError(fd_, 400, \"Bad Request\");\r\n      break;\r\n    }\r\n    // else if (read_num == 0)\r\n    // {\r\n    //     error_ = true;\r\n    //     break;\r\n    // }\r\n    else if (zero) {\r\n      // 有请求出现但是读不到数据，可能是Request\r\n      // Aborted，或者来自网络的数据没有达到等原因\r\n      // 最可能是对端已经关闭了，统一按照对端已经关闭处理\r\n      // error_ = true;\r\n      connectionState_ = H_DISCONNECTING;\r\n      if (read_num == 0) {\r\n        // error_ = true;\r\n        break;\r\n      }\r\n      // cout << \"readnum == 0\" << endl;\r\n    }\r\n\r\n    if (state_ == STATE_PARSE_URI) {\r\n      URIState flag = this->parseURI();\r\n      if (flag == PARSE_URI_AGAIN)\r\n        break;\r\n      else if (flag == PARSE_URI_ERROR) {\r\n        perror(\"2\");\r\n        LOG << \"FD = \" << fd_ << \",\" << inBuffer_ << \"******\";\r\n        inBuffer_.clear();\r\n        error_ = true;\r\n        handleError(fd_, 400, \"Bad Request\");\r\n        break;\r\n      } else\r\n        state_ = STATE_PARSE_HEADERS;\r\n    }\r\n    if (state_ == STATE_PARSE_HEADERS) {\r\n      HeaderState flag = this->parseHeaders();\r\n      if (flag == PARSE_HEADER_AGAIN)\r\n        break;\r\n      else if (flag == PARSE_HEADER_ERROR) {\r\n        perror(\"3\");\r\n        error_ = true;\r\n        handleError(fd_, 400, \"Bad Request\");\r\n        break;\r\n      }\r\n      if (method_ == METHOD_POST) {\r\n        // POST方法准备\r\n        state_ = STATE_RECV_BODY;\r\n      } else {\r\n        state_ = STATE_ANALYSIS;\r\n      }\r\n    }\r\n    if (state_ == STATE_RECV_BODY) {\r\n      int content_length = -1;\r\n      if (headers_.find(\"Content-length\") != headers_.end()) {\r\n        content_length = stoi(headers_[\"Content-length\"]);\r\n      } else {\r\n        // cout << \"(state_ == STATE_RECV_BODY)\" << endl;\r\n        error_ = true;\r\n        handleError(fd_, 400, \"Bad Request: Lack of argument (Content-length)\");\r\n        break;\r\n      }\r\n      if (static_cast<int>(inBuffer_.size()) < content_length) break;\r\n      state_ = STATE_ANALYSIS;\r\n    }\r\n    if (state_ == STATE_ANALYSIS) {\r\n      AnalysisState flag = this->analysisRequest();\r\n      if (flag == ANALYSIS_SUCCESS) {\r\n        state_ = STATE_FINISH;\r\n        break;\r\n      } else {\r\n        // cout << \"state_ == STATE_ANALYSIS\" << endl;\r\n        error_ = true;\r\n        break;\r\n      }\r\n    }\r\n  } while (false);\r\n  // cout << \"state_=\" << state_ << endl;\r\n  if (!error_) {\r\n    if (outBuffer_.size() > 0) {\r\n      handleWrite();\r\n      // events_ |= EPOLLOUT;\r\n    }\r\n    // error_ may change\r\n    if (!error_ && state_ == STATE_FINISH) {\r\n      this->reset();\r\n      if (inBuffer_.size() > 0) {\r\n        if (connectionState_ != H_DISCONNECTING) handleRead();\r\n      }\r\n\r\n      // if ((keepAlive_ || inBuffer_.size() > 0) && connectionState_ ==\r\n      // H_CONNECTED)\r\n      // {\r\n      //     this->reset();\r\n      //     events_ |= EPOLLIN;\r\n      // }\r\n    } else if (!error_ && connectionState_ != H_DISCONNECTED)\r\n      events_ |= EPOLLIN;\r\n  }\r\n}\r\n\r\nvoid HttpData::handleWrite() {\r\n  if (!error_ && connectionState_ != H_DISCONNECTED) {\r\n    __uint32_t &events_ = channel_->getEvents();\r\n    if (writen(fd_, outBuffer_) < 0) {\r\n      perror(\"writen\");\r\n      events_ = 0;\r\n      error_ = true;\r\n    }\r\n    if (outBuffer_.size() > 0) events_ |= EPOLLOUT;\r\n  }\r\n}\r\n\r\nvoid HttpData::handleConn() {\r\n  seperateTimer();\r\n  __uint32_t &events_ = channel_->getEvents();\r\n  if (!error_ && connectionState_ == H_CONNECTED) {\r\n    if (events_ != 0) {\r\n      int timeout = DEFAULT_EXPIRED_TIME;\r\n      if (keepAlive_) timeout = DEFAULT_KEEP_ALIVE_TIME;\r\n      if ((events_ & EPOLLIN) && (events_ & EPOLLOUT)) {\r\n        events_ = __uint32_t(0);\r\n        events_ |= EPOLLOUT;\r\n      }\r\n      // events_ |= (EPOLLET | EPOLLONESHOT);\r\n      events_ |= EPOLLET;\r\n      loop_->updatePoller(channel_, timeout);\r\n\r\n    } else if (keepAlive_) {\r\n      events_ |= (EPOLLIN | EPOLLET);\r\n      // events_ |= (EPOLLIN | EPOLLET | EPOLLONESHOT);\r\n      int timeout = DEFAULT_KEEP_ALIVE_TIME;\r\n      loop_->updatePoller(channel_, timeout);\r\n    } else {\r\n      // cout << \"close normally\" << endl;\r\n      // loop_->shutdown(channel_);\r\n      // loop_->runInLoop(bind(&HttpData::handleClose, shared_from_this()));\r\n      events_ |= (EPOLLIN | EPOLLET);\r\n      // events_ |= (EPOLLIN | EPOLLET | EPOLLONESHOT);\r\n      int timeout = (DEFAULT_KEEP_ALIVE_TIME >> 1);\r\n      loop_->updatePoller(channel_, timeout);\r\n    }\r\n  } else if (!error_ && connectionState_ == H_DISCONNECTING &&\r\n             (events_ & EPOLLOUT)) {\r\n    events_ = (EPOLLOUT | EPOLLET);\r\n  } else {\r\n    // cout << \"close with errors\" << endl;\r\n    loop_->runInLoop(bind(&HttpData::handleClose, shared_from_this()));\r\n  }\r\n}\r\n\r\nURIState HttpData::parseURI() {\r\n  string &str = inBuffer_;\r\n  string cop = str;\r\n  // 读到完整的请求行再开始解析请求\r\n  size_t pos = str.find('\\r', nowReadPos_);\r\n  if (pos < 0) {\r\n    return PARSE_URI_AGAIN;\r\n  }\r\n  // 去掉请求行所占的空间，节省空间\r\n  string request_line = str.substr(0, pos);\r\n  if (str.size() > pos + 1)\r\n    str = str.substr(pos + 1);\r\n  else\r\n    str.clear();\r\n  // Method\r\n  int posGet = request_line.find(\"GET\");\r\n  int posPost = request_line.find(\"POST\");\r\n  int posHead = request_line.find(\"HEAD\");\r\n\r\n  if (posGet >= 0) {\r\n    pos = posGet;\r\n    method_ = METHOD_GET;\r\n  } else if (posPost >= 0) {\r\n    pos = posPost;\r\n    method_ = METHOD_POST;\r\n  } else if (posHead >= 0) {\r\n    pos = posHead;\r\n    method_ = METHOD_HEAD;\r\n  } else {\r\n    return PARSE_URI_ERROR;\r\n  }\r\n\r\n  // filename\r\n  pos = request_line.find(\"/\", pos);\r\n  if (pos < 0) {\r\n    fileName_ = \"index.html\";\r\n    HTTPVersion_ = HTTP_11;\r\n    return PARSE_URI_SUCCESS;\r\n  } else {\r\n    size_t _pos = request_line.find(' ', pos);\r\n    if (_pos < 0)\r\n      return PARSE_URI_ERROR;\r\n    else {\r\n      if (_pos - pos > 1) {\r\n        fileName_ = request_line.substr(pos + 1, _pos - pos - 1);\r\n        size_t __pos = fileName_.find('?');\r\n        if (__pos >= 0) {\r\n          fileName_ = fileName_.substr(0, __pos);\r\n        }\r\n      }\r\n\r\n      else\r\n        fileName_ = \"index.html\";\r\n    }\r\n    pos = _pos;\r\n  }\r\n  // cout << \"fileName_: \" << fileName_ << endl;\r\n  // HTTP 版本号\r\n  pos = request_line.find(\"/\", pos);\r\n  if (pos < 0)\r\n    return PARSE_URI_ERROR;\r\n  else {\r\n    if (request_line.size() - pos <= 3)\r\n      return PARSE_URI_ERROR;\r\n    else {\r\n      string ver = request_line.substr(pos + 1, 3);\r\n      if (ver == \"1.0\")\r\n        HTTPVersion_ = HTTP_10;\r\n      else if (ver == \"1.1\")\r\n        HTTPVersion_ = HTTP_11;\r\n      else\r\n        return PARSE_URI_ERROR;\r\n    }\r\n  }\r\n  return PARSE_URI_SUCCESS;\r\n}\r\n\r\nHeaderState HttpData::parseHeaders() {\r\n  string &str = inBuffer_;\r\n  int key_start = -1, key_end = -1, value_start = -1, value_end = -1;\r\n  int now_read_line_begin = 0;\r\n  bool notFinish = true;\r\n  size_t i = 0;\r\n  for (; i < str.size() && notFinish; ++i) {\r\n    switch (hState_) {\r\n      case H_START: {\r\n        if (str[i] == '\\n' || str[i] == '\\r') break;\r\n        hState_ = H_KEY;\r\n        key_start = i;\r\n        now_read_line_begin = i;\r\n        break;\r\n      }\r\n      case H_KEY: {\r\n        if (str[i] == ':') {\r\n          key_end = i;\r\n          if (key_end - key_start <= 0) return PARSE_HEADER_ERROR;\r\n          hState_ = H_COLON;\r\n        } else if (str[i] == '\\n' || str[i] == '\\r')\r\n          return PARSE_HEADER_ERROR;\r\n        break;\r\n      }\r\n      case H_COLON: {\r\n        if (str[i] == ' ') {\r\n          hState_ = H_SPACES_AFTER_COLON;\r\n        } else\r\n          return PARSE_HEADER_ERROR;\r\n        break;\r\n      }\r\n      case H_SPACES_AFTER_COLON: {\r\n        hState_ = H_VALUE;\r\n        value_start = i;\r\n        break;\r\n      }\r\n      case H_VALUE: {\r\n        if (str[i] == '\\r') {\r\n          hState_ = H_CR;\r\n          value_end = i;\r\n          if (value_end - value_start <= 0) return PARSE_HEADER_ERROR;\r\n        } else if (i - value_start > 255)\r\n          return PARSE_HEADER_ERROR;\r\n        break;\r\n      }\r\n      case H_CR: {\r\n        if (str[i] == '\\n') {\r\n          hState_ = H_LF;\r\n          string key(str.begin() + key_start, str.begin() + key_end);\r\n          string value(str.begin() + value_start, str.begin() + value_end);\r\n          headers_[key] = value;\r\n          now_read_line_begin = i;\r\n        } else\r\n          return PARSE_HEADER_ERROR;\r\n        break;\r\n      }\r\n      case H_LF: {\r\n        if (str[i] == '\\r') {\r\n          hState_ = H_END_CR;\r\n        } else {\r\n          key_start = i;\r\n          hState_ = H_KEY;\r\n        }\r\n        break;\r\n      }\r\n      case H_END_CR: {\r\n        if (str[i] == '\\n') {\r\n          hState_ = H_END_LF;\r\n        } else\r\n          return PARSE_HEADER_ERROR;\r\n        break;\r\n      }\r\n      case H_END_LF: {\r\n        notFinish = false;\r\n        key_start = i;\r\n        now_read_line_begin = i;\r\n        break;\r\n      }\r\n    }\r\n  }\r\n  if (hState_ == H_END_LF) {\r\n    str = str.substr(i);\r\n    return PARSE_HEADER_SUCCESS;\r\n  }\r\n  str = str.substr(now_read_line_begin);\r\n  return PARSE_HEADER_AGAIN;\r\n}\r\n\r\nAnalysisState HttpData::analysisRequest() {\r\n  if (method_ == METHOD_POST) {\r\n    // ------------------------------------------------------\r\n    // My CV stitching handler which requires OpenCV library\r\n    // ------------------------------------------------------\r\n    // string header;\r\n    // header += string(\"HTTP/1.1 200 OK\\r\\n\");\r\n    // if(headers_.find(\"Connection\") != headers_.end() &&\r\n    // headers_[\"Connection\"] == \"Keep-Alive\")\r\n    // {\r\n    //     keepAlive_ = true;\r\n    //     header += string(\"Connection: Keep-Alive\\r\\n\") + \"Keep-Alive:\r\n    //     timeout=\" + to_string(DEFAULT_KEEP_ALIVE_TIME) + \"\\r\\n\";\r\n    // }\r\n    // int length = stoi(headers_[\"Content-length\"]);\r\n    // vector<char> data(inBuffer_.begin(), inBuffer_.begin() + length);\r\n    // Mat src = imdecode(data, CV_LOAD_IMAGE_ANYDEPTH|CV_LOAD_IMAGE_ANYCOLOR);\r\n    // //imwrite(\"receive.bmp\", src);\r\n    // Mat res = stitch(src);\r\n    // vector<uchar> data_encode;\r\n    // imencode(\".png\", res, data_encode);\r\n    // header += string(\"Content-length: \") + to_string(data_encode.size()) +\r\n    // \"\\r\\n\\r\\n\";\r\n    // outBuffer_ += header + string(data_encode.begin(), data_encode.end());\r\n    // inBuffer_ = inBuffer_.substr(length);\r\n    // return ANALYSIS_SUCCESS;\r\n  } else if (method_ == METHOD_GET || method_ == METHOD_HEAD) {\r\n    string header;\r\n    header += \"HTTP/1.1 200 OK\\r\\n\";\r\n    if (headers_.find(\"Connection\") != headers_.end() &&\r\n        (headers_[\"Connection\"] == \"Keep-Alive\" ||\r\n         headers_[\"Connection\"] == \"keep-alive\")) {\r\n      keepAlive_ = true;\r\n      header += string(\"Connection: Keep-Alive\\r\\n\") + \"Keep-Alive: timeout=\" +\r\n                to_string(DEFAULT_KEEP_ALIVE_TIME) + \"\\r\\n\";\r\n    }\r\n    int dot_pos = fileName_.find('.');\r\n    string filetype;\r\n    if (dot_pos < 0)\r\n      filetype = MimeType::getMime(\"default\");\r\n    else\r\n      filetype = MimeType::getMime(fileName_.substr(dot_pos));\r\n\r\n    // echo test\r\n    if (fileName_ == \"hello\") {\r\n      outBuffer_ =\r\n          \"HTTP/1.1 200 OK\\r\\nContent-type: text/plain\\r\\n\\r\\nHello World\";\r\n      return ANALYSIS_SUCCESS;\r\n    }\r\n    if (fileName_ == \"favicon.ico\") {\r\n      header += \"Content-Type: image/png\\r\\n\";\r\n      header += \"Content-Length: \" + to_string(sizeof favicon) + \"\\r\\n\";\r\n      header += \"Server: LinYa's Web Server\\r\\n\";\r\n\r\n      header += \"\\r\\n\";\r\n      outBuffer_ += header;\r\n      outBuffer_ += string(favicon, favicon + sizeof favicon);\r\n      ;\r\n      return ANALYSIS_SUCCESS;\r\n    }\r\n\r\n    struct stat sbuf;\r\n    if (stat(fileName_.c_str(), &sbuf) < 0) {\r\n      header.clear();\r\n      handleError(fd_, 404, \"Not Found!\");\r\n      return ANALYSIS_ERROR;\r\n    }\r\n    header += \"Content-Type: \" + filetype + \"\\r\\n\";\r\n    header += \"Content-Length: \" + to_string(sbuf.st_size) + \"\\r\\n\";\r\n    header += \"Server: LinYa's Web Server\\r\\n\";\r\n    // 头部结束\r\n    header += \"\\r\\n\";\r\n    outBuffer_ += header;\r\n\r\n    if (method_ == METHOD_HEAD) return ANALYSIS_SUCCESS;\r\n\r\n    int src_fd = open(fileName_.c_str(), O_RDONLY, 0);\r\n    if (src_fd < 0) {\r\n      outBuffer_.clear();\r\n      handleError(fd_, 404, \"Not Found!\");\r\n      return ANALYSIS_ERROR;\r\n    }\r\n    void *mmapRet = mmap(NULL, sbuf.st_size, PROT_READ, MAP_PRIVATE, src_fd, 0);\r\n    close(src_fd);\r\n    if (mmapRet == (void *)-1) {\r\n      munmap(mmapRet, sbuf.st_size);\r\n      outBuffer_.clear();\r\n      handleError(fd_, 404, \"Not Found!\");\r\n      return ANALYSIS_ERROR;\r\n    }\r\n    char *src_addr = static_cast<char *>(mmapRet);\r\n    outBuffer_ += string(src_addr, src_addr + sbuf.st_size);\r\n    ;\r\n    munmap(mmapRet, sbuf.st_size);\r\n    return ANALYSIS_SUCCESS;\r\n  }\r\n  return ANALYSIS_ERROR;\r\n}\r\n\r\nvoid HttpData::handleError(int fd, int err_num, string short_msg) {\r\n  short_msg = \" \" + short_msg;\r\n  char send_buff[4096];\r\n  string body_buff, header_buff;\r\n  body_buff += \"<html><title>哎~出错了</title>\";\r\n  body_buff += \"<body bgcolor=\\\"ffffff\\\">\";\r\n  body_buff += to_string(err_num) + short_msg;\r\n  body_buff += \"<hr><em> LinYa's Web Server</em>\\n</body></html>\";\r\n\r\n  header_buff += \"HTTP/1.1 \" + to_string(err_num) + short_msg + \"\\r\\n\";\r\n  header_buff += \"Content-Type: text/html\\r\\n\";\r\n  header_buff += \"Connection: Close\\r\\n\";\r\n  header_buff += \"Content-Length: \" + to_string(body_buff.size()) + \"\\r\\n\";\r\n  header_buff += \"Server: LinYa's Web Server\\r\\n\";\r\n  ;\r\n  header_buff += \"\\r\\n\";\r\n  // 错误处理不考虑writen不完的情况\r\n  sprintf(send_buff, \"%s\", header_buff.c_str());\r\n  writen(fd, send_buff, strlen(send_buff));\r\n  sprintf(send_buff, \"%s\", body_buff.c_str());\r\n  writen(fd, send_buff, strlen(send_buff));\r\n}\r\n\r\nvoid HttpData::handleClose() {\r\n  connectionState_ = H_DISCONNECTED;\r\n  shared_ptr<HttpData> guard(shared_from_this());\r\n  loop_->removeFromPoller(channel_);\r\n}\r\n\r\nvoid HttpData::newEvent() {\r\n  channel_->setEvents(DEFAULT_EVENT);\r\n  loop_->addToPoller(channel_, DEFAULT_EXPIRED_TIME);\r\n}\r\n"
  },
  {
    "path": "WebServer/HttpData.h",
    "content": "// @Author Lin Ya\r\n// @Email xxbbb@vip.qq.com\r\n#pragma once\r\n#include <sys/epoll.h>\r\n#include <unistd.h>\r\n#include <functional>\r\n#include <map>\r\n#include <memory>\r\n#include <string>\r\n#include <unordered_map>\r\n#include \"Timer.h\"\r\n\r\n\r\nclass EventLoop;\r\nclass TimerNode;\r\nclass Channel;\r\n\r\nenum ProcessState {\r\n  STATE_PARSE_URI = 1,\r\n  STATE_PARSE_HEADERS,\r\n  STATE_RECV_BODY,\r\n  STATE_ANALYSIS,\r\n  STATE_FINISH\r\n};\r\n\r\nenum URIState {\r\n  PARSE_URI_AGAIN = 1,\r\n  PARSE_URI_ERROR,\r\n  PARSE_URI_SUCCESS,\r\n};\r\n\r\nenum HeaderState {\r\n  PARSE_HEADER_SUCCESS = 1,\r\n  PARSE_HEADER_AGAIN,\r\n  PARSE_HEADER_ERROR\r\n};\r\n\r\nenum AnalysisState { ANALYSIS_SUCCESS = 1, ANALYSIS_ERROR };\r\n\r\nenum ParseState {\r\n  H_START = 0,\r\n  H_KEY,\r\n  H_COLON,\r\n  H_SPACES_AFTER_COLON,\r\n  H_VALUE,\r\n  H_CR,\r\n  H_LF,\r\n  H_END_CR,\r\n  H_END_LF\r\n};\r\n\r\nenum ConnectionState { H_CONNECTED = 0, H_DISCONNECTING, H_DISCONNECTED };\r\n\r\nenum HttpMethod { METHOD_POST = 1, METHOD_GET, METHOD_HEAD };\r\n\r\nenum HttpVersion { HTTP_10 = 1, HTTP_11 };\r\n\r\nclass MimeType {\r\n private:\r\n  static void init();\r\n  static std::unordered_map<std::string, std::string> mime;\r\n  MimeType();\r\n  MimeType(const MimeType &m);\r\n\r\n public:\r\n  static std::string getMime(const std::string &suffix);\r\n\r\n private:\r\n  static pthread_once_t once_control;\r\n};\r\n\r\nclass HttpData : public std::enable_shared_from_this<HttpData> {\r\n public:\r\n  HttpData(EventLoop *loop, int connfd);\r\n  ~HttpData() { close(fd_); }\r\n  void reset();\r\n  void seperateTimer();\r\n  void linkTimer(std::shared_ptr<TimerNode> mtimer) {\r\n    // shared_ptr重载了bool, 但weak_ptr没有\r\n    timer_ = mtimer;\r\n  }\r\n  std::shared_ptr<Channel> getChannel() { return channel_; }\r\n  EventLoop *getLoop() { return loop_; }\r\n  void handleClose();\r\n  void newEvent();\r\n\r\n private:\r\n  EventLoop *loop_;\r\n  std::shared_ptr<Channel> channel_;\r\n  int fd_;\r\n  std::string inBuffer_;\r\n  std::string outBuffer_;\r\n  bool error_;\r\n  ConnectionState connectionState_;\r\n\r\n  HttpMethod method_;\r\n  HttpVersion HTTPVersion_;\r\n  std::string fileName_;\r\n  std::string path_;\r\n  int nowReadPos_;\r\n  ProcessState state_;\r\n  ParseState hState_;\r\n  bool keepAlive_;\r\n  std::map<std::string, std::string> headers_;\r\n  std::weak_ptr<TimerNode> timer_;\r\n\r\n  void handleRead();\r\n  void handleWrite();\r\n  void handleConn();\r\n  void handleError(int fd, int err_num, std::string short_msg);\r\n  URIState parseURI();\r\n  HeaderState parseHeaders();\r\n  AnalysisState analysisRequest();\r\n};"
  },
  {
    "path": "WebServer/Main.cpp",
    "content": "// @Author Lin Ya\r\n// @Email xxbbb@vip.qq.com\r\n#include <getopt.h>\r\n#include <string>\r\n#include \"EventLoop.h\"\r\n#include \"Server.h\"\r\n#include \"base/Logging.h\"\r\n\r\n\r\nint main(int argc, char *argv[]) {\r\n  int threadNum = 4;\r\n  int port = 80;\r\n  std::string logPath = \"./WebServer.log\";\r\n\r\n  // parse args\r\n  int opt;\r\n  const char *str = \"t:l:p:\";\r\n  while ((opt = getopt(argc, argv, str)) != -1) {\r\n    switch (opt) {\r\n      case 't': {\r\n        threadNum = atoi(optarg);\r\n        break;\r\n      }\r\n      case 'l': {\r\n        logPath = optarg;\r\n        if (logPath.size() < 2 || optarg[0] != '/') {\r\n          printf(\"logPath should start with \\\"/\\\"\\n\");\r\n          abort();\r\n        }\r\n        break;\r\n      }\r\n      case 'p': {\r\n        port = atoi(optarg);\r\n        break;\r\n      }\r\n      default:\r\n        break;\r\n    }\r\n  }\r\n  Logger::setLogFileName(logPath);\r\n// STL库在多线程上应用\r\n#ifndef _PTHREADS\r\n  LOG << \"_PTHREADS is not defined !\";\r\n#endif\r\n  EventLoop mainLoop;\r\n  Server myHTTPServer(&mainLoop, threadNum, port);\r\n  myHTTPServer.start();\r\n  mainLoop.loop();\r\n  return 0;\r\n}\r\n"
  },
  {
    "path": "WebServer/Makefile",
    "content": "# MAINSOURCE代表含有main入口函数的cpp文件，因为含有测试代码，\r\n# 所以要为多个目标编译，这里把Makefile写的通用了一点，\r\n# 以后加东西Makefile不用做多少改动\r\nMAINSOURCE := Main.cpp base/tests/LoggingTest.cpp tests/HTTPClient.cpp\r\n# MAINOBJS := $(patsubst %.cpp,%.o,$(MAINSOURCE))\r\nSOURCE  := $(wildcard *.cpp base/*.cpp tests/*.cpp)\r\noverride SOURCE := $(filter-out $(MAINSOURCE),$(SOURCE))\r\nOBJS    := $(patsubst %.cpp,%.o,$(SOURCE))\r\n\r\nTARGET  := WebServer\r\nCC      := g++\r\nLIBS    := -lpthread\r\nINCLUDE:= -I./usr/local/lib\r\nCFLAGS  := -std=c++11 -g -Wall -O3 -D_PTHREADS\r\nCXXFLAGS:= $(CFLAGS)\r\n\r\n# Test object\r\nSUBTARGET1 := LoggingTest\r\nSUBTARGET2 := HTTPClient\r\n\r\n.PHONY : objs clean veryclean rebuild all tests debug\r\nall : $(TARGET) $(SUBTARGET1) $(SUBTARGET2)\r\nobjs : $(OBJS)\r\nrebuild: veryclean all\r\n\r\ntests : $(SUBTARGET1) $(SUBTARGET2)\r\nclean :\r\n\tfind . -name '*.o' | xargs rm -f\r\nveryclean :\r\n\tfind . -name '*.o' | xargs rm -f\r\n\tfind . -name $(TARGET) | xargs rm -f\r\n\tfind . -name $(SUBTARGET1) | xargs rm -f\r\n\tfind . -name $(SUBTARGET2) | xargs rm -f\r\ndebug:\r\n\t@echo $(SOURCE)\r\n\r\n$(TARGET) : $(OBJS) Main.o\r\n\t$(CC) $(CXXFLAGS) -o $@ $^ $(LDFLAGS) $(LIBS)\r\n# $@代表目标，这里是$(TARGET)\r\n\r\n$(SUBTARGET1) : $(OBJS) base/tests/LoggingTest.o\r\n\t$(CC) $(CXXFLAGS) -o $@ $^ $(LDFLAGS) $(LIBS)\r\n\r\n$(SUBTARGET2) : $(OBJS) tests/HTTPClient.o\r\n\t$(CC) $(CXXFLAGS) -o $@ $^ $(LDFLAGS) $(LIBS)\r\n"
  },
  {
    "path": "WebServer/Makefile.bak",
    "content": "# @Author Lin Ya\r\n# @Email xxbbb@vip.qq.com\r\n# MAINSOURCE代表含有main入口函数的cpp文件，因为含有测试代码，\r\n# 所以要为多个目标编译，这里把Makefile写的通用了一点，\r\n# 以后加东西Makefile不用做多少改动\r\nMAINSOURCE := Main.cpp base/tests/LoggingTest.cpp tests/HTTPClient.cpp\r\n# MAINOBJS := $(patsubst %.cpp,%.o,$(MAINSOURCE))\r\nSOURCE  := $(wildcard *.cpp base/*.cpp tests/*.cpp)\r\noverride SOURCE := $(filter-out $(MAINSOURCE),$(SOURCE))\r\nOBJS    := $(patsubst %.cpp,%.o,$(SOURCE))\r\n\r\nTARGET  := WebServer\r\nCC      := g++\r\nLIBS    := -lpthread -lopencv_core -lopencv_imgproc -lopencv_highgui -lopencv_imgcodecs\r\nINCLUDE:= -I./usr/local/lib\r\nCFLAGS  := -std=c++11 -g -Wall -O3 -D_PTHREADS\r\nCXXFLAGS:= $(CFLAGS)\r\n\r\n# Test object\r\nSUBTARGET1 := LoggingTest\r\nSUBTARGET2 := HTTPClient\r\n\r\n.PHONY : objs clean veryclean rebuild all tests debug\r\nall : $(TARGET) $(SUBTARGET1) $(SUBTARGET2)\r\nobjs : $(OBJS)\r\nrebuild: veryclean all\r\n\r\ntests : $(SUBTARGET1) $(SUBTARGET2)\r\nclean :\r\n\tfind . -name '*.o' | xargs rm -f\r\nveryclean :\r\n\tfind . -name '*.o' | xargs rm -f\r\n\tfind . -name $(TARGET) | xargs rm -f\r\n\tfind . -name $(SUBTARGET1) | xargs rm -f\r\n\tfind . -name $(SUBTARGET2) | xargs rm -f\r\ndebug:\r\n\t@echo $(SOURCE)\r\n\r\n$(TARGET) : $(OBJS) Main.o\r\n\t$(CC) $(CXXFLAGS) -o $@ $^ $(LDFLAGS) $(LIBS)\r\n# $@代表目标，这里是$(TARGET)\r\n\r\n$(SUBTARGET1) : $(OBJS) base/tests/LoggingTest.o\r\n\t$(CC) $(CXXFLAGS) -o $@ $^ $(LDFLAGS) $(LIBS)\r\n\r\n$(SUBTARGET2) : $(OBJS) tests/HTTPClient.o\r\n\t$(CC) $(CXXFLAGS) -o $@ $^ $(LDFLAGS) $(LIBS)"
  },
  {
    "path": "WebServer/Server.cpp",
    "content": "// @Author Lin Ya\r\n// @Email xxbbb@vip.qq.com\r\n#include \"Server.h\"\r\n#include <arpa/inet.h>\r\n#include <netinet/in.h>\r\n#include <sys/socket.h>\r\n#include <functional>\r\n#include \"Util.h\"\r\n#include \"base/Logging.h\"\r\n\r\nServer::Server(EventLoop *loop, int threadNum, int port)\r\n    : loop_(loop),\r\n      threadNum_(threadNum),\r\n      eventLoopThreadPool_(new EventLoopThreadPool(loop_, threadNum)),\r\n      started_(false),\r\n      acceptChannel_(new Channel(loop_)),\r\n      port_(port),\r\n      listenFd_(socket_bind_listen(port_)) {\r\n  acceptChannel_->setFd(listenFd_);\r\n  handle_for_sigpipe();\r\n  if (setSocketNonBlocking(listenFd_) < 0) {\r\n    perror(\"set socket non block failed\");\r\n    abort();\r\n  }\r\n}\r\n\r\nvoid Server::start() {\r\n  eventLoopThreadPool_->start();\r\n  // acceptChannel_->setEvents(EPOLLIN | EPOLLET | EPOLLONESHOT);\r\n  acceptChannel_->setEvents(EPOLLIN | EPOLLET);\r\n  acceptChannel_->setReadHandler(bind(&Server::handNewConn, this));\r\n  acceptChannel_->setConnHandler(bind(&Server::handThisConn, this));\r\n  loop_->addToPoller(acceptChannel_, 0);\r\n  started_ = true;\r\n}\r\n\r\nvoid Server::handNewConn() {\r\n  struct sockaddr_in client_addr;\r\n  memset(&client_addr, 0, sizeof(struct sockaddr_in));\r\n  socklen_t client_addr_len = sizeof(client_addr);\r\n  int accept_fd = 0;\r\n  while ((accept_fd = accept(listenFd_, (struct sockaddr *)&client_addr,\r\n                             &client_addr_len)) > 0) {\r\n    EventLoop *loop = eventLoopThreadPool_->getNextLoop();\r\n    LOG << \"New connection from \" << inet_ntoa(client_addr.sin_addr) << \":\"\r\n        << ntohs(client_addr.sin_port);\r\n    // cout << \"new connection\" << endl;\r\n    // cout << inet_ntoa(client_addr.sin_addr) << endl;\r\n    // cout << ntohs(client_addr.sin_port) << endl;\r\n    /*\r\n    // TCP的保活机制默认是关闭的\r\n    int optval = 0;\r\n    socklen_t len_optval = 4;\r\n    getsockopt(accept_fd, SOL_SOCKET,  SO_KEEPALIVE, &optval, &len_optval);\r\n    cout << \"optval ==\" << optval << endl;\r\n    */\r\n    // 限制服务器的最大并发连接数\r\n    if (accept_fd >= MAXFDS) {\r\n      close(accept_fd);\r\n      continue;\r\n    }\r\n    // 设为非阻塞模式\r\n    if (setSocketNonBlocking(accept_fd) < 0) {\r\n      LOG << \"Set non block failed!\";\r\n      // perror(\"Set non block failed!\");\r\n      return;\r\n    }\r\n\r\n    setSocketNodelay(accept_fd);\r\n    // setSocketNoLinger(accept_fd);\r\n\r\n    shared_ptr<HttpData> req_info(new HttpData(loop, accept_fd));\r\n    req_info->getChannel()->setHolder(req_info);\r\n    loop->queueInLoop(std::bind(&HttpData::newEvent, req_info));\r\n  }\r\n  acceptChannel_->setEvents(EPOLLIN | EPOLLET);\r\n}"
  },
  {
    "path": "WebServer/Server.h",
    "content": "// @Author Lin Ya\r\n// @Email xxbbb@vip.qq.com\r\n#pragma once\r\n#include <memory>\r\n#include \"Channel.h\"\r\n#include \"EventLoop.h\"\r\n#include \"EventLoopThreadPool.h\"\r\n\r\nclass Server {\r\n public:\r\n  Server(EventLoop *loop, int threadNum, int port);\r\n  ~Server() {}\r\n  EventLoop *getLoop() const { return loop_; }\r\n  void start();\r\n  void handNewConn();\r\n  void handThisConn() { loop_->updatePoller(acceptChannel_); }\r\n\r\n private:\r\n  EventLoop *loop_;\r\n  int threadNum_;\r\n  std::unique_ptr<EventLoopThreadPool> eventLoopThreadPool_;\r\n  bool started_;\r\n  std::shared_ptr<Channel> acceptChannel_;\r\n  int port_;\r\n  int listenFd_;\r\n  static const int MAXFDS = 100000;\r\n};"
  },
  {
    "path": "WebServer/ThreadPool.cpp",
    "content": "// @Author Lin Ya\r\n// @Email xxbbb@vip.qq.com\r\n\r\n\r\n// This file has not been used\r\n#include \"ThreadPool.h\"\r\n\r\npthread_mutex_t ThreadPool::lock = PTHREAD_MUTEX_INITIALIZER;\r\npthread_cond_t ThreadPool::notify = PTHREAD_COND_INITIALIZER;\r\nstd::vector<pthread_t> ThreadPool::threads;\r\nstd::vector<ThreadPoolTask> ThreadPool::queue;\r\nint ThreadPool::thread_count = 0;\r\nint ThreadPool::queue_size = 0;\r\nint ThreadPool::head = 0;\r\nint ThreadPool::tail = 0;\r\nint ThreadPool::count = 0;\r\nint ThreadPool::shutdown = 0;\r\nint ThreadPool::started = 0;\r\n\r\nint ThreadPool::threadpool_create(int _thread_count, int _queue_size)\r\n{\r\n    bool err = false;\r\n    do\r\n    {\r\n        if(_thread_count <= 0 || _thread_count > MAX_THREADS || _queue_size <= 0 || _queue_size > MAX_QUEUE) \r\n        {\r\n            _thread_count = 4;\r\n            _queue_size = 1024;\r\n        }\r\n    \r\n        thread_count = 0;\r\n        queue_size = _queue_size;\r\n        head = tail = count = 0;\r\n        shutdown = started = 0;\r\n\r\n        threads.resize(_thread_count);\r\n        queue.resize(_queue_size);\r\n    \r\n        /* Start worker threads */\r\n        for(int i = 0; i < _thread_count; ++i) \r\n        {\r\n            if(pthread_create(&threads[i], NULL, threadpool_thread, (void*)(0)) != 0) \r\n            {\r\n                //threadpool_destroy(pool, 0);\r\n                return -1;\r\n            }\r\n            ++thread_count;\r\n            ++started;\r\n        }\r\n    } while(false);\r\n    \r\n    if (err) \r\n    {\r\n        //threadpool_free(pool);\r\n        return -1;\r\n    }\r\n    return 0;\r\n}\r\n\r\nint ThreadPool::threadpool_add(std::shared_ptr<void> args, std::function<void(std::shared_ptr<void>)> fun)\r\n{\r\n    int next, err = 0;\r\n    if(pthread_mutex_lock(&lock) != 0)\r\n        return THREADPOOL_LOCK_FAILURE;\r\n    do \r\n    {\r\n        next = (tail + 1) % queue_size;\r\n        // 队列满\r\n        if(count == queue_size) \r\n        {\r\n            err = THREADPOOL_QUEUE_FULL;\r\n            break;\r\n        }\r\n        // 已关闭\r\n        if(shutdown)\r\n        {\r\n            err = THREADPOOL_SHUTDOWN;\r\n            break;\r\n        }\r\n        queue[tail].fun = fun;\r\n        queue[tail].args = args;\r\n        tail = next;\r\n        ++count;\r\n        \r\n        /* pthread_cond_broadcast */\r\n        if(pthread_cond_signal(&notify) != 0) \r\n        {\r\n            err = THREADPOOL_LOCK_FAILURE;\r\n            break;\r\n        }\r\n    } while(false);\r\n\r\n    if(pthread_mutex_unlock(&lock) != 0)\r\n        err = THREADPOOL_LOCK_FAILURE;\r\n    return err;\r\n}\r\n\r\n\r\nint ThreadPool::threadpool_destroy(ShutDownOption shutdown_option)\r\n{\r\n    printf(\"Thread pool destroy !\\n\");\r\n    int i, err = 0;\r\n\r\n    if(pthread_mutex_lock(&lock) != 0) \r\n    {\r\n        return THREADPOOL_LOCK_FAILURE;\r\n    }\r\n    do \r\n    {\r\n        if(shutdown) {\r\n            err = THREADPOOL_SHUTDOWN;\r\n            break;\r\n        }\r\n        shutdown = shutdown_option;\r\n\r\n        if((pthread_cond_broadcast(&notify) != 0) ||\r\n           (pthread_mutex_unlock(&lock) != 0)) {\r\n            err = THREADPOOL_LOCK_FAILURE;\r\n            break;\r\n        }\r\n\r\n        for(i = 0; i < thread_count; ++i)\r\n        {\r\n            if(pthread_join(threads[i], NULL) != 0)\r\n            {\r\n                err = THREADPOOL_THREAD_FAILURE;\r\n            }\r\n        }\r\n    } while(false);\r\n\r\n    if(!err) \r\n    {\r\n        threadpool_free();\r\n    }\r\n    return err;\r\n}\r\n\r\nint ThreadPool::threadpool_free()\r\n{\r\n    if(started > 0)\r\n        return -1;\r\n    pthread_mutex_lock(&lock);\r\n    pthread_mutex_destroy(&lock);\r\n    pthread_cond_destroy(&notify);\r\n    return 0;\r\n}\r\n\r\n\r\nvoid *ThreadPool::threadpool_thread(void *args)\r\n{\r\n    while (true)\r\n    {\r\n        ThreadPoolTask task;\r\n        pthread_mutex_lock(&lock);\r\n        while((count == 0) && (!shutdown)) \r\n        {\r\n            pthread_cond_wait(&notify, &lock);\r\n        }\r\n        if((shutdown == immediate_shutdown) ||\r\n           ((shutdown == graceful_shutdown) && (count == 0)))\r\n        {\r\n            break;\r\n        }\r\n        task.fun = queue[head].fun;\r\n        task.args = queue[head].args;\r\n        queue[head].fun = NULL;\r\n        queue[head].args.reset();\r\n        head = (head + 1) % queue_size;\r\n        --count;\r\n        pthread_mutex_unlock(&lock);\r\n        (task.fun)(task.args);\r\n    }\r\n    --started;\r\n    pthread_mutex_unlock(&lock);\r\n    printf(\"This threadpool thread finishs!\\n\");\r\n    pthread_exit(NULL);\r\n    return(NULL);\r\n}"
  },
  {
    "path": "WebServer/ThreadPool.h",
    "content": "// @Author Lin Ya\r\n// @Email xxbbb@vip.qq.com\r\n\r\n// This file has not been used\r\n#pragma once\r\n#include \"Channel.h\"\r\n#include <pthread.h>\r\n#include <functional>\r\n#include <memory>\r\n#include <vector>\r\n\r\nconst int THREADPOOL_INVALID = -1;\r\nconst int THREADPOOL_LOCK_FAILURE = -2;\r\nconst int THREADPOOL_QUEUE_FULL = -3;\r\nconst int THREADPOOL_SHUTDOWN = -4;\r\nconst int THREADPOOL_THREAD_FAILURE = -5;\r\nconst int THREADPOOL_GRACEFUL = 1;\r\n\r\nconst int MAX_THREADS = 1024;\r\nconst int MAX_QUEUE = 65535;\r\n\r\ntypedef enum\r\n{\r\n    immediate_shutdown = 1,\r\n    graceful_shutdown  = 2\r\n} ShutDownOption;\r\n\r\nstruct ThreadPoolTask\r\n{\r\n    std::function<void(std::shared_ptr<void>)> fun;\r\n    std::shared_ptr<void> args;\r\n};\r\n\r\n\r\nclass ThreadPool\r\n{\r\nprivate:\r\n    static pthread_mutex_t lock;\r\n    static pthread_cond_t notify;\r\n\r\n    static std::vector<pthread_t> threads;\r\n    static std::vector<ThreadPoolTask> queue;\r\n    static int thread_count;\r\n    static int queue_size;\r\n    static int head;\r\n    // tail 指向尾节点的下一节点\r\n    static int tail;\r\n    static int count;\r\n    static int shutdown;\r\n    static int started;\r\npublic:\r\n    static int threadpool_create(int _thread_count, int _queue_size);\r\n    static int threadpool_add(std::shared_ptr<void> args, std::function<void(std::shared_ptr<void>)> fun);\r\n    static int threadpool_destroy(ShutDownOption shutdown_option = graceful_shutdown);\r\n    static int threadpool_free();\r\n    static void *threadpool_thread(void *args);\r\n};\r\n"
  },
  {
    "path": "WebServer/Timer.cpp",
    "content": "// @Author Lin Ya\r\n// @Email xxbbb@vip.qq.com\r\n#include \"Timer.h\"\r\n#include <sys/time.h>\r\n#include <unistd.h>\r\n#include <queue>\r\n\r\nTimerNode::TimerNode(std::shared_ptr<HttpData> requestData, int timeout)\r\n    : deleted_(false), SPHttpData(requestData) {\r\n  struct timeval now;\r\n  gettimeofday(&now, NULL);\r\n  // 以毫秒计\r\n  expiredTime_ =\r\n      (((now.tv_sec % 10000) * 1000) + (now.tv_usec / 1000)) + timeout;\r\n}\r\n\r\nTimerNode::~TimerNode() {\r\n  if (SPHttpData) SPHttpData->handleClose();\r\n}\r\n\r\nTimerNode::TimerNode(TimerNode &tn)\r\n    : SPHttpData(tn.SPHttpData), expiredTime_(0) {}\r\n\r\nvoid TimerNode::update(int timeout) {\r\n  struct timeval now;\r\n  gettimeofday(&now, NULL);\r\n  expiredTime_ =\r\n      (((now.tv_sec % 10000) * 1000) + (now.tv_usec / 1000)) + timeout;\r\n}\r\n\r\nbool TimerNode::isValid() {\r\n  struct timeval now;\r\n  gettimeofday(&now, NULL);\r\n  size_t temp = (((now.tv_sec % 10000) * 1000) + (now.tv_usec / 1000));\r\n  if (temp < expiredTime_)\r\n    return true;\r\n  else {\r\n    this->setDeleted();\r\n    return false;\r\n  }\r\n}\r\n\r\nvoid TimerNode::clearReq() {\r\n  SPHttpData.reset();\r\n  this->setDeleted();\r\n}\r\n\r\nTimerManager::TimerManager() {}\r\n\r\nTimerManager::~TimerManager() {}\r\n\r\nvoid TimerManager::addTimer(std::shared_ptr<HttpData> SPHttpData, int timeout) {\r\n  SPTimerNode new_node(new TimerNode(SPHttpData, timeout));\r\n  timerNodeQueue.push(new_node);\r\n  SPHttpData->linkTimer(new_node);\r\n}\r\n\r\n/* 处理逻辑是这样的~\r\n因为(1) 优先队列不支持随机访问\r\n(2) 即使支持，随机删除某节点后破坏了堆的结构，需要重新更新堆结构。\r\n所以对于被置为deleted的时间节点，会延迟到它(1)超时 或\r\n(2)它前面的节点都被删除时，它才会被删除。\r\n一个点被置为deleted,它最迟会在TIMER_TIME_OUT时间后被删除。\r\n这样做有两个好处：\r\n(1) 第一个好处是不需要遍历优先队列，省时。\r\n(2)\r\n第二个好处是给超时时间一个容忍的时间，就是设定的超时时间是删除的下限(并不是一到超时时间就立即删除)，如果监听的请求在超时后的下一次请求中又一次出现了，\r\n就不用再重新申请RequestData节点了，这样可以继续重复利用前面的RequestData，减少了一次delete和一次new的时间。\r\n*/\r\n\r\nvoid TimerManager::handleExpiredEvent() {\r\n  // MutexLockGuard locker(lock);\r\n  while (!timerNodeQueue.empty()) {\r\n    SPTimerNode ptimer_now = timerNodeQueue.top();\r\n    if (ptimer_now->isDeleted())\r\n      timerNodeQueue.pop();\r\n    else if (ptimer_now->isValid() == false)\r\n      timerNodeQueue.pop();\r\n    else\r\n      break;\r\n  }\r\n}"
  },
  {
    "path": "WebServer/Timer.h",
    "content": "// @Author Lin Ya\r\n// @Email xxbbb@vip.qq.com\r\n#pragma once\r\n#include <unistd.h>\r\n#include <deque>\r\n#include <memory>\r\n#include <queue>\r\n#include \"HttpData.h\"\r\n#include \"base/MutexLock.h\"\r\n#include \"base/noncopyable.h\"\r\n\r\n\r\nclass HttpData;\r\n\r\nclass TimerNode {\r\n public:\r\n  TimerNode(std::shared_ptr<HttpData> requestData, int timeout);\r\n  ~TimerNode();\r\n  TimerNode(TimerNode &tn);\r\n  void update(int timeout);\r\n  bool isValid();\r\n  void clearReq();\r\n  void setDeleted() { deleted_ = true; }\r\n  bool isDeleted() const { return deleted_; }\r\n  size_t getExpTime() const { return expiredTime_; }\r\n\r\n private:\r\n  bool deleted_;\r\n  size_t expiredTime_;\r\n  std::shared_ptr<HttpData> SPHttpData;\r\n};\r\n\r\nstruct TimerCmp {\r\n  bool operator()(std::shared_ptr<TimerNode> &a,\r\n                  std::shared_ptr<TimerNode> &b) const {\r\n    return a->getExpTime() > b->getExpTime();\r\n  }\r\n};\r\n\r\nclass TimerManager {\r\n public:\r\n  TimerManager();\r\n  ~TimerManager();\r\n  void addTimer(std::shared_ptr<HttpData> SPHttpData, int timeout);\r\n  void handleExpiredEvent();\r\n\r\n private:\r\n  typedef std::shared_ptr<TimerNode> SPTimerNode;\r\n  std::priority_queue<SPTimerNode, std::deque<SPTimerNode>, TimerCmp>\r\n      timerNodeQueue;\r\n  // MutexLock lock;\r\n};"
  },
  {
    "path": "WebServer/Util.cpp",
    "content": "// @Author Lin Ya\r\n// @Email xxbbb@vip.qq.com\r\n#include \"Util.h\"\r\n\r\n#include <errno.h>\r\n#include <fcntl.h>\r\n#include <netinet/in.h>\r\n#include <netinet/tcp.h>\r\n#include <signal.h>\r\n#include <string.h>\r\n#include <sys/socket.h>\r\n#include <unistd.h>\r\n\r\n\r\nconst int MAX_BUFF = 4096;\r\nssize_t readn(int fd, void *buff, size_t n) {\r\n  size_t nleft = n;\r\n  ssize_t nread = 0;\r\n  ssize_t readSum = 0;\r\n  char *ptr = (char *)buff;\r\n  while (nleft > 0) {\r\n    if ((nread = read(fd, ptr, nleft)) < 0) {\r\n      if (errno == EINTR)\r\n        nread = 0;\r\n      else if (errno == EAGAIN) {\r\n        return readSum;\r\n      } else {\r\n        return -1;\r\n      }\r\n    } else if (nread == 0)\r\n      break;\r\n    readSum += nread;\r\n    nleft -= nread;\r\n    ptr += nread;\r\n  }\r\n  return readSum;\r\n}\r\n\r\nssize_t readn(int fd, std::string &inBuffer, bool &zero) {\r\n  ssize_t nread = 0;\r\n  ssize_t readSum = 0;\r\n  while (true) {\r\n    char buff[MAX_BUFF];\r\n    if ((nread = read(fd, buff, MAX_BUFF)) < 0) {\r\n      if (errno == EINTR)\r\n        continue;\r\n      else if (errno == EAGAIN) {\r\n        return readSum;\r\n      } else {\r\n        perror(\"read error\");\r\n        return -1;\r\n      }\r\n    } else if (nread == 0) {\r\n      // printf(\"redsum = %d\\n\", readSum);\r\n      zero = true;\r\n      break;\r\n    }\r\n    // printf(\"before inBuffer.size() = %d\\n\", inBuffer.size());\r\n    // printf(\"nread = %d\\n\", nread);\r\n    readSum += nread;\r\n    // buff += nread;\r\n    inBuffer += std::string(buff, buff + nread);\r\n    // printf(\"after inBuffer.size() = %d\\n\", inBuffer.size());\r\n  }\r\n  return readSum;\r\n}\r\n\r\nssize_t readn(int fd, std::string &inBuffer) {\r\n  ssize_t nread = 0;\r\n  ssize_t readSum = 0;\r\n  while (true) {\r\n    char buff[MAX_BUFF];\r\n    if ((nread = read(fd, buff, MAX_BUFF)) < 0) {\r\n      if (errno == EINTR)\r\n        continue;\r\n      else if (errno == EAGAIN) {\r\n        return readSum;\r\n      } else {\r\n        perror(\"read error\");\r\n        return -1;\r\n      }\r\n    } else if (nread == 0) {\r\n      // printf(\"redsum = %d\\n\", readSum);\r\n      break;\r\n    }\r\n    // printf(\"before inBuffer.size() = %d\\n\", inBuffer.size());\r\n    // printf(\"nread = %d\\n\", nread);\r\n    readSum += nread;\r\n    // buff += nread;\r\n    inBuffer += std::string(buff, buff + nread);\r\n    // printf(\"after inBuffer.size() = %d\\n\", inBuffer.size());\r\n  }\r\n  return readSum;\r\n}\r\n\r\nssize_t writen(int fd, void *buff, size_t n) {\r\n  size_t nleft = n;\r\n  ssize_t nwritten = 0;\r\n  ssize_t writeSum = 0;\r\n  char *ptr = (char *)buff;\r\n  while (nleft > 0) {\r\n    if ((nwritten = write(fd, ptr, nleft)) <= 0) {\r\n      if (nwritten < 0) {\r\n        if (errno == EINTR) {\r\n          nwritten = 0;\r\n          continue;\r\n        } else if (errno == EAGAIN) {\r\n          return writeSum;\r\n        } else\r\n          return -1;\r\n      }\r\n    }\r\n    writeSum += nwritten;\r\n    nleft -= nwritten;\r\n    ptr += nwritten;\r\n  }\r\n  return writeSum;\r\n}\r\n\r\nssize_t writen(int fd, std::string &sbuff) {\r\n  size_t nleft = sbuff.size();\r\n  ssize_t nwritten = 0;\r\n  ssize_t writeSum = 0;\r\n  const char *ptr = sbuff.c_str();\r\n  while (nleft > 0) {\r\n    if ((nwritten = write(fd, ptr, nleft)) <= 0) {\r\n      if (nwritten < 0) {\r\n        if (errno == EINTR) {\r\n          nwritten = 0;\r\n          continue;\r\n        } else if (errno == EAGAIN)\r\n          break;\r\n        else\r\n          return -1;\r\n      }\r\n    }\r\n    writeSum += nwritten;\r\n    nleft -= nwritten;\r\n    ptr += nwritten;\r\n  }\r\n  if (writeSum == static_cast<int>(sbuff.size()))\r\n    sbuff.clear();\r\n  else\r\n    sbuff = sbuff.substr(writeSum);\r\n  return writeSum;\r\n}\r\n\r\nvoid handle_for_sigpipe() {\r\n  struct sigaction sa;\r\n  memset(&sa, '\\0', sizeof(sa));\r\n  sa.sa_handler = SIG_IGN;\r\n  sa.sa_flags = 0;\r\n  if (sigaction(SIGPIPE, &sa, NULL)) return;\r\n}\r\n\r\nint setSocketNonBlocking(int fd) {\r\n  int flag = fcntl(fd, F_GETFL, 0);\r\n  if (flag == -1) return -1;\r\n\r\n  flag |= O_NONBLOCK;\r\n  if (fcntl(fd, F_SETFL, flag) == -1) return -1;\r\n  return 0;\r\n}\r\n\r\nvoid setSocketNodelay(int fd) {\r\n  int enable = 1;\r\n  setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (void *)&enable, sizeof(enable));\r\n}\r\n\r\nvoid setSocketNoLinger(int fd) {\r\n  struct linger linger_;\r\n  linger_.l_onoff = 1;\r\n  linger_.l_linger = 30;\r\n  setsockopt(fd, SOL_SOCKET, SO_LINGER, (const char *)&linger_,\r\n             sizeof(linger_));\r\n}\r\n\r\nvoid shutDownWR(int fd) {\r\n  shutdown(fd, SHUT_WR);\r\n  // printf(\"shutdown\\n\");\r\n}\r\n\r\nint socket_bind_listen(int port) {\r\n  // 检查port值，取正确区间范围\r\n  if (port < 0 || port > 65535) return -1;\r\n\r\n  // 创建socket(IPv4 + TCP)，返回监听描述符\r\n  int listen_fd = 0;\r\n  if ((listen_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) return -1;\r\n\r\n  // 消除bind时\"Address already in use\"错误\r\n  int optval = 1;\r\n  if (setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &optval,\r\n                 sizeof(optval)) == -1) {\r\n    close(listen_fd);\r\n    return -1;\r\n  }\r\n\r\n  // 设置服务器IP和Port，和监听描述副绑定\r\n  struct sockaddr_in server_addr;\r\n  bzero((char *)&server_addr, sizeof(server_addr));\r\n  server_addr.sin_family = AF_INET;\r\n  server_addr.sin_addr.s_addr = htonl(INADDR_ANY);\r\n  server_addr.sin_port = htons((unsigned short)port);\r\n  if (bind(listen_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) ==\r\n      -1) {\r\n    close(listen_fd);\r\n    return -1;\r\n  }\r\n\r\n  // 开始监听，最大等待队列长为LISTENQ\r\n  if (listen(listen_fd, 2048) == -1) {\r\n    close(listen_fd);\r\n    return -1;\r\n  }\r\n\r\n  // 无效监听描述符\r\n  if (listen_fd == -1) {\r\n    close(listen_fd);\r\n    return -1;\r\n  }\r\n  return listen_fd;\r\n}"
  },
  {
    "path": "WebServer/Util.h",
    "content": "// @Author Lin Ya\r\n// @Email xxbbb@vip.qq.com\r\n#pragma once\r\n#include <cstdlib>\r\n#include <string>\r\n\r\nssize_t readn(int fd, void *buff, size_t n);\r\nssize_t readn(int fd, std::string &inBuffer, bool &zero);\r\nssize_t readn(int fd, std::string &inBuffer);\r\nssize_t writen(int fd, void *buff, size_t n);\r\nssize_t writen(int fd, std::string &sbuff);\r\nvoid handle_for_sigpipe();\r\nint setSocketNonBlocking(int fd);\r\nvoid setSocketNodelay(int fd);\r\nvoid setSocketNoLinger(int fd);\r\nvoid shutDownWR(int fd);\r\nint socket_bind_listen(int port);"
  },
  {
    "path": "WebServer/base/AsyncLogging.cpp",
    "content": "// @Author Lin Ya\r\n// @Email xxbbb@vip.qq.com\r\n#include \"AsyncLogging.h\"\r\n#include <assert.h>\r\n#include <stdio.h>\r\n#include <unistd.h>\r\n#include <functional>\r\n#include \"LogFile.h\"\r\n\r\nAsyncLogging::AsyncLogging(std::string logFileName_, int flushInterval)\r\n    : flushInterval_(flushInterval),\r\n      running_(false),\r\n      basename_(logFileName_),\r\n      thread_(std::bind(&AsyncLogging::threadFunc, this), \"Logging\"),\r\n      mutex_(),\r\n      cond_(mutex_),\r\n      currentBuffer_(new Buffer),\r\n      nextBuffer_(new Buffer),\r\n      buffers_(),\r\n      latch_(1) {\r\n  assert(logFileName_.size() > 1);\r\n  currentBuffer_->bzero();\r\n  nextBuffer_->bzero();\r\n  buffers_.reserve(16);\r\n}\r\n\r\nvoid AsyncLogging::append(const char* logline, int len) {\r\n  MutexLockGuard lock(mutex_);\r\n  if (currentBuffer_->avail() > len)\r\n    currentBuffer_->append(logline, len);\r\n  else {\r\n    buffers_.push_back(currentBuffer_);\r\n    currentBuffer_.reset();\r\n    if (nextBuffer_)\r\n      currentBuffer_ = std::move(nextBuffer_);\r\n    else\r\n      currentBuffer_.reset(new Buffer);\r\n    currentBuffer_->append(logline, len);\r\n    cond_.notify();\r\n  }\r\n}\r\n\r\nvoid AsyncLogging::threadFunc() {\r\n  assert(running_ == true);\r\n  latch_.countDown();\r\n  LogFile output(basename_);\r\n  BufferPtr newBuffer1(new Buffer);\r\n  BufferPtr newBuffer2(new Buffer);\r\n  newBuffer1->bzero();\r\n  newBuffer2->bzero();\r\n  BufferVector buffersToWrite;\r\n  buffersToWrite.reserve(16);\r\n  while (running_) {\r\n    assert(newBuffer1 && newBuffer1->length() == 0);\r\n    assert(newBuffer2 && newBuffer2->length() == 0);\r\n    assert(buffersToWrite.empty());\r\n\r\n    {\r\n      MutexLockGuard lock(mutex_);\r\n      if (buffers_.empty())  // unusual usage!\r\n      {\r\n        cond_.waitForSeconds(flushInterval_);\r\n      }\r\n      buffers_.push_back(currentBuffer_);\r\n      currentBuffer_.reset();\r\n\r\n      currentBuffer_ = std::move(newBuffer1);\r\n      buffersToWrite.swap(buffers_);\r\n      if (!nextBuffer_) {\r\n        nextBuffer_ = std::move(newBuffer2);\r\n      }\r\n    }\r\n\r\n    assert(!buffersToWrite.empty());\r\n\r\n    if (buffersToWrite.size() > 25) {\r\n      // char buf[256];\r\n      // snprintf(buf, sizeof buf, \"Dropped log messages at %s, %zd larger\r\n      // buffers\\n\",\r\n      //          Timestamp::now().toFormattedString().c_str(),\r\n      //          buffersToWrite.size()-2);\r\n      // fputs(buf, stderr);\r\n      // output.append(buf, static_cast<int>(strlen(buf)));\r\n      buffersToWrite.erase(buffersToWrite.begin() + 2, buffersToWrite.end());\r\n    }\r\n\r\n    for (size_t i = 0; i < buffersToWrite.size(); ++i) {\r\n      // FIXME: use unbuffered stdio FILE ? or use ::writev ?\r\n      output.append(buffersToWrite[i]->data(), buffersToWrite[i]->length());\r\n    }\r\n\r\n    if (buffersToWrite.size() > 2) {\r\n      // drop non-bzero-ed buffers, avoid trashing\r\n      buffersToWrite.resize(2);\r\n    }\r\n\r\n    if (!newBuffer1) {\r\n      assert(!buffersToWrite.empty());\r\n      newBuffer1 = buffersToWrite.back();\r\n      buffersToWrite.pop_back();\r\n      newBuffer1->reset();\r\n    }\r\n\r\n    if (!newBuffer2) {\r\n      assert(!buffersToWrite.empty());\r\n      newBuffer2 = buffersToWrite.back();\r\n      buffersToWrite.pop_back();\r\n      newBuffer2->reset();\r\n    }\r\n\r\n    buffersToWrite.clear();\r\n    output.flush();\r\n  }\r\n  output.flush();\r\n}\r\n"
  },
  {
    "path": "WebServer/base/AsyncLogging.h",
    "content": "// @Author Lin Ya\r\n// @Email xxbbb@vip.qq.com\r\n#pragma once\r\n#include <functional>\r\n#include <string>\r\n#include <vector>\r\n#include \"CountDownLatch.h\"\r\n#include \"LogStream.h\"\r\n#include \"MutexLock.h\"\r\n#include \"Thread.h\"\r\n#include \"noncopyable.h\"\r\n\r\n\r\nclass AsyncLogging : noncopyable {\r\n public:\r\n  AsyncLogging(const std::string basename, int flushInterval = 2);\r\n  ~AsyncLogging() {\r\n    if (running_) stop();\r\n  }\r\n  void append(const char* logline, int len);\r\n\r\n  void start() {\r\n    running_ = true;\r\n    thread_.start();\r\n    latch_.wait();\r\n  }\r\n\r\n  void stop() {\r\n    running_ = false;\r\n    cond_.notify();\r\n    thread_.join();\r\n  }\r\n\r\n private:\r\n  void threadFunc();\r\n  typedef FixedBuffer<kLargeBuffer> Buffer;\r\n  typedef std::vector<std::shared_ptr<Buffer>> BufferVector;\r\n  typedef std::shared_ptr<Buffer> BufferPtr;\r\n  const int flushInterval_;\r\n  bool running_;\r\n  std::string basename_;\r\n  Thread thread_;\r\n  MutexLock mutex_;\r\n  Condition cond_;\r\n  BufferPtr currentBuffer_;\r\n  BufferPtr nextBuffer_;\r\n  BufferVector buffers_;\r\n  CountDownLatch latch_;\r\n};"
  },
  {
    "path": "WebServer/base/CMakeLists.txt",
    "content": "set(LIB_SRC\n    AsyncLogging.cpp\n    CountDownLatch.cpp\n    FileUtil.cpp\n    LogFile.cpp\n    Logging.cpp\n    LogStream.cpp\n    Thread.cpp\n)\n\nadd_library(libserver_base ${LIB_SRC})\ntarget_link_libraries(libserver_base pthread rt)\n\nset_target_properties(libserver_base PROPERTIES OUTPUT_NAME \"server_base\")\n\nadd_subdirectory(tests)"
  },
  {
    "path": "WebServer/base/Condition.h",
    "content": "// @Author Lin Ya\r\n// @Email xxbbb@vip.qq.com\r\n#pragma once\r\n#include <errno.h>\r\n#include <pthread.h>\r\n#include <pthread.h>\r\n#include <time.h>\r\n#include <cstdint>\r\n#include \"MutexLock.h\"\r\n#include \"noncopyable.h\"\r\n\r\n\r\nclass Condition : noncopyable {\r\n public:\r\n  explicit Condition(MutexLock &_mutex) : mutex(_mutex) {\r\n    pthread_cond_init(&cond, NULL);\r\n  }\r\n  ~Condition() { pthread_cond_destroy(&cond); }\r\n  void wait() { pthread_cond_wait(&cond, mutex.get()); }\r\n  void notify() { pthread_cond_signal(&cond); }\r\n  void notifyAll() { pthread_cond_broadcast(&cond); }\r\n  bool waitForSeconds(int seconds) {\r\n    struct timespec abstime;\r\n    clock_gettime(CLOCK_REALTIME, &abstime);\r\n    abstime.tv_sec += static_cast<time_t>(seconds);\r\n    return ETIMEDOUT == pthread_cond_timedwait(&cond, mutex.get(), &abstime);\r\n  }\r\n\r\n private:\r\n  MutexLock &mutex;\r\n  pthread_cond_t cond;\r\n};"
  },
  {
    "path": "WebServer/base/CountDownLatch.cpp",
    "content": "// @Author Lin Ya\r\n// @Email xxbbb@vip.qq.com\r\n#include \"CountDownLatch.h\"\r\n\r\nCountDownLatch::CountDownLatch(int count)\r\n    : mutex_(), condition_(mutex_), count_(count) {}\r\n\r\nvoid CountDownLatch::wait() {\r\n  MutexLockGuard lock(mutex_);\r\n  while (count_ > 0) condition_.wait();\r\n}\r\n\r\nvoid CountDownLatch::countDown() {\r\n  MutexLockGuard lock(mutex_);\r\n  --count_;\r\n  if (count_ == 0) condition_.notifyAll();\r\n}"
  },
  {
    "path": "WebServer/base/CountDownLatch.h",
    "content": "// @Author Lin Ya\r\n// @Email xxbbb@vip.qq.com\r\n#pragma once\r\n#include \"Condition.h\"\r\n#include \"MutexLock.h\"\r\n#include \"noncopyable.h\"\r\n\r\n// CountDownLatch的主要作用是确保Thread中传进去的func真的启动了以后\r\n// 外层的start才返回\r\nclass CountDownLatch : noncopyable {\r\n public:\r\n  explicit CountDownLatch(int count);\r\n  void wait();\r\n  void countDown();\r\n\r\n private:\r\n  mutable MutexLock mutex_;\r\n  Condition condition_;\r\n  int count_;\r\n};"
  },
  {
    "path": "WebServer/base/CurrentThread.h",
    "content": "// @Author Lin Ya\r\n// @Email xxbbb@vip.qq.com\r\n#pragma once\r\n#include <stdint.h>\r\n\r\nnamespace CurrentThread {\r\n// internal\r\nextern __thread int t_cachedTid;\r\nextern __thread char t_tidString[32];\r\nextern __thread int t_tidStringLength;\r\nextern __thread const char* t_threadName;\r\nvoid cacheTid();\r\ninline int tid() {\r\n  if (__builtin_expect(t_cachedTid == 0, 0)) {\r\n    cacheTid();\r\n  }\r\n  return t_cachedTid;\r\n}\r\n\r\ninline const char* tidString()  // for logging\r\n{\r\n  return t_tidString;\r\n}\r\n\r\ninline int tidStringLength()  // for logging\r\n{\r\n  return t_tidStringLength;\r\n}\r\n\r\ninline const char* name() { return t_threadName; }\r\n}\r\n"
  },
  {
    "path": "WebServer/base/FileUtil.cpp",
    "content": "// @Author Lin Ya\r\n// @Email xxbbb@vip.qq.com\r\n#include \"FileUtil.h\"\r\n#include <assert.h>\r\n#include <errno.h>\r\n#include <fcntl.h>\r\n#include <stdio.h>\r\n#include <sys/stat.h>\r\n#include <unistd.h>\r\n\r\nusing namespace std;\r\n\r\nAppendFile::AppendFile(string filename) : fp_(fopen(filename.c_str(), \"ae\")) {\r\n  // 用户提供缓冲区\r\n  setbuffer(fp_, buffer_, sizeof buffer_);\r\n}\r\n\r\nAppendFile::~AppendFile() { fclose(fp_); }\r\n\r\nvoid AppendFile::append(const char* logline, const size_t len) {\r\n  size_t n = this->write(logline, len);\r\n  size_t remain = len - n;\r\n  while (remain > 0) {\r\n    size_t x = this->write(logline + n, remain);\r\n    if (x == 0) {\r\n      int err = ferror(fp_);\r\n      if (err) fprintf(stderr, \"AppendFile::append() failed !\\n\");\r\n      break;\r\n    }\r\n    n += x;\r\n    remain = len - n;\r\n  }\r\n}\r\n\r\nvoid AppendFile::flush() { fflush(fp_); }\r\n\r\nsize_t AppendFile::write(const char* logline, size_t len) {\r\n  return fwrite_unlocked(logline, 1, len, fp_);\r\n}"
  },
  {
    "path": "WebServer/base/FileUtil.h",
    "content": "// @Author Lin Ya\r\n// @Email xxbbb@vip.qq.com\r\n#pragma once\r\n#include <string>\r\n#include \"noncopyable.h\"\r\n\r\n\r\nclass AppendFile : noncopyable {\r\n public:\r\n  explicit AppendFile(std::string filename);\r\n  ~AppendFile();\r\n  // append 会向文件写\r\n  void append(const char *logline, const size_t len);\r\n  void flush();\r\n\r\n private:\r\n  size_t write(const char *logline, size_t len);\r\n  FILE *fp_;\r\n  char buffer_[64 * 1024];\r\n};"
  },
  {
    "path": "WebServer/base/LogFile.cpp",
    "content": "// @Author Lin Ya\r\n// @Email xxbbb@vip.qq.com\r\n#include \"LogFile.h\"\r\n#include <assert.h>\r\n#include <stdio.h>\r\n#include <time.h>\r\n#include \"FileUtil.h\"\r\n\r\n\r\nusing namespace std;\r\n\r\nLogFile::LogFile(const string& basename, int flushEveryN)\r\n    : basename_(basename),\r\n      flushEveryN_(flushEveryN),\r\n      count_(0),\r\n      mutex_(new MutexLock) {\r\n  // assert(basename.find('/') >= 0);\r\n  file_.reset(new AppendFile(basename));\r\n}\r\n\r\nLogFile::~LogFile() {}\r\n\r\nvoid LogFile::append(const char* logline, int len) {\r\n  MutexLockGuard lock(*mutex_);\r\n  append_unlocked(logline, len);\r\n}\r\n\r\nvoid LogFile::flush() {\r\n  MutexLockGuard lock(*mutex_);\r\n  file_->flush();\r\n}\r\n\r\nvoid LogFile::append_unlocked(const char* logline, int len) {\r\n  file_->append(logline, len);\r\n  ++count_;\r\n  if (count_ >= flushEveryN_) {\r\n    count_ = 0;\r\n    file_->flush();\r\n  }\r\n}"
  },
  {
    "path": "WebServer/base/LogFile.h",
    "content": "// @Author Lin Ya\r\n// @Email xxbbb@vip.qq.com\r\n#pragma once\r\n#include <memory>\r\n#include <string>\r\n#include \"FileUtil.h\"\r\n#include \"MutexLock.h\"\r\n#include \"noncopyable.h\"\r\n\r\n\r\n// TODO 提供自动归档功能\r\nclass LogFile : noncopyable {\r\n public:\r\n  // 每被append flushEveryN次，flush一下，会往文件写，只不过，文件也是带缓冲区的\r\n  LogFile(const std::string& basename, int flushEveryN = 1024);\r\n  ~LogFile();\r\n\r\n  void append(const char* logline, int len);\r\n  void flush();\r\n  bool rollFile();\r\n\r\n private:\r\n  void append_unlocked(const char* logline, int len);\r\n\r\n  const std::string basename_;\r\n  const int flushEveryN_;\r\n\r\n  int count_;\r\n  std::unique_ptr<MutexLock> mutex_;\r\n  std::unique_ptr<AppendFile> file_;\r\n};"
  },
  {
    "path": "WebServer/base/LogStream.cpp",
    "content": "// @Author Lin Ya\r\n// @Email xxbbb@vip.qq.com\r\n#include \"LogStream.h\"\r\n#include <assert.h>\r\n#include <stdint.h>\r\n#include <stdio.h>\r\n#include <string.h>\r\n#include <algorithm>\r\n#include <limits>\r\n\r\nconst char digits[] = \"9876543210123456789\";\r\nconst char* zero = digits + 9;\r\n\r\n// From muduo\r\ntemplate <typename T>\r\nsize_t convert(char buf[], T value) {\r\n  T i = value;\r\n  char* p = buf;\r\n\r\n  do {\r\n    int lsd = static_cast<int>(i % 10);\r\n    i /= 10;\r\n    *p++ = zero[lsd];\r\n  } while (i != 0);\r\n\r\n  if (value < 0) {\r\n    *p++ = '-';\r\n  }\r\n  *p = '\\0';\r\n  std::reverse(buf, p);\r\n\r\n  return p - buf;\r\n}\r\n\r\ntemplate class FixedBuffer<kSmallBuffer>;\r\ntemplate class FixedBuffer<kLargeBuffer>;\r\n\r\ntemplate <typename T>\r\nvoid LogStream::formatInteger(T v) {\r\n  // buffer容不下kMaxNumericSize个字符的话会被直接丢弃\r\n  if (buffer_.avail() >= kMaxNumericSize) {\r\n    size_t len = convert(buffer_.current(), v);\r\n    buffer_.add(len);\r\n  }\r\n}\r\n\r\nLogStream& LogStream::operator<<(short v) {\r\n  *this << static_cast<int>(v);\r\n  return *this;\r\n}\r\n\r\nLogStream& LogStream::operator<<(unsigned short v) {\r\n  *this << static_cast<unsigned int>(v);\r\n  return *this;\r\n}\r\n\r\nLogStream& LogStream::operator<<(int v) {\r\n  formatInteger(v);\r\n  return *this;\r\n}\r\n\r\nLogStream& LogStream::operator<<(unsigned int v) {\r\n  formatInteger(v);\r\n  return *this;\r\n}\r\n\r\nLogStream& LogStream::operator<<(long v) {\r\n  formatInteger(v);\r\n  return *this;\r\n}\r\n\r\nLogStream& LogStream::operator<<(unsigned long v) {\r\n  formatInteger(v);\r\n  return *this;\r\n}\r\n\r\nLogStream& LogStream::operator<<(long long v) {\r\n  formatInteger(v);\r\n  return *this;\r\n}\r\n\r\nLogStream& LogStream::operator<<(unsigned long long v) {\r\n  formatInteger(v);\r\n  return *this;\r\n}\r\n\r\nLogStream& LogStream::operator<<(double v) {\r\n  if (buffer_.avail() >= kMaxNumericSize) {\r\n    int len = snprintf(buffer_.current(), kMaxNumericSize, \"%.12g\", v);\r\n    buffer_.add(len);\r\n  }\r\n  return *this;\r\n}\r\n\r\nLogStream& LogStream::operator<<(long double v) {\r\n  if (buffer_.avail() >= kMaxNumericSize) {\r\n    int len = snprintf(buffer_.current(), kMaxNumericSize, \"%.12Lg\", v);\r\n    buffer_.add(len);\r\n  }\r\n  return *this;\r\n}"
  },
  {
    "path": "WebServer/base/LogStream.h",
    "content": "// @Author Lin Ya\r\n// @Email xxbbb@vip.qq.com\r\n#pragma once\r\n#include <assert.h>\r\n#include <string.h>\r\n#include <string>\r\n#include \"noncopyable.h\"\r\n\r\nclass AsyncLogging;\r\nconst int kSmallBuffer = 4000;\r\nconst int kLargeBuffer = 4000 * 1000;\r\n\r\ntemplate <int SIZE>\r\nclass FixedBuffer : noncopyable {\r\n public:\r\n  FixedBuffer() : cur_(data_) {}\r\n\r\n  ~FixedBuffer() {}\r\n\r\n  void append(const char* buf, size_t len) {\r\n    if (avail() > static_cast<int>(len)) {\r\n      memcpy(cur_, buf, len);\r\n      cur_ += len;\r\n    }\r\n  }\r\n\r\n  const char* data() const { return data_; }\r\n  int length() const { return static_cast<int>(cur_ - data_); }\r\n\r\n  char* current() { return cur_; }\r\n  int avail() const { return static_cast<int>(end() - cur_); }\r\n  void add(size_t len) { cur_ += len; }\r\n\r\n  void reset() { cur_ = data_; }\r\n  void bzero() { memset(data_, 0, sizeof data_); }\r\n\r\n private:\r\n  const char* end() const { return data_ + sizeof data_; }\r\n\r\n  char data_[SIZE];\r\n  char* cur_;\r\n};\r\n\r\nclass LogStream : noncopyable {\r\n public:\r\n  typedef FixedBuffer<kSmallBuffer> Buffer;\r\n\r\n  LogStream& operator<<(bool v) {\r\n    buffer_.append(v ? \"1\" : \"0\", 1);\r\n    return *this;\r\n  }\r\n\r\n  LogStream& operator<<(short);\r\n  LogStream& operator<<(unsigned short);\r\n  LogStream& operator<<(int);\r\n  LogStream& operator<<(unsigned int);\r\n  LogStream& operator<<(long);\r\n  LogStream& operator<<(unsigned long);\r\n  LogStream& operator<<(long long);\r\n  LogStream& operator<<(unsigned long long);\r\n\r\n  LogStream& operator<<(const void*);\r\n\r\n  LogStream& operator<<(float v) {\r\n    *this << static_cast<double>(v);\r\n    return *this;\r\n  }\r\n  LogStream& operator<<(double);\r\n  LogStream& operator<<(long double);\r\n\r\n  LogStream& operator<<(char v) {\r\n    buffer_.append(&v, 1);\r\n    return *this;\r\n  }\r\n\r\n  LogStream& operator<<(const char* str) {\r\n    if (str)\r\n      buffer_.append(str, strlen(str));\r\n    else\r\n      buffer_.append(\"(null)\", 6);\r\n    return *this;\r\n  }\r\n\r\n  LogStream& operator<<(const unsigned char* str) {\r\n    return operator<<(reinterpret_cast<const char*>(str));\r\n  }\r\n\r\n  LogStream& operator<<(const std::string& v) {\r\n    buffer_.append(v.c_str(), v.size());\r\n    return *this;\r\n  }\r\n\r\n  void append(const char* data, int len) { buffer_.append(data, len); }\r\n  const Buffer& buffer() const { return buffer_; }\r\n  void resetBuffer() { buffer_.reset(); }\r\n\r\n private:\r\n  void staticCheck();\r\n\r\n  template <typename T>\r\n  void formatInteger(T);\r\n\r\n  Buffer buffer_;\r\n\r\n  static const int kMaxNumericSize = 32;\r\n};"
  },
  {
    "path": "WebServer/base/Logging.cpp",
    "content": "// @Author Lin Ya\r\n// @Email xxbbb@vip.qq.com\r\n#include \"Logging.h\"\r\n#include \"CurrentThread.h\"\r\n#include \"Thread.h\"\r\n#include \"AsyncLogging.h\"\r\n#include <assert.h>\r\n#include <iostream>\r\n#include <time.h>  \r\n#include <sys/time.h> \r\n\r\n\r\nstatic pthread_once_t once_control_ = PTHREAD_ONCE_INIT;\r\nstatic AsyncLogging *AsyncLogger_;\r\n\r\nstd::string Logger::logFileName_ = \"./WebServer.log\";\r\n\r\nvoid once_init()\r\n{\r\n    AsyncLogger_ = new AsyncLogging(Logger::getLogFileName());\r\n    AsyncLogger_->start(); \r\n}\r\n\r\nvoid output(const char* msg, int len)\r\n{\r\n    pthread_once(&once_control_, once_init);\r\n    AsyncLogger_->append(msg, len);\r\n}\r\n\r\nLogger::Impl::Impl(const char *fileName, int line)\r\n  : stream_(),\r\n    line_(line),\r\n    basename_(fileName)\r\n{\r\n    formatTime();\r\n}\r\n\r\nvoid Logger::Impl::formatTime()\r\n{\r\n    struct timeval tv;\r\n    time_t time;\r\n    char str_t[26] = {0};\r\n    gettimeofday (&tv, NULL);\r\n    time = tv.tv_sec;\r\n    struct tm* p_time = localtime(&time);   \r\n    strftime(str_t, 26, \"%Y-%m-%d %H:%M:%S\\n\", p_time);\r\n    stream_ << str_t;\r\n}\r\n\r\nLogger::Logger(const char *fileName, int line)\r\n  : impl_(fileName, line)\r\n{ }\r\n\r\nLogger::~Logger()\r\n{\r\n    impl_.stream_ << \" -- \" << impl_.basename_ << ':' << impl_.line_ << '\\n';\r\n    const LogStream::Buffer& buf(stream().buffer());\r\n    output(buf.data(), buf.length());\r\n}"
  },
  {
    "path": "WebServer/base/Logging.h",
    "content": "// @Author Lin Ya\r\n// @Email xxbbb@vip.qq.com\r\n#pragma once\r\n#include <pthread.h>\r\n#include <stdio.h>\r\n#include <string.h>\r\n#include <string>\r\n#include \"LogStream.h\"\r\n\r\n\r\nclass AsyncLogging;\r\n\r\nclass Logger {\r\n public:\r\n  Logger(const char *fileName, int line);\r\n  ~Logger();\r\n  LogStream &stream() { return impl_.stream_; }\r\n\r\n  static void setLogFileName(std::string fileName) { logFileName_ = fileName; }\r\n  static std::string getLogFileName() { return logFileName_; }\r\n\r\n private:\r\n  class Impl {\r\n   public:\r\n    Impl(const char *fileName, int line);\r\n    void formatTime();\r\n\r\n    LogStream stream_;\r\n    int line_;\r\n    std::string basename_;\r\n  };\r\n  Impl impl_;\r\n  static std::string logFileName_;\r\n};\r\n\r\n#define LOG Logger(__FILE__, __LINE__).stream()"
  },
  {
    "path": "WebServer/base/Log的设计.txt",
    "content": "Log的设计仿照了muduo库的设计,但我写的没那么复杂\r\nhttps://github.com/chenshuo/muduo\r\n\r\n与Log相关的类包括FileUtil、LogFile、AsyncLogging、LogStream、Logging。\r\n其中前4个类每一个类都含有一个append函数，Log的设计也是主要围绕这个append函数展开的。\r\n\r\nFileUtil是最底层的文件类，封装了Log文件的打开、写入并在类析构的时候关闭文件，底层使用了标准IO，该append函数直接向文件写。\r\nLogFile进一步封装了FileUtil，并设置了一个循环次数，没过这么多次就flush一次。\r\nAsyncLogging是核心，它负责启动一个log线程，专门用来将log写入LogFile，应用了“双缓冲技术”，其实有4个以上的缓冲区，但思想是一样的。\r\nAsyncLogging负责(定时到或被填满时)将缓冲区中的数据写入LogFile中。\r\nLogStream主要用来格式化输出，重载了<<运算符，同时也有自己的一块缓冲区，这里缓冲区的存在是为了缓存一行，把多个<<的结果连成一块。\r\nLogging是对外接口，Logging类内涵一个LogStream对象，主要是为了每次打log的时候在log之前和之后加上固定的格式化的信息，比如打log的行、\r\n文件名等信息。\r\n"
  },
  {
    "path": "WebServer/base/MutexLock.h",
    "content": "// @Author Lin Ya\r\n// @Email xxbbb@vip.qq.com\r\n#pragma once\r\n#include <pthread.h>\r\n#include <cstdio>\r\n#include \"noncopyable.h\"\r\n\r\n\r\nclass MutexLock : noncopyable {\r\n public:\r\n  MutexLock() { pthread_mutex_init(&mutex, NULL); }\r\n  ~MutexLock() {\r\n    pthread_mutex_lock(&mutex);\r\n    pthread_mutex_destroy(&mutex);\r\n  }\r\n  void lock() { pthread_mutex_lock(&mutex); }\r\n  void unlock() { pthread_mutex_unlock(&mutex); }\r\n  pthread_mutex_t *get() { return &mutex; }\r\n\r\n private:\r\n  pthread_mutex_t mutex;\r\n\r\n  // 友元类不受访问权限影响\r\n private:\r\n  friend class Condition;\r\n};\r\n\r\nclass MutexLockGuard : noncopyable {\r\n public:\r\n  explicit MutexLockGuard(MutexLock &_mutex) : mutex(_mutex) { mutex.lock(); }\r\n  ~MutexLockGuard() { mutex.unlock(); }\r\n\r\n private:\r\n  MutexLock &mutex;\r\n};"
  },
  {
    "path": "WebServer/base/Thread.cpp",
    "content": "// @Author Lin Ya\r\n// @Email xxbbb@vip.qq.com\r\n#include \"Thread.h\"\r\n#include <assert.h>\r\n#include <errno.h>\r\n#include <linux/unistd.h>\r\n#include <stdint.h>\r\n#include <stdio.h>\r\n#include <sys/prctl.h>\r\n#include <sys/types.h>\r\n#include <unistd.h>\r\n#include <memory>\r\n#include \"CurrentThread.h\"\r\n\r\n\r\n#include <iostream>\r\nusing namespace std;\r\n\r\nnamespace CurrentThread {\r\n__thread int t_cachedTid = 0;\r\n__thread char t_tidString[32];\r\n__thread int t_tidStringLength = 6;\r\n__thread const char* t_threadName = \"default\";\r\n}\r\n\r\npid_t gettid() { return static_cast<pid_t>(::syscall(SYS_gettid)); }\r\n\r\nvoid CurrentThread::cacheTid() {\r\n  if (t_cachedTid == 0) {\r\n    t_cachedTid = gettid();\r\n    t_tidStringLength =\r\n        snprintf(t_tidString, sizeof t_tidString, \"%5d \", t_cachedTid);\r\n  }\r\n}\r\n\r\n// 为了在线程中保留name,tid这些数据\r\nstruct ThreadData {\r\n  typedef Thread::ThreadFunc ThreadFunc;\r\n  ThreadFunc func_;\r\n  string name_;\r\n  pid_t* tid_;\r\n  CountDownLatch* latch_;\r\n\r\n  ThreadData(const ThreadFunc& func, const string& name, pid_t* tid,\r\n             CountDownLatch* latch)\r\n      : func_(func), name_(name), tid_(tid), latch_(latch) {}\r\n\r\n  void runInThread() {\r\n    *tid_ = CurrentThread::tid();\r\n    tid_ = NULL;\r\n    latch_->countDown();\r\n    latch_ = NULL;\r\n\r\n    CurrentThread::t_threadName = name_.empty() ? \"Thread\" : name_.c_str();\r\n    prctl(PR_SET_NAME, CurrentThread::t_threadName);\r\n\r\n    func_();\r\n    CurrentThread::t_threadName = \"finished\";\r\n  }\r\n};\r\n\r\nvoid* startThread(void* obj) {\r\n  ThreadData* data = static_cast<ThreadData*>(obj);\r\n  data->runInThread();\r\n  delete data;\r\n  return NULL;\r\n}\r\n\r\nThread::Thread(const ThreadFunc& func, const string& n)\r\n    : started_(false),\r\n      joined_(false),\r\n      pthreadId_(0),\r\n      tid_(0),\r\n      func_(func),\r\n      name_(n),\r\n      latch_(1) {\r\n  setDefaultName();\r\n}\r\n\r\nThread::~Thread() {\r\n  if (started_ && !joined_) pthread_detach(pthreadId_);\r\n}\r\n\r\nvoid Thread::setDefaultName() {\r\n  if (name_.empty()) {\r\n    char buf[32];\r\n    snprintf(buf, sizeof buf, \"Thread\");\r\n    name_ = buf;\r\n  }\r\n}\r\n\r\nvoid Thread::start() {\r\n  assert(!started_);\r\n  started_ = true;\r\n  ThreadData* data = new ThreadData(func_, name_, &tid_, &latch_);\r\n  if (pthread_create(&pthreadId_, NULL, &startThread, data)) {\r\n    started_ = false;\r\n    delete data;\r\n  } else {\r\n    latch_.wait();\r\n    assert(tid_ > 0);\r\n  }\r\n}\r\n\r\nint Thread::join() {\r\n  assert(started_);\r\n  assert(!joined_);\r\n  joined_ = true;\r\n  return pthread_join(pthreadId_, NULL);\r\n}"
  },
  {
    "path": "WebServer/base/Thread.h",
    "content": "// @Author Lin Ya\r\n// @Email xxbbb@vip.qq.com\r\n#pragma once\r\n#include <pthread.h>\r\n#include <sys/syscall.h>\r\n#include <unistd.h>\r\n#include <functional>\r\n#include <memory>\r\n#include <string>\r\n#include \"CountDownLatch.h\"\r\n#include \"noncopyable.h\"\r\n\r\nclass Thread : noncopyable {\r\n public:\r\n  typedef std::function<void()> ThreadFunc;\r\n  explicit Thread(const ThreadFunc&, const std::string& name = std::string());\r\n  ~Thread();\r\n  void start();\r\n  int join();\r\n  bool started() const { return started_; }\r\n  pid_t tid() const { return tid_; }\r\n  const std::string& name() const { return name_; }\r\n\r\n private:\r\n  void setDefaultName();\r\n  bool started_;\r\n  bool joined_;\r\n  pthread_t pthreadId_;\r\n  pid_t tid_;\r\n  ThreadFunc func_;\r\n  std::string name_;\r\n  CountDownLatch latch_;\r\n};"
  },
  {
    "path": "WebServer/base/noncopyable.h",
    "content": "// @Author Lin Ya\r\n// @Email xxbbb@vip.qq.com\r\n#pragma once\r\n\r\nclass noncopyable {\r\n protected:\r\n  noncopyable() {}\r\n  ~noncopyable() {}\r\n\r\n private:\r\n  noncopyable(const noncopyable&);\r\n  const noncopyable& operator=(const noncopyable&);\r\n};"
  },
  {
    "path": "WebServer/base/tests/CMakeLists.txt",
    "content": "add_executable(LoggingTest LoggingTest.cpp)\ntarget_link_libraries(LoggingTest libserver_base)"
  },
  {
    "path": "WebServer/base/tests/LoggingTest.cpp",
    "content": "// @Author Lin Ya\r\n// @Email xxbbb@vip.qq.com\r\n#include \"../Logging.h\"\r\n#include \"../Thread.h\"\r\n#include <string>\r\n#include <unistd.h>\r\n#include <vector>\r\n#include <memory>\r\n#include <iostream>\r\nusing namespace std;\r\n\r\nvoid threadFunc()\r\n{\r\n    for (int i = 0; i < 100000; ++i)\r\n    {\r\n        LOG << i;\r\n    }\r\n}\r\n\r\nvoid type_test()\r\n{\r\n    // 13 lines\r\n    cout << \"----------type test-----------\" << endl;\r\n    LOG << 0;\r\n    LOG << 1234567890123;\r\n    LOG << 1.0f;\r\n    LOG << 3.1415926;\r\n    LOG << (short) 1;\r\n    LOG << (long long) 1;\r\n    LOG << (unsigned int) 1;\r\n    LOG << (unsigned long) 1;\r\n    LOG << (long double) 1.6555556;\r\n    LOG << (unsigned long long) 1;\r\n    LOG << 'c';\r\n    LOG << \"abcdefg\";\r\n    LOG << string(\"This is a string\");\r\n}\r\n\r\nvoid stressing_single_thread()\r\n{\r\n    // 100000 lines\r\n    cout << \"----------stressing test single thread-----------\" << endl;\r\n    for (int i = 0; i < 100000; ++i)\r\n    {\r\n        LOG << i;\r\n    }\r\n}\r\n\r\nvoid stressing_multi_threads(int threadNum = 4)\r\n{\r\n    // threadNum * 100000 lines\r\n    cout << \"----------stressing test multi thread-----------\" << endl;\r\n    vector<shared_ptr<Thread>> vsp;\r\n    for (int i = 0; i < threadNum; ++i)\r\n    {\r\n        shared_ptr<Thread> tmp(new Thread(threadFunc, \"testFunc\"));\r\n        vsp.push_back(tmp);\r\n    }\r\n    for (int i = 0; i < threadNum; ++i)\r\n    {\r\n        vsp[i]->start();\r\n    }\r\n    sleep(3);\r\n}\r\n\r\nvoid other()\r\n{\r\n    // 1 line\r\n    cout << \"----------other test-----------\" << endl;\r\n    LOG << \"fddsa\" << 'c' << 0 << 3.666 << string(\"This is a string\");\r\n}\r\n\r\n\r\nint main()\r\n{\r\n    // 共500014行\r\n    type_test();\r\n    sleep(3);\r\n\r\n    stressing_single_thread();\r\n    sleep(3);\r\n\r\n    other();\r\n    sleep(3);\r\n\r\n    stressing_multi_threads();\r\n    sleep(3);\r\n    return 0;\r\n}"
  },
  {
    "path": "WebServer/config.h",
    "content": "#pragma once\r\n\r\n"
  },
  {
    "path": "WebServer/tests/CMakeLists.txt",
    "content": "add_executable(HTTPClient HTTPClient.cpp)"
  },
  {
    "path": "WebServer/tests/HTTPClient.cpp",
    "content": "#include <arpa/inet.h>\r\n#include <fcntl.h>\r\n#include <netinet/in.h>\r\n#include <stdio.h>\r\n#include <stdlib.h>\r\n#include <string.h>\r\n#include <sys/epoll.h>\r\n#include <sys/socket.h>\r\n#include <sys/types.h>\r\n#include <time.h>\r\n#include <unistd.h>\r\n#include <iostream>\r\n\r\n\r\nusing namespace std;\r\n\r\n#define MAXSIZE 1024\r\n#define IPADDRESS \"127.0.0.1\"\r\n#define SERV_PORT 8888\r\n#define FDSIZE 1024\r\n#define EPOLLEVENTS 20\r\n\r\n// static void handle_connection(int sockfd);\r\n// static void handle_events(int epollfd,struct epoll_event *events,int num,int\r\n// sockfd,char *buf);\r\n// static void do_read(int epollfd,int fd,int sockfd,char *buf);\r\n// static void do_read(int epollfd,int fd,int sockfd,char *buf);\r\n// static void do_write(int epollfd,int fd,int sockfd,char *buf);\r\n// static void add_event(int epollfd,int fd,int state);\r\n// static void delete_event(int epollfd,int fd,int state);\r\n// static void modify_event(int epollfd,int fd,int state);\r\n\r\nint setSocketNonBlocking1(int fd) {\r\n  int flag = fcntl(fd, F_GETFL, 0);\r\n  if (flag == -1) return -1;\r\n\r\n  flag |= O_NONBLOCK;\r\n  if (fcntl(fd, F_SETFL, flag) == -1) return -1;\r\n  return 0;\r\n}\r\nint main(int argc, char *argv[]) {\r\n  int sockfd;\r\n  struct sockaddr_in servaddr;\r\n  sockfd = socket(AF_INET, SOCK_STREAM, 0);\r\n  bzero(&servaddr, sizeof(servaddr));\r\n  servaddr.sin_family = AF_INET;\r\n  servaddr.sin_port = htons(SERV_PORT);\r\n  inet_pton(AF_INET, IPADDRESS, &servaddr.sin_addr);\r\n  char buff[4096];\r\n  buff[0] = '\\0';\r\n  // 发空串\r\n  const char *p = \" \";\r\n  if (connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) == 0) {\r\n    setSocketNonBlocking1(sockfd);\r\n    cout << \"1:\" << endl;\r\n    ssize_t n = write(sockfd, p, strlen(p));\r\n    cout << \"strlen(p) = \" << strlen(p) << endl;\r\n    sleep(1);\r\n    n = read(sockfd, buff, 4096);\r\n    cout << \"n=\" << n << endl;\r\n    printf(\"%s\", buff);\r\n    close(sockfd);\r\n  } else {\r\n    perror(\"err1\");\r\n  }\r\n  sleep(1);\r\n\r\n  // 发\"GET  HTTP/1.1\"\r\n  p = \"GET  HTTP/1.1\";\r\n  sockfd = socket(AF_INET, SOCK_STREAM, 0);\r\n  if (connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) == 0) {\r\n    setSocketNonBlocking1(sockfd);\r\n    cout << \"2:\" << endl;\r\n    ssize_t n = write(sockfd, p, strlen(p));\r\n    cout << \"strlen(p) = \" << strlen(p) << endl;\r\n    sleep(1);\r\n    n = read(sockfd, buff, 4096);\r\n    cout << \"n=\" << n << endl;\r\n    printf(\"%s\", buff);\r\n    close(sockfd);\r\n  } else {\r\n    perror(\"err2\");\r\n  }\r\n  sleep(1);\r\n\r\n  // 发\r\n  // GET  HTTP/1.1\r\n  // Host: 192.168.52.135:8888\r\n  // Content-Type: application/x-www-form-urlencoded\r\n  // Connection: Keep-Alive\r\n  p = \"GET / HTTP/1.1\\r\\nHost: 192.168.52.135:8888\\r\\nContent-Type: \"\r\n      \"application/x-www-form-urlencoded\\r\\nConnection: Keep-Alive\\r\\n\\r\\n\";\r\n  sockfd = socket(AF_INET, SOCK_STREAM, 0);\r\n  if (connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) == 0) {\r\n    setSocketNonBlocking1(sockfd);\r\n    cout << \"3:\" << endl;\r\n    ssize_t n = write(sockfd, p, strlen(p));\r\n    cout << \"strlen(p) = \" << strlen(p) << endl;\r\n    sleep(1);\r\n    n = read(sockfd, buff, 4096);\r\n    cout << \"n=\" << n << endl;\r\n    printf(\"%s\", buff);\r\n    close(sockfd);\r\n  } else {\r\n    perror(\"err3\");\r\n  }\r\n  return 0;\r\n}\r\n\r\n// static void handle_connection(int sockfd)\r\n// {\r\n//     int epollfd;\r\n//     struct epoll_event events[EPOLLEVENTS];\r\n//     char buf[MAXSIZE];\r\n//     int ret;\r\n//     epollfd = epoll_create(FDSIZE);\r\n//     add_event(epollfd,STDIN_FILENO,EPOLLIN);\r\n//     for ( ; ; )\r\n//     {\r\n//         ret = epoll_wait(epollfd,events,EPOLLEVENTS,-1);\r\n//         handle_events(epollfd,events,ret,sockfd,buf);\r\n//     }\r\n//     close(epollfd);\r\n// }\r\n\r\n// static void\r\n// handle_events(int epollfd,struct epoll_event *events,int num,int sockfd,char\r\n// *buf)\r\n// {\r\n//     int fd;\r\n//     int i;\r\n//     for (i = 0;i < num;i++)\r\n//     {\r\n//         fd = events[i].data.fd;\r\n//         if (events[i].events & EPOLLIN)\r\n//             do_read(epollfd,fd,sockfd,buf);\r\n//         else if (events[i].events & EPOLLOUT)\r\n//             do_write(epollfd,fd,sockfd,buf);\r\n//     }\r\n// }\r\n\r\n// static void do_read(int epollfd,int fd,int sockfd,char *buf)\r\n// {\r\n//     int nread;\r\n//     nread = read(fd,buf,MAXSIZE);\r\n//         if (nread == -1)\r\n//     {\r\n//         perror(\"read error:\");\r\n//         close(fd);\r\n//     }\r\n//     else if (nread == 0)\r\n//     {\r\n//         fprintf(stderr,\"server close.\\n\");\r\n//         close(fd);\r\n//     }\r\n//     else\r\n//     {\r\n//         if (fd == STDIN_FILENO)\r\n//             add_event(epollfd,sockfd,EPOLLOUT);\r\n//         else\r\n//         {\r\n//             delete_event(epollfd,sockfd,EPOLLIN);\r\n//             add_event(epollfd,STDOUT_FILENO,EPOLLOUT);\r\n//         }\r\n//     }\r\n// }\r\n\r\n// static void do_write(int epollfd,int fd,int sockfd,char *buf)\r\n// {\r\n//     int nwrite;\r\n//     nwrite = write(fd,buf,strlen(buf));\r\n//     if (nwrite == -1)\r\n//     {\r\n//         perror(\"write error:\");\r\n//         close(fd);\r\n//     }\r\n//     else\r\n//     {\r\n//         if (fd == STDOUT_FILENO)\r\n//             delete_event(epollfd,fd,EPOLLOUT);\r\n//         else\r\n//             modify_event(epollfd,fd,EPOLLIN);\r\n//     }\r\n//     memset(buf,0,MAXSIZE);\r\n// }\r\n\r\n// static void add_event(int epollfd,int fd,int state)\r\n// {\r\n//     struct epoll_event ev;\r\n//     ev.events = state;\r\n//     ev.data.fd = fd;\r\n//     epoll_ctl(epollfd,EPOLL_CTL_ADD,fd,&ev);\r\n// }\r\n\r\n// static void delete_event(int epollfd,int fd,int state)\r\n// {\r\n//     struct epoll_event ev;\r\n//     ev.events = state;\r\n//     ev.data.fd = fd;\r\n//     epoll_ctl(epollfd,EPOLL_CTL_DEL,fd,&ev);\r\n// }\r\n\r\n// static void modify_event(int epollfd,int fd,int state)\r\n// {\r\n//     struct epoll_event ev;\r\n//     ev.events = state;\r\n//     ev.data.fd = fd;\r\n//     epoll_ctl(epollfd,EPOLL_CTL_MOD,fd,&ev);\r\n// }"
  },
  {
    "path": "build.sh",
    "content": "#!/bin/sh\n\nset -x\n\nSOURCE_DIR=`pwd`\nBUILD_DIR=${BUILD_DIR:-../build}\nBUILD_TYPE=${BUILD_TYPE:-Debug}\n\nmkdir -p $BUILD_DIR/$BUILD_TYPE \\\n    && cd $BUILD_DIR/$BUILD_TYPE \\\n    && cmake \\\n            -DCMAKE_BUILD_TYPE=$BUILD_TYPE \\\n            $SOURCE_DIR \\\n    && make $*"
  },
  {
    "path": "old_version/old_version_0.1/Makefile",
    "content": "SOURCE  := $(wildcard *.cpp)\r\nOBJS    := $(patsubst %.c,%.o,$(patsubst %.cpp,%.o,$(SOURCE)))\r\n\r\nTARGET  := myserver\r\nCC      := g++\r\nLIBS    := -lpthread -lopencv_core -lopencv_imgproc -lopencv_highgui\r\nINCLUDE:= -I./usr/local/include/opencv\r\nCFLAGS  := -std=c++11 -g -Wall -O3 $(INCLUDE)\r\nCXXFLAGS:= $(CFLAGS)\r\n\r\n.PHONY : objs clean veryclean rebuild all\r\nall : $(TARGET)\r\nobjs : $(OBJS)\r\nrebuild: veryclean all\r\nclean :\r\n\trm -fr *.o\r\nveryclean : clean\r\n\trm -rf $(TARGET)\r\n\r\n$(TARGET) : $(OBJS)\r\n\t$(CC) $(CXXFLAGS) -o $@ $(OBJS) $(LDFLAGS) $(LIBS)"
  },
  {
    "path": "old_version/old_version_0.1/Makefile1",
    "content": "cc=g++ -std=c++11\r\nmain: main.o requestData.o epoll.o threadpool.o\r\n\t$(cc) -o main main.o requestData.o epoll.o threadpool.o -lpthread -lopencv_core -lopencv_imgproc -lopencv_highgui -lopencv_ml \r\nmain.o: main.cpp\r\n\t$(cc) -I/usr/local/include/opencv -c main.cpp\r\nrequestData.o: requestData.h requestData.cpp\r\n\t$(cc) -I/usr/local/include/opencv -c requestData.cpp\r\nepoll.o: epoll.h epoll.cpp\r\n\t$(cc) -I/usr/local/include/opencv -c epoll.cpp\r\nthreadpool.o: threadpool.h threadpool.cpp\r\n\t$(cc) -I/usr/local/include/opencv -c threadpool.cpp\r\n.PHONY: clean\r\nclean:\r\n\t-rm main *.o\r\n\r\n\r\n\r\n\r\n.PHONY : everything objs clean veryclean rebuild\r\neverything :\r\n\t$(TARGET)\r\nall :\r\n\t$(TARGET)\r\nobjs :\r\n\t$(OBJS)\r\nrebuild:\r\n\tveryclean everything \r\nclean :\r\n\trm -fr *.o\r\nveryclean :\r\n\tclean rm -fr $(TARGET)"
  },
  {
    "path": "old_version/old_version_0.1/epoll.cpp",
    "content": "#include \"epoll.h\"\r\n#include <sys/epoll.h>\r\n#include <errno.h>\r\n#include \"threadpool.h\"\r\n\r\nstruct epoll_event* events;\r\n\r\nint epoll_init()\r\n{\r\n    int epoll_fd = epoll_create(LISTENQ + 1);\r\n    if(epoll_fd == -1)\r\n        return -1;\r\n    //events = (struct epoll_event*)malloc(sizeof(struct epoll_event) * MAXEVENTS);\r\n    events = new epoll_event[MAXEVENTS];\r\n    return epoll_fd;\r\n}\r\n\r\n// 注册新描述符\r\nint epoll_add(int epoll_fd, int fd, void *request, __uint32_t events)\r\n{\r\n    struct epoll_event event;\r\n    event.data.ptr = request;\r\n    event.events = events;\r\n    //printf(\"add to epoll %d\\n\", fd);\r\n    if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &event) < 0)\r\n    {\r\n        perror(\"epoll_add error\");\r\n        return -1;\r\n    }\r\n    return 0;\r\n}\r\n\r\n// 修改描述符状态\r\nint epoll_mod(int epoll_fd, int fd, void *request, __uint32_t events)\r\n{\r\n    struct epoll_event event;\r\n    event.data.ptr = request;\r\n    event.events = events;\r\n    if(epoll_ctl(epoll_fd, EPOLL_CTL_MOD, fd, &event) < 0)\r\n    {\r\n        perror(\"epoll_mod error\");\r\n        return -1;\r\n    } \r\n    return 0;\r\n}\r\n\r\n// 从epoll中删除描述符\r\nint epoll_del(int epoll_fd, int fd, void *request, __uint32_t events)\r\n{\r\n    struct epoll_event event;\r\n    event.data.ptr = request;\r\n    event.events = events;\r\n    if(epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fd, &event) < 0)\r\n    {\r\n        perror(\"epoll_del error\");\r\n        return -1;\r\n    } \r\n    return 0;\r\n}\r\n\r\n// 返回活跃事件数\r\nint my_epoll_wait(int epoll_fd, struct epoll_event* events, int max_events, int timeout)\r\n{\r\n    int ret_count = epoll_wait(epoll_fd, events, max_events, timeout);\r\n    if (ret_count < 0)\r\n    {\r\n        perror(\"epoll wait error\");\r\n    }\r\n    return ret_count;\r\n}"
  },
  {
    "path": "old_version/old_version_0.1/epoll.h",
    "content": "#ifndef EVENTPOLL\r\n#define EVENTPOLL\r\n#include \"requestData.h\"\r\n\r\nconst int MAXEVENTS = 5000;\r\nconst int LISTENQ = 1024;\r\n\r\nint epoll_init();\r\nint epoll_add(int epoll_fd, int fd, void *request, __uint32_t events);\r\nint epoll_mod(int epoll_fd, int fd, void *request, __uint32_t events);\r\nint epoll_del(int epoll_fd, int fd, void *request, __uint32_t events);\r\nint my_epoll_wait(int epoll_fd, struct epoll_event *events, int max_events, int timeout);\r\n\r\n#endif"
  },
  {
    "path": "old_version/old_version_0.1/improvement.txt",
    "content": "1. 指针可以改成shared_ptr，不需要delete。\r\n2. 想法在某些地方写成单例模式。\r\n3. readn 和 writen 改成写到不能写后或读到不能读后加入epoll等待，要记录写到哪或读到哪，维护这样一个状态。比较两者的性能差异。\r\n4. 信号处理部分可以将 epoll_wait 替换为更安全的 epoll_pwait。\r\n\r\n\r\n踩坑：\r\n1. 对EPOLLONESHOT的误解，原以为当epoll_wait监听到相应的事件触发后，epoll会把与事件关联的fd从epoll描述符中禁止掉并且彻底删除，实际上并不会删除，man手册上的解释如下：\r\nEPOLLONESHOT (since Linux 2.6.2)\r\n              Sets the one-shot behavior for the associated file descriptor.\r\n              This means that after an event is pulled out with\r\n              epoll_wait(2) the associated file descriptor is internally\r\n              disabled and no other events will be reported by the epoll\r\n              interface.  The user must call epoll_ctl() with EPOLL_CTL_MOD\r\n              to rearm the file descriptor with a new event mask.\r\n另外：\r\nLinux Programmer's Manual 中第六个问题：\r\n        Q6  Will closing a file descriptor cause it to be removed from all\r\n           epoll sets automatically?\r\n\r\n        A6  Yes, but be aware of the following point.  A file descriptor is a\r\n           reference to an open file description (see open(2)).  Whenever a\r\n           file descriptor is duplicated via dup(2), dup2(2), fcntl(2)\r\n           F_DUPFD, or fork(2), a new file descriptor referring to the same\r\n           open file description is created.  An open file description con‐\r\n           tinues to exist until all file descriptors referring to it have\r\n           been closed.  A file descriptor is removed from an epoll set only\r\n           after all the file descriptors referring to the underlying open\r\n           file description have been closed (or before if the file descrip‐\r\n           tor is explicitly removed using epoll_ctl(2) EPOLL_CTL_DEL).\r\n           This means that even after a file descriptor that is part of an\r\n           epoll set has been closed, events may be reported for that file\r\n           descriptor if other file descriptors referring to the same under‐\r\n           lying file description remain open.\r\n\r\n当调用close()关闭对应的fd时，会使相应的引用计数减一，只有减到0时，epoll才会真的删掉它，所以，比较安全的做法是：\r\n先del掉它，再close它(如果不确定close是否真的关闭了这个文件。)。\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\nubuntu 配置opencv：\r\nvim /etc/ld.so.conf\r\n加一行include /usr/local/opencv/*\r\n/sbin/ldconfig –v"
  },
  {
    "path": "old_version/old_version_0.1/index.html",
    "content": "Hello World !"
  },
  {
    "path": "old_version/old_version_0.1/main.cpp",
    "content": "#include \"requestData.h\"\r\n#include \"epoll.h\"\r\n#include \"threadpool.h\"\r\n#include \"util.h\"\r\n\r\n#include <sys/epoll.h>\r\n#include <queue>\r\n#include <sys/time.h>\r\n#include <sys/socket.h>\r\n#include <netinet/in.h>\r\n#include <stdio.h>\r\n#include <string.h>\r\n#include <cstdlib>\r\n#include <iostream>\r\n#include <vector>\r\n#include <unistd.h>\r\n\r\nusing namespace std;\r\n\r\nconst int THREADPOOL_THREAD_NUM = 4;\r\nconst int QUEUE_SIZE = 65535;\r\n\r\nconst int PORT = 8888;\r\nconst int ASK_STATIC_FILE = 1;\r\nconst int ASK_IMAGE_STITCH = 2;\r\n\r\nconst string PATH = \"/\";\r\n\r\nconst int TIMER_TIME_OUT = 500;\r\n\r\n\r\nextern pthread_mutex_t qlock;\r\nextern struct epoll_event* events;\r\nvoid acceptConnection(int listen_fd, int epoll_fd, const string &path);\r\n\r\nextern priority_queue<mytimer*, deque<mytimer*>, timerCmp> myTimerQueue;\r\n\r\nint socket_bind_listen(int port)\r\n{\r\n    // 检查port值，取正确区间范围\r\n    if (port < 1024 || port > 65535)\r\n        return -1;\r\n\r\n    // 创建socket(IPv4 + TCP)，返回监听描述符\r\n    int listen_fd = 0;\r\n    if((listen_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1)\r\n        return -1;\r\n\r\n    // 消除bind时\"Address already in use\"错误\r\n    int optval = 1;\r\n    if(setsockopt(listen_fd, SOL_SOCKET,  SO_REUSEADDR, &optval, sizeof(optval)) == -1)\r\n        return -1;\r\n\r\n    // 设置服务器IP和Port，和监听描述副绑定\r\n    struct sockaddr_in server_addr;\r\n    bzero((char*)&server_addr, sizeof(server_addr));\r\n    server_addr.sin_family = AF_INET;\r\n    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);\r\n    server_addr.sin_port = htons((unsigned short)port);\r\n    if(bind(listen_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1)\r\n        return -1;\r\n\r\n    // 开始监听，最大等待队列长为LISTENQ\r\n    if(listen(listen_fd, LISTENQ) == -1)\r\n        return -1;\r\n\r\n    // 无效监听描述符\r\n    if(listen_fd == -1)\r\n    {\r\n        close(listen_fd);\r\n        return -1;\r\n    }\r\n\r\n    return listen_fd;\r\n}\r\n\r\nvoid myHandler(void *args)\r\n{\r\n    requestData *req_data = (requestData*)args;\r\n    req_data->handleRequest();\r\n}\r\n\r\nvoid acceptConnection(int listen_fd, int epoll_fd, const string &path)\r\n{\r\n    struct sockaddr_in client_addr;\r\n    memset(&client_addr, 0, sizeof(struct sockaddr_in));\r\n    socklen_t client_addr_len = 0;\r\n    int accept_fd = 0;\r\n    while((accept_fd = accept(listen_fd, (struct sockaddr*)&client_addr, &client_addr_len)) > 0)\r\n    {\r\n        /*\r\n        // TCP的保活机制默认是关闭的\r\n        int optval = 0;\r\n        socklen_t len_optval = 4;\r\n        getsockopt(accept_fd, SOL_SOCKET,  SO_KEEPALIVE, &optval, &len_optval);\r\n        cout << \"optval ==\" << optval << endl;\r\n        */\r\n        \r\n        // 设为非阻塞模式\r\n        int ret = setSocketNonBlocking(accept_fd);\r\n        if (ret < 0)\r\n        {\r\n            perror(\"Set non block failed!\");\r\n            return;\r\n        }\r\n\r\n        requestData *req_info = new requestData(epoll_fd, accept_fd, path);\r\n\r\n        // 文件描述符可以读，边缘触发(Edge Triggered)模式，保证一个socket连接在任一时刻只被一个线程处理\r\n        __uint32_t _epo_event = EPOLLIN | EPOLLET | EPOLLONESHOT;\r\n        epoll_add(epoll_fd, accept_fd, static_cast<void*>(req_info), _epo_event);\r\n        // 新增时间信息\r\n        mytimer *mtimer = new mytimer(req_info, TIMER_TIME_OUT);\r\n        req_info->addTimer(mtimer);\r\n        pthread_mutex_lock(&qlock);\r\n        myTimerQueue.push(mtimer);\r\n        pthread_mutex_unlock(&qlock);\r\n    }\r\n    //if(accept_fd == -1)\r\n     //   perror(\"accept\");\r\n}\r\n// 分发处理函数\r\nvoid handle_events(int epoll_fd, int listen_fd, struct epoll_event* events, int events_num, const string &path, threadpool_t* tp)\r\n{\r\n    for(int i = 0; i < events_num; i++)\r\n    {\r\n        // 获取有事件产生的描述符\r\n        requestData* request = (requestData*)(events[i].data.ptr);\r\n        int fd = request->getFd();\r\n\r\n        // 有事件发生的描述符为监听描述符\r\n        if(fd == listen_fd)\r\n        {\r\n            //cout << \"This is listen_fd\" << endl;\r\n            acceptConnection(listen_fd, epoll_fd, path);\r\n        }\r\n        else\r\n        {\r\n            // 排除错误事件\r\n            if ((events[i].events & EPOLLERR) || (events[i].events & EPOLLHUP)\r\n                || (!(events[i].events & EPOLLIN)))\r\n            {\r\n                printf(\"error event\\n\");\r\n                delete request;\r\n                continue;\r\n            }\r\n\r\n            // 将请求任务加入到线程池中\r\n            // 加入线程池之前将Timer和request分离\r\n            request->seperateTimer();\r\n            int rc = threadpool_add(tp, myHandler, events[i].data.ptr, 0);\r\n        }\r\n    }\r\n}\r\n\r\n/* 处理逻辑是这样的~\r\n因为(1) 优先队列不支持随机访问\r\n(2) 即使支持，随机删除某节点后破坏了堆的结构，需要重新更新堆结构。\r\n所以对于被置为deleted的时间节点，会延迟到它(1)超时 或 (2)它前面的节点都被删除时，它才会被删除。\r\n一个点被置为deleted,它最迟会在TIMER_TIME_OUT时间后被删除。\r\n这样做有两个好处：\r\n(1) 第一个好处是不需要遍历优先队列，省时。\r\n(2) 第二个好处是给超时时间一个容忍的时间，就是设定的超时时间是删除的下限(并不是一到超时时间就立即删除)，如果监听的请求在超时后的下一次请求中又一次出现了，\r\n就不用再重新申请requestData节点了，这样可以继续重复利用前面的requestData，减少了一次delete和一次new的时间。\r\n*/\r\n\r\nvoid handle_expired_event()\r\n{\r\n    pthread_mutex_lock(&qlock);\r\n    while (!myTimerQueue.empty())\r\n    {\r\n        mytimer *ptimer_now = myTimerQueue.top();\r\n        if (ptimer_now->isDeleted())\r\n        {\r\n            myTimerQueue.pop();\r\n            delete ptimer_now;\r\n        }\r\n        else if (ptimer_now->isvalid() == false)\r\n        {\r\n            myTimerQueue.pop();\r\n            delete ptimer_now;\r\n        }\r\n        else\r\n        {\r\n            break;\r\n        }\r\n    }\r\n    pthread_mutex_unlock(&qlock);\r\n}\r\n\r\nint main()\r\n{\r\n    handle_for_sigpipe();\r\n    int epoll_fd = epoll_init();\r\n    if (epoll_fd < 0)\r\n    {\r\n        perror(\"epoll init failed\");\r\n        return 1;\r\n    }\r\n    threadpool_t *threadpool = threadpool_create(THREADPOOL_THREAD_NUM, QUEUE_SIZE, 0);\r\n    int listen_fd = socket_bind_listen(PORT);\r\n    if (listen_fd < 0) \r\n    {\r\n        perror(\"socket bind failed\");\r\n        return 1;\r\n    }\r\n    if (setSocketNonBlocking(listen_fd) < 0)\r\n    {\r\n        perror(\"set socket non block failed\");\r\n        return 1;\r\n    }\r\n    __uint32_t event = EPOLLIN | EPOLLET;\r\n    requestData *req = new requestData();\r\n    req->setFd(listen_fd);\r\n    epoll_add(epoll_fd, listen_fd, static_cast<void*>(req), event);\r\n    while (true)\r\n    {\r\n        int events_num = my_epoll_wait(epoll_fd, events, MAXEVENTS, -1);\r\n        //printf(\"%zu\\n\", myTimerQueue.size());        \r\n        if (events_num == 0)\r\n            continue;\r\n        printf(\"%d\\n\", events_num);\r\n        //printf(\"%zu\\n\", myTimerQueue.size());    \r\n        // else\r\n        //     cout << \"one connection has come!\" << endl;\r\n        // 遍历events数组，根据监听种类及描述符类型分发操作\r\n        handle_events(epoll_fd, listen_fd, events, events_num, PATH, threadpool);\r\n\r\n        handle_expired_event();\r\n    }\r\n    return 0;\r\n}"
  },
  {
    "path": "old_version/old_version_0.1/requestData.cpp",
    "content": "#include \"requestData.h\"\r\n#include \"util.h\"\r\n#include \"epoll.h\"\r\n#include <sys/epoll.h>\r\n#include <unistd.h>\r\n#include <sys/time.h>\r\n#include <unordered_map>\r\n#include <fcntl.h>\r\n#include <sys/stat.h>\r\n#include <sys/mman.h>\r\n#include <queue>\r\n\r\n#include <opencv/cv.h>\r\n#include <opencv2/core/core.hpp>\r\n#include <opencv2/highgui/highgui.hpp>\r\n#include <opencv2/opencv.hpp>\r\nusing namespace cv;\r\n\r\n//test\r\n#include <iostream>\r\nusing namespace std;\r\n\r\npthread_mutex_t qlock = PTHREAD_MUTEX_INITIALIZER;\r\npthread_mutex_t MimeType::lock = PTHREAD_MUTEX_INITIALIZER;\r\nstd::unordered_map<std::string, std::string> MimeType::mime;\r\n\r\nstd::string MimeType::getMime(const std::string &suffix)\r\n{\r\n    if (mime.size() == 0)\r\n    {\r\n        pthread_mutex_lock(&lock);\r\n        if (mime.size() == 0)\r\n        {\r\n            mime[\".html\"] = \"text/html\";\r\n            mime[\".avi\"] = \"video/x-msvideo\";\r\n            mime[\".bmp\"] = \"image/bmp\";\r\n            mime[\".c\"] = \"text/plain\";\r\n            mime[\".doc\"] = \"application/msword\";\r\n            mime[\".gif\"] = \"image/gif\";\r\n            mime[\".gz\"] = \"application/x-gzip\";\r\n            mime[\".htm\"] = \"text/html\";\r\n            mime[\".ico\"] = \"application/x-ico\";\r\n            mime[\".jpg\"] = \"image/jpeg\";\r\n            mime[\".png\"] = \"image/png\";\r\n            mime[\".txt\"] = \"text/plain\";\r\n            mime[\".mp3\"] = \"audio/mp3\";\r\n            mime[\"default\"] = \"text/html\";\r\n        }\r\n        pthread_mutex_unlock(&lock);\r\n    }\r\n    if (mime.find(suffix) == mime.end())\r\n        return mime[\"default\"];\r\n    else\r\n        return mime[suffix];\r\n}\r\n\r\n\r\npriority_queue<mytimer*, deque<mytimer*>, timerCmp> myTimerQueue;\r\n\r\nrequestData::requestData(): \r\n    now_read_pos(0), state(STATE_PARSE_URI), h_state(h_start), \r\n    keep_alive(false), againTimes(0), timer(NULL)\r\n{\r\n    cout << \"requestData constructed !\" << endl;\r\n}\r\n\r\nrequestData::requestData(int _epollfd, int _fd, std::string _path):\r\n    now_read_pos(0), state(STATE_PARSE_URI), h_state(h_start), \r\n    keep_alive(false), againTimes(0), timer(NULL),\r\n    path(_path), fd(_fd), epollfd(_epollfd)\r\n{}\r\n\r\nrequestData::~requestData()\r\n{\r\n    cout << \"~requestData()\" << endl;\r\n    struct epoll_event ev;\r\n    // 超时的一定都是读请求，没有\"被动\"写。\r\n    ev.events = EPOLLIN | EPOLLET | EPOLLONESHOT;\r\n    ev.data.ptr = (void*)this;\r\n    epoll_ctl(epollfd, EPOLL_CTL_DEL, fd, &ev);\r\n    if (timer != NULL)\r\n    {\r\n        timer->clearReq();\r\n        timer = NULL;\r\n    }\r\n    close(fd);\r\n}\r\n\r\nvoid requestData::addTimer(mytimer *mtimer)\r\n{\r\n    if (timer == NULL)\r\n        timer = mtimer;\r\n}\r\n\r\nint requestData::getFd()\r\n{\r\n    return fd;\r\n}\r\nvoid requestData::setFd(int _fd)\r\n{\r\n    fd = _fd;\r\n}\r\n\r\nvoid requestData::reset()\r\n{\r\n    againTimes = 0;\r\n    content.clear();\r\n    file_name.clear();\r\n    path.clear();\r\n    now_read_pos = 0;\r\n    state = STATE_PARSE_URI;\r\n    h_state = h_start;\r\n    headers.clear();\r\n    keep_alive = false;\r\n}\r\n\r\nvoid requestData::seperateTimer()\r\n{\r\n    if (timer)\r\n    {\r\n        timer->clearReq();\r\n        timer = NULL;\r\n    }\r\n}\r\n\r\nvoid requestData::handleRequest()\r\n{\r\n    char buff[MAX_BUFF];\r\n    bool isError = false;\r\n    while (true)\r\n    {\r\n        int read_num = readn(fd, buff, MAX_BUFF);\r\n        if (read_num < 0)\r\n        {\r\n            perror(\"1\");\r\n            isError = true;\r\n            break;\r\n        }\r\n        else if (read_num == 0)\r\n        {\r\n            // 有请求出现但是读不到数据，可能是Request Aborted，或者来自网络的数据没有达到等原因\r\n            perror(\"read_num == 0\");\r\n            if (errno == EAGAIN)\r\n            {\r\n                if (againTimes > AGAIN_MAX_TIMES)\r\n                    isError = true;\r\n                else\r\n                    ++againTimes;\r\n            }\r\n            else if (errno != 0)\r\n                isError = true;\r\n            break;\r\n        }\r\n        string now_read(buff, buff + read_num);\r\n        content += now_read;\r\n\r\n        if (state == STATE_PARSE_URI)\r\n        {\r\n            int flag = this->parse_URI();\r\n            if (flag == PARSE_URI_AGAIN)\r\n            {\r\n                break;\r\n            }\r\n            else if (flag == PARSE_URI_ERROR)\r\n            {\r\n                perror(\"2\");\r\n                isError = true;\r\n                break;\r\n            }\r\n        }\r\n        if (state == STATE_PARSE_HEADERS)\r\n        {\r\n            int flag = this->parse_Headers();\r\n            if (flag == PARSE_HEADER_AGAIN)\r\n            {  \r\n                break;\r\n            }\r\n            else if (flag == PARSE_HEADER_ERROR)\r\n            {\r\n                perror(\"3\");\r\n                isError = true;\r\n                break;\r\n            }\r\n            if(method == METHOD_POST)\r\n            {\r\n                state = STATE_RECV_BODY;\r\n            }\r\n            else \r\n            {\r\n                state = STATE_ANALYSIS;\r\n            }\r\n        }\r\n        if (state == STATE_RECV_BODY)\r\n        {\r\n            int content_length = -1;\r\n            if (headers.find(\"Content-length\") != headers.end())\r\n            {\r\n                content_length = stoi(headers[\"Content-length\"]);\r\n            }\r\n            else\r\n            {\r\n                isError = true;\r\n                break;\r\n            }\r\n            if (content.size() < content_length)\r\n                continue;\r\n            state = STATE_ANALYSIS;\r\n        }\r\n        if (state == STATE_ANALYSIS)\r\n        {\r\n            int flag = this->analysisRequest();\r\n            if (flag < 0)\r\n            {\r\n                isError = true;\r\n                break;\r\n            }\r\n            else if (flag == ANALYSIS_SUCCESS)\r\n            {\r\n\r\n                state = STATE_FINISH;\r\n                break;\r\n            }\r\n            else\r\n            {\r\n                isError = true;\r\n                break;\r\n            }\r\n        }\r\n    }\r\n\r\n    if (isError)\r\n    {\r\n        delete this;\r\n        return;\r\n    }\r\n    // 加入epoll继续\r\n    if (state == STATE_FINISH)\r\n    {\r\n        if (keep_alive)\r\n        {\r\n            printf(\"ok\\n\");\r\n            this->reset();\r\n        }\r\n        else\r\n        {\r\n            delete this;\r\n            return;\r\n        }\r\n    }\r\n    // 一定要先加时间信息，否则可能会出现刚加进去，下个in触发来了，然后分离失败后，又加入队列，最后超时被删，然后正在线程中进行的任务出错，double free错误。\r\n    // 新增时间信息\r\n    pthread_mutex_lock(&qlock);\r\n    mytimer *mtimer = new mytimer(this, 500);\r\n    timer = mtimer;\r\n    myTimerQueue.push(mtimer);\r\n    pthread_mutex_unlock(&qlock);\r\n\r\n    __uint32_t _epo_event = EPOLLIN | EPOLLET | EPOLLONESHOT;\r\n    int ret = epoll_mod(epollfd, fd, static_cast<void*>(this), _epo_event);\r\n    if (ret < 0)\r\n    {\r\n        // 返回错误处理\r\n        delete this;\r\n        return;\r\n    }\r\n}\r\n\r\nint requestData::parse_URI()\r\n{\r\n    string &str = content;\r\n    // 读到完整的请求行再开始解析请求\r\n    int pos = str.find('\\r', now_read_pos);\r\n    if (pos < 0)\r\n    {\r\n        return PARSE_URI_AGAIN;\r\n    }\r\n    // 去掉请求行所占的空间，节省空间\r\n    string request_line = str.substr(0, pos);\r\n    if (str.size() > pos + 1)\r\n        str = str.substr(pos + 1);\r\n    else \r\n        str.clear();\r\n    // Method\r\n    pos = request_line.find(\"GET\");\r\n    if (pos < 0)\r\n    {\r\n        pos = request_line.find(\"POST\");\r\n        if (pos < 0)\r\n        {\r\n            return PARSE_URI_ERROR;\r\n        }\r\n        else\r\n        {\r\n            method = METHOD_POST;\r\n        }\r\n    }\r\n    else\r\n    {\r\n        method = METHOD_GET;\r\n    }\r\n    //printf(\"method = %d\\n\", method);\r\n    // filename\r\n    pos = request_line.find(\"/\", pos);\r\n    if (pos < 0)\r\n    {\r\n        return PARSE_URI_ERROR;\r\n    }\r\n    else\r\n    {\r\n        int _pos = request_line.find(' ', pos);\r\n        if (_pos < 0)\r\n            return PARSE_URI_ERROR;\r\n        else\r\n        {\r\n            if (_pos - pos > 1)\r\n            {\r\n                file_name = request_line.substr(pos + 1, _pos - pos - 1);\r\n                int __pos = file_name.find('?');\r\n                if (__pos >= 0)\r\n                {\r\n                    file_name = file_name.substr(0, __pos);\r\n                }\r\n            }\r\n                \r\n            else\r\n                file_name = \"index.html\";\r\n        }\r\n        pos = _pos;\r\n    }\r\n    //cout << \"file_name: \" << file_name << endl;\r\n    // HTTP 版本号\r\n    pos = request_line.find(\"/\", pos);\r\n    if (pos < 0)\r\n    {\r\n        return PARSE_URI_ERROR;\r\n    }\r\n    else\r\n    {\r\n        if (request_line.size() - pos <= 3)\r\n        {\r\n            return PARSE_URI_ERROR;\r\n        }\r\n        else\r\n        {\r\n            string ver = request_line.substr(pos + 1, 3);\r\n            if (ver == \"1.0\")\r\n                HTTPversion = HTTP_10;\r\n            else if (ver == \"1.1\")\r\n                HTTPversion = HTTP_11;\r\n            else\r\n                return PARSE_URI_ERROR;\r\n        }\r\n    }\r\n    state = STATE_PARSE_HEADERS;\r\n    return PARSE_URI_SUCCESS;\r\n}\r\n\r\nint requestData::parse_Headers()\r\n{\r\n    string &str = content;\r\n    int key_start = -1, key_end = -1, value_start = -1, value_end = -1;\r\n    int now_read_line_begin = 0;\r\n    bool notFinish = true;\r\n    for (int i = 0; i < str.size() && notFinish; ++i)\r\n    {\r\n        switch(h_state)\r\n        {\r\n            case h_start:\r\n            {\r\n                if (str[i] == '\\n' || str[i] == '\\r')\r\n                    break;\r\n                h_state = h_key;\r\n                key_start = i;\r\n                now_read_line_begin = i;\r\n                break;\r\n            }\r\n            case h_key:\r\n            {\r\n                if (str[i] == ':')\r\n                {\r\n                    key_end = i;\r\n                    if (key_end - key_start <= 0)\r\n                        return PARSE_HEADER_ERROR;\r\n                    h_state = h_colon;\r\n                }\r\n                else if (str[i] == '\\n' || str[i] == '\\r')\r\n                    return PARSE_HEADER_ERROR;\r\n                break;  \r\n            }\r\n            case h_colon:\r\n            {\r\n                if (str[i] == ' ')\r\n                {\r\n                    h_state = h_spaces_after_colon;\r\n                }\r\n                else\r\n                    return PARSE_HEADER_ERROR;\r\n                break;  \r\n            }\r\n            case h_spaces_after_colon:\r\n            {\r\n                h_state = h_value;\r\n                value_start = i;\r\n                break;  \r\n            }\r\n            case h_value:\r\n            {\r\n                if (str[i] == '\\r')\r\n                {\r\n                    h_state = h_CR;\r\n                    value_end = i;\r\n                    if (value_end - value_start <= 0)\r\n                        return PARSE_HEADER_ERROR;\r\n                }\r\n                else if (i - value_start > 255)\r\n                    return PARSE_HEADER_ERROR;\r\n                break;  \r\n            }\r\n            case h_CR:\r\n            {\r\n                if (str[i] == '\\n')\r\n                {\r\n                    h_state = h_LF;\r\n                    string key(str.begin() + key_start, str.begin() + key_end);\r\n                    string value(str.begin() + value_start, str.begin() + value_end);\r\n                    headers[key] = value;\r\n                    now_read_line_begin = i;\r\n                }\r\n                else\r\n                    return PARSE_HEADER_ERROR;\r\n                break;  \r\n            }\r\n            case h_LF:\r\n            {\r\n                if (str[i] == '\\r')\r\n                {\r\n                    h_state = h_end_CR;\r\n                }\r\n                else\r\n                {\r\n                    key_start = i;\r\n                    h_state = h_key;\r\n                }\r\n                break;\r\n            }\r\n            case h_end_CR:\r\n            {\r\n                if (str[i] == '\\n')\r\n                {\r\n                    h_state = h_end_LF;\r\n                }\r\n                else\r\n                    return PARSE_HEADER_ERROR;\r\n                break;\r\n            }\r\n            case h_end_LF:\r\n            {\r\n                notFinish = false;\r\n                key_start = i;\r\n                now_read_line_begin = i;\r\n                break;\r\n            }\r\n        }\r\n    }\r\n    if (h_state == h_end_LF)\r\n    {\r\n        str = str.substr(now_read_line_begin);\r\n        return PARSE_HEADER_SUCCESS;\r\n    }\r\n    str = str.substr(now_read_line_begin);\r\n    return PARSE_HEADER_AGAIN;\r\n}\r\n\r\nint requestData::analysisRequest()\r\n{\r\n    if (method == METHOD_POST)\r\n    {\r\n        //get content\r\n        char header[MAX_BUFF];\r\n        sprintf(header, \"HTTP/1.1 %d %s\\r\\n\", 200, \"OK\");\r\n        if(headers.find(\"Connection\") != headers.end() && headers[\"Connection\"] == \"keep-alive\")\r\n        {\r\n            keep_alive = true;\r\n            sprintf(header, \"%sConnection: keep-alive\\r\\n\", header);\r\n            sprintf(header, \"%sKeep-Alive: timeout=%d\\r\\n\", header, EPOLL_WAIT_TIME);\r\n        }\r\n        //cout << \"content=\" << content << endl;\r\n        // test char*\r\n        char *send_content = \"I have receiced this.\";\r\n\r\n        sprintf(header, \"%sContent-length: %zu\\r\\n\", header, strlen(send_content));\r\n        sprintf(header, \"%s\\r\\n\", header);\r\n        size_t send_len = (size_t)writen(fd, header, strlen(header));\r\n        if(send_len != strlen(header))\r\n        {\r\n            perror(\"Send header failed\");\r\n            return ANALYSIS_ERROR;\r\n        }\r\n        \r\n        send_len = (size_t)writen(fd, send_content, strlen(send_content));\r\n        if(send_len != strlen(send_content))\r\n        {\r\n            perror(\"Send content failed\");\r\n            return ANALYSIS_ERROR;\r\n        }\r\n        cout << \"content size ==\" << content.size() << endl;\r\n        vector<char> data(content.begin(), content.end());\r\n        Mat test = imdecode(data, CV_LOAD_IMAGE_ANYDEPTH|CV_LOAD_IMAGE_ANYCOLOR);\r\n        imwrite(\"receive.bmp\", test);\r\n        return ANALYSIS_SUCCESS;\r\n    }\r\n    else if (method == METHOD_GET)\r\n    {\r\n        char header[MAX_BUFF];\r\n        sprintf(header, \"HTTP/1.1 %d %s\\r\\n\", 200, \"OK\");\r\n        if(headers.find(\"Connection\") != headers.end() && headers[\"Connection\"] == \"keep-alive\")\r\n        {\r\n            keep_alive = true;\r\n            sprintf(header, \"%sConnection: keep-alive\\r\\n\", header);\r\n            sprintf(header, \"%sKeep-Alive: timeout=%d\\r\\n\", header, EPOLL_WAIT_TIME);\r\n        }\r\n        int dot_pos = file_name.find('.');\r\n        const char* filetype;\r\n        if (dot_pos < 0) \r\n            filetype = MimeType::getMime(\"default\").c_str();\r\n        else\r\n            filetype = MimeType::getMime(file_name.substr(dot_pos)).c_str();\r\n        struct stat sbuf;\r\n        if (stat(file_name.c_str(), &sbuf) < 0)\r\n        {\r\n            handleError(fd, 404, \"Not Found!\");\r\n            return ANALYSIS_ERROR;\r\n        }\r\n\r\n        sprintf(header, \"%sContent-type: %s\\r\\n\", header, filetype);\r\n        // 通过Content-length返回文件大小\r\n        sprintf(header, \"%sContent-length: %ld\\r\\n\", header, sbuf.st_size);\r\n\r\n        sprintf(header, \"%s\\r\\n\", header);\r\n        size_t send_len = (size_t)writen(fd, header, strlen(header));\r\n        if(send_len != strlen(header))\r\n        {\r\n            perror(\"Send header failed\");\r\n            return ANALYSIS_ERROR;\r\n        }\r\n        int src_fd = open(file_name.c_str(), O_RDONLY, 0);\r\n        char *src_addr = static_cast<char*>(mmap(NULL, sbuf.st_size, PROT_READ, MAP_PRIVATE, src_fd, 0));\r\n        close(src_fd);\r\n    \r\n        // 发送文件并校验完整性\r\n        send_len = writen(fd, src_addr, sbuf.st_size);\r\n        if(send_len != sbuf.st_size)\r\n        {\r\n            perror(\"Send file failed\");\r\n            return ANALYSIS_ERROR;\r\n        }\r\n        munmap(src_addr, sbuf.st_size);\r\n        return ANALYSIS_SUCCESS;\r\n    }\r\n    else\r\n        return ANALYSIS_ERROR;\r\n}\r\n\r\nvoid requestData::handleError(int fd, int err_num, string short_msg)\r\n{\r\n    short_msg = \" \" + short_msg;\r\n    char send_buff[MAX_BUFF];\r\n    string body_buff, header_buff;\r\n    body_buff += \"<html><title>TKeed Error</title>\";\r\n    body_buff += \"<body bgcolor=\\\"ffffff\\\">\";\r\n    body_buff += to_string(err_num) + short_msg;\r\n    body_buff += \"<hr><em> LinYa's Web Server</em>\\n</body></html>\";\r\n\r\n    header_buff += \"HTTP/1.1 \" + to_string(err_num) + short_msg + \"\\r\\n\";\r\n    header_buff += \"Content-type: text/html\\r\\n\";\r\n    header_buff += \"Connection: close\\r\\n\";\r\n    header_buff += \"Content-length: \" + to_string(body_buff.size()) + \"\\r\\n\";\r\n    header_buff += \"\\r\\n\";\r\n    sprintf(send_buff, \"%s\", header_buff.c_str());\r\n    writen(fd, send_buff, strlen(send_buff));\r\n    sprintf(send_buff, \"%s\", body_buff.c_str());\r\n    writen(fd, send_buff, strlen(send_buff));\r\n}\r\n\r\nmytimer::mytimer(requestData *_request_data, int timeout): deleted(false), request_data(_request_data)\r\n{\r\n    //cout << \"mytimer()\" << endl;\r\n    struct timeval now;\r\n    gettimeofday(&now, NULL);\r\n    // 以毫秒计\r\n    expired_time = ((now.tv_sec * 1000) + (now.tv_usec / 1000)) + timeout;\r\n}\r\n\r\nmytimer::~mytimer()\r\n{\r\n    cout << \"~mytimer()\" << endl;\r\n    if (request_data != NULL)\r\n    {\r\n        cout << \"request_data=\" << request_data << endl;\r\n        delete request_data;\r\n        request_data = NULL;\r\n    }\r\n}\r\n\r\nvoid mytimer::update(int timeout)\r\n{\r\n    struct timeval now;\r\n    gettimeofday(&now, NULL);\r\n    expired_time = ((now.tv_sec * 1000) + (now.tv_usec / 1000)) + timeout;\r\n}\r\n\r\nbool mytimer::isvalid()\r\n{\r\n    struct timeval now;\r\n    gettimeofday(&now, NULL);\r\n    size_t temp = ((now.tv_sec * 1000) + (now.tv_usec / 1000));\r\n    if (temp < expired_time)\r\n    {\r\n        return true;\r\n    }\r\n    else\r\n    {\r\n        this->setDeleted();\r\n        return false;\r\n    }\r\n}\r\n\r\nvoid mytimer::clearReq()\r\n{\r\n    request_data = NULL;\r\n    this->setDeleted();\r\n}\r\n\r\nvoid mytimer::setDeleted()\r\n{\r\n    deleted = true;\r\n}\r\n\r\nbool mytimer::isDeleted() const\r\n{\r\n    return deleted;\r\n}\r\n\r\nsize_t mytimer::getExpTime() const\r\n{\r\n    return expired_time;\r\n}\r\n\r\nbool timerCmp::operator()(const mytimer *a, const mytimer *b) const\r\n{\r\n    return a->getExpTime() > b->getExpTime();\r\n}"
  },
  {
    "path": "old_version/old_version_0.1/requestData.h",
    "content": "#ifndef REQUESTDATA\r\n#define REQUESTDATA\r\n#include <string>\r\n#include <unordered_map>\r\n\r\nconst int STATE_PARSE_URI = 1;\r\nconst int STATE_PARSE_HEADERS = 2;\r\nconst int STATE_RECV_BODY = 3;\r\nconst int STATE_ANALYSIS = 4;\r\nconst int STATE_FINISH = 5;\r\n\r\nconst int MAX_BUFF = 4096;\r\n\r\n// 有请求出现但是读不到数据,可能是Request Aborted,\r\n// 或者来自网络的数据没有达到等原因,\r\n// 对这样的请求尝试超过一定的次数就抛弃\r\nconst int AGAIN_MAX_TIMES = 200;\r\n\r\nconst int PARSE_URI_AGAIN = -1;\r\nconst int PARSE_URI_ERROR = -2;\r\nconst int PARSE_URI_SUCCESS = 0;\r\n\r\nconst int PARSE_HEADER_AGAIN = -1;\r\nconst int PARSE_HEADER_ERROR = -2;\r\nconst int PARSE_HEADER_SUCCESS = 0;\r\n\r\nconst int ANALYSIS_ERROR = -2;\r\nconst int ANALYSIS_SUCCESS = 0;\r\n\r\nconst int METHOD_POST = 1;\r\nconst int METHOD_GET = 2;\r\nconst int HTTP_10 = 1;\r\nconst int HTTP_11 = 2;\r\n\r\nconst int EPOLL_WAIT_TIME = 500;\r\n\r\nclass MimeType\r\n{\r\nprivate:\r\n    static pthread_mutex_t lock;\r\n    static std::unordered_map<std::string, std::string> mime;\r\n    MimeType();\r\n    MimeType(const MimeType &m);\r\npublic:\r\n    static std::string getMime(const std::string &suffix);\r\n};\r\n\r\nenum HeadersState\r\n{\r\n    h_start = 0,\r\n    h_key,\r\n    h_colon,\r\n    h_spaces_after_colon,\r\n    h_value,\r\n    h_CR,\r\n    h_LF,\r\n    h_end_CR,\r\n    h_end_LF\r\n};\r\n\r\nstruct mytimer;\r\nstruct requestData;\r\n\r\nstruct requestData\r\n{\r\nprivate:\r\n    int againTimes;\r\n    std::string path;\r\n    int fd;\r\n    int epollfd;\r\n    // content的内容用完就清\r\n    std::string content;\r\n    int method;\r\n    int HTTPversion;\r\n    std::string file_name;\r\n    int now_read_pos;\r\n    int state;\r\n    int h_state;\r\n    bool isfinish;\r\n    bool keep_alive;\r\n    std::unordered_map<std::string, std::string> headers;\r\n    mytimer *timer;\r\n\r\nprivate:\r\n    int parse_URI();\r\n    int parse_Headers();\r\n    int analysisRequest();\r\n\r\npublic:\r\n\r\n    requestData();\r\n    requestData(int _epollfd, int _fd, std::string _path);\r\n    ~requestData();\r\n    void addTimer(mytimer *mtimer);\r\n    void reset();\r\n    void seperateTimer();\r\n    int getFd();\r\n    void setFd(int _fd);\r\n    void handleRequest();\r\n    void handleError(int fd, int err_num, std::string short_msg);\r\n};\r\n\r\nstruct mytimer\r\n{\r\n    bool deleted;\r\n    size_t expired_time;\r\n    requestData *request_data;\r\n\r\n    mytimer(requestData *_request_data, int timeout);\r\n    ~mytimer();\r\n    void update(int timeout);\r\n    bool isvalid();\r\n    void clearReq();\r\n    void setDeleted();\r\n    bool isDeleted() const;\r\n    size_t getExpTime() const;\r\n};\r\n\r\nstruct timerCmp\r\n{\r\n    bool operator()(const mytimer *a, const mytimer *b) const;\r\n};\r\n#endif"
  },
  {
    "path": "old_version/old_version_0.1/threadpool.cpp",
    "content": "#include \"threadpool.h\"\r\n\r\nthreadpool_t *threadpool_create(int thread_count, int queue_size, int flags)\r\n{\r\n    threadpool_t *pool;\r\n    int i;\r\n    //(void) flags;\r\n    do\r\n    {\r\n        if(thread_count <= 0 || thread_count > MAX_THREADS || queue_size <= 0 || queue_size > MAX_QUEUE) {\r\n            return NULL;\r\n        }\r\n    \r\n        if((pool = (threadpool_t *)malloc(sizeof(threadpool_t))) == NULL) \r\n        {\r\n            break;\r\n        }\r\n    \r\n        /* Initialize */\r\n        pool->thread_count = 0;\r\n        pool->queue_size = queue_size;\r\n        pool->head = pool->tail = pool->count = 0;\r\n        pool->shutdown = pool->started = 0;\r\n    \r\n        /* Allocate thread and task queue */\r\n        pool->threads = (pthread_t *)malloc(sizeof(pthread_t) * thread_count);\r\n        pool->queue = (threadpool_task_t *)malloc(sizeof(threadpool_task_t) * queue_size);\r\n    \r\n        /* Initialize mutex and conditional variable first */\r\n        if((pthread_mutex_init(&(pool->lock), NULL) != 0) ||\r\n           (pthread_cond_init(&(pool->notify), NULL) != 0) ||\r\n           (pool->threads == NULL) ||\r\n           (pool->queue == NULL)) \r\n        {\r\n            break;\r\n        }\r\n    \r\n        /* Start worker threads */\r\n        for(i = 0; i < thread_count; i++) {\r\n            if(pthread_create(&(pool->threads[i]), NULL, threadpool_thread, (void*)pool) != 0) \r\n            {\r\n                threadpool_destroy(pool, 0);\r\n                return NULL;\r\n            }\r\n            pool->thread_count++;\r\n            pool->started++;\r\n        }\r\n    \r\n        return pool;\r\n    } while(false);\r\n    \r\n    if (pool != NULL) \r\n    {\r\n        threadpool_free(pool);\r\n    }\r\n    return NULL;\r\n}\r\n\r\nint threadpool_add(threadpool_t *pool, void (*function)(void *), void *argument, int flags)\r\n{\r\n    //printf(\"add to thread pool !\\n\");\r\n    int err = 0;\r\n    int next;\r\n    //(void) flags;\r\n    if(pool == NULL || function == NULL)\r\n    {\r\n        return THREADPOOL_INVALID;\r\n    }\r\n    if(pthread_mutex_lock(&(pool->lock)) != 0)\r\n    {\r\n        return THREADPOOL_LOCK_FAILURE;\r\n    }\r\n    next = (pool->tail + 1) % pool->queue_size;\r\n    do \r\n    {\r\n        /* Are we full ? */\r\n        if(pool->count == pool->queue_size) {\r\n            err = THREADPOOL_QUEUE_FULL;\r\n            break;\r\n        }\r\n        /* Are we shutting down ? */\r\n        if(pool->shutdown) {\r\n            err = THREADPOOL_SHUTDOWN;\r\n            break;\r\n        }\r\n        /* Add task to queue */\r\n        pool->queue[pool->tail].function = function;\r\n        pool->queue[pool->tail].argument = argument;\r\n        pool->tail = next;\r\n        pool->count += 1;\r\n        \r\n        /* pthread_cond_broadcast */\r\n        if(pthread_cond_signal(&(pool->notify)) != 0) {\r\n            err = THREADPOOL_LOCK_FAILURE;\r\n            break;\r\n        }\r\n    } while(false);\r\n\r\n    if(pthread_mutex_unlock(&pool->lock) != 0) {\r\n        err = THREADPOOL_LOCK_FAILURE;\r\n    }\r\n\r\n    return err;\r\n}\r\n\r\nint threadpool_destroy(threadpool_t *pool, int flags)\r\n{\r\n    printf(\"Thread pool destroy !\\n\");\r\n    int i, err = 0;\r\n\r\n    if(pool == NULL)\r\n    {\r\n        return THREADPOOL_INVALID;\r\n    }\r\n\r\n    if(pthread_mutex_lock(&(pool->lock)) != 0) \r\n    {\r\n        return THREADPOOL_LOCK_FAILURE;\r\n    }\r\n\r\n    do \r\n    {\r\n        /* Already shutting down */\r\n        if(pool->shutdown) {\r\n            err = THREADPOOL_SHUTDOWN;\r\n            break;\r\n        }\r\n\r\n        pool->shutdown = (flags & THREADPOOL_GRACEFUL) ?\r\n            graceful_shutdown : immediate_shutdown;\r\n\r\n        /* Wake up all worker threads */\r\n        if((pthread_cond_broadcast(&(pool->notify)) != 0) ||\r\n           (pthread_mutex_unlock(&(pool->lock)) != 0)) {\r\n            err = THREADPOOL_LOCK_FAILURE;\r\n            break;\r\n        }\r\n\r\n        /* Join all worker thread */\r\n        for(i = 0; i < pool->thread_count; ++i)\r\n        {\r\n            if(pthread_join(pool->threads[i], NULL) != 0)\r\n            {\r\n                err = THREADPOOL_THREAD_FAILURE;\r\n            }\r\n        }\r\n    } while(false);\r\n\r\n    /* Only if everything went well do we deallocate the pool */\r\n    if(!err) \r\n    {\r\n        threadpool_free(pool);\r\n    }\r\n    return err;\r\n}\r\n\r\nint threadpool_free(threadpool_t *pool)\r\n{\r\n    if(pool == NULL || pool->started > 0)\r\n    {\r\n        return -1;\r\n    }\r\n\r\n    /* Did we manage to allocate ? */\r\n    if(pool->threads) \r\n    {\r\n        free(pool->threads);\r\n        free(pool->queue);\r\n \r\n        /* Because we allocate pool->threads after initializing the\r\n           mutex and condition variable, we're sure they're\r\n           initialized. Let's lock the mutex just in case. */\r\n        pthread_mutex_lock(&(pool->lock));\r\n        pthread_mutex_destroy(&(pool->lock));\r\n        pthread_cond_destroy(&(pool->notify));\r\n    }\r\n    free(pool);    \r\n    return 0;\r\n}\r\n\r\n\r\nstatic void *threadpool_thread(void *threadpool)\r\n{\r\n    threadpool_t *pool = (threadpool_t *)threadpool;\r\n    threadpool_task_t task;\r\n\r\n    for(;;)\r\n    {\r\n        /* Lock must be taken to wait on conditional variable */\r\n        pthread_mutex_lock(&(pool->lock));\r\n\r\n        /* Wait on condition variable, check for spurious wakeups.\r\n           When returning from pthread_cond_wait(), we own the lock. */\r\n        while((pool->count == 0) && (!pool->shutdown)) \r\n        {\r\n            pthread_cond_wait(&(pool->notify), &(pool->lock));\r\n        }\r\n\r\n        if((pool->shutdown == immediate_shutdown) ||\r\n           ((pool->shutdown == graceful_shutdown) &&\r\n            (pool->count == 0)))\r\n        {\r\n            break;\r\n        }\r\n\r\n        /* Grab our task */\r\n        task.function = pool->queue[pool->head].function;\r\n        task.argument = pool->queue[pool->head].argument;\r\n        pool->head = (pool->head + 1) % pool->queue_size;\r\n        pool->count -= 1;\r\n\r\n        /* Unlock */\r\n        pthread_mutex_unlock(&(pool->lock));\r\n\r\n        /* Get to work */\r\n        (*(task.function))(task.argument);\r\n    }\r\n\r\n    --pool->started;\r\n\r\n    pthread_mutex_unlock(&(pool->lock));\r\n    pthread_exit(NULL);\r\n    return(NULL);\r\n}"
  },
  {
    "path": "old_version/old_version_0.1/threadpool.h",
    "content": "#ifndef THREADPOOL\r\n#define THREADPOOL\r\n#include \"requestData.h\"\r\n#include <pthread.h>\r\n\r\nconst int THREADPOOL_INVALID = -1;\r\nconst int THREADPOOL_LOCK_FAILURE = -2;\r\nconst int THREADPOOL_QUEUE_FULL = -3;\r\nconst int THREADPOOL_SHUTDOWN = -4;\r\nconst int THREADPOOL_THREAD_FAILURE = -5;\r\nconst int THREADPOOL_GRACEFUL = 1;\r\n\r\nconst int MAX_THREADS = 1024;\r\nconst int MAX_QUEUE = 65535;\r\n\r\ntypedef enum \r\n{\r\n    immediate_shutdown = 1,\r\n    graceful_shutdown  = 2\r\n} threadpool_shutdown_t;\r\n\r\n/**\r\n *  @struct threadpool_task\r\n *  @brief the work struct\r\n *\r\n *  @var function Pointer to the function that will perform the task.\r\n *  @var argument Argument to be passed to the function.\r\n */\r\n\r\ntypedef struct {\r\n    void (*function)(void *);\r\n    void *argument;\r\n} threadpool_task_t;\r\n\r\n/**\r\n *  @struct threadpool\r\n *  @brief The threadpool struct\r\n *\r\n *  @var notify       Condition variable to notify worker threads.\r\n *  @var threads      Array containing worker threads ID.\r\n *  @var thread_count Number of threads\r\n *  @var queue        Array containing the task queue.\r\n *  @var queue_size   Size of the task queue.\r\n *  @var head         Index of the first element.\r\n *  @var tail         Index of the next element.\r\n *  @var count        Number of pending tasks\r\n *  @var shutdown     Flag indicating if the pool is shutting down\r\n *  @var started      Number of started threads\r\n */\r\nstruct threadpool_t\r\n{\r\n    pthread_mutex_t lock;\r\n    pthread_cond_t notify;\r\n    pthread_t *threads;\r\n    threadpool_task_t *queue;\r\n    int thread_count;\r\n    int queue_size;\r\n    int head;\r\n    int tail;\r\n    int count;\r\n    int shutdown;\r\n    int started;\r\n};\r\n\r\nthreadpool_t *threadpool_create(int thread_count, int queue_size, int flags);\r\nint threadpool_add(threadpool_t *pool, void (*function)(void *), void *argument, int flags);\r\nint threadpool_destroy(threadpool_t *pool, int flags);\r\nint threadpool_free(threadpool_t *pool);\r\nstatic void *threadpool_thread(void *threadpool);\r\n\r\n#endif"
  },
  {
    "path": "old_version/old_version_0.1/util.cpp",
    "content": "#include \"util.h\"\r\n#include <unistd.h>\r\n#include <fcntl.h>\r\n#include <signal.h>\r\n#include <errno.h>\r\n#include <string.h>\r\n\r\nssize_t readn(int fd, void *buff, size_t n)\r\n{\r\n    size_t nleft = n;\r\n    ssize_t nread = 0;\r\n    ssize_t readSum = 0;\r\n    char *ptr = (char*)buff;\r\n    while (nleft > 0)\r\n    {\r\n        if ((nread = read(fd, ptr, nleft)) < 0)\r\n        {\r\n            if (errno == EINTR)\r\n                nread = 0;\r\n            else if (errno == EAGAIN)\r\n            {\r\n                return readSum;\r\n            }\r\n            else\r\n            {\r\n                return -1;\r\n            }  \r\n        }\r\n        else if (nread == 0)\r\n            break;\r\n        readSum += nread;\r\n        nleft -= nread;\r\n        ptr += nread;\r\n    }\r\n    return readSum;\r\n}\r\n\r\nssize_t writen(int fd, void *buff, size_t n)\r\n{\r\n    size_t nleft = n;\r\n    ssize_t nwritten = 0;\r\n    ssize_t writeSum = 0;\r\n    char *ptr = (char*)buff;\r\n    while (nleft > 0)\r\n    {\r\n        if ((nwritten = write(fd, ptr, nleft)) <= 0)\r\n        {\r\n            if (nwritten < 0)\r\n            {\r\n                if (errno == EINTR || errno == EAGAIN)\r\n                {\r\n                    nwritten = 0;\r\n                    continue;\r\n                }\r\n                else\r\n                    return -1;\r\n            }\r\n        }\r\n        writeSum += nwritten;\r\n        nleft -= nwritten;\r\n        ptr += nwritten;\r\n    }\r\n    return writeSum;\r\n}\r\n\r\nvoid handle_for_sigpipe()\r\n{\r\n    struct sigaction sa;\r\n    memset(&sa, '\\0', sizeof(sa));\r\n    sa.sa_handler = SIG_IGN;\r\n    sa.sa_flags = 0;\r\n    if(sigaction(SIGPIPE, &sa, NULL))\r\n        return;\r\n}\r\n\r\nint setSocketNonBlocking(int fd)\r\n{\r\n    int flag = fcntl(fd, F_GETFL, 0);\r\n    if(flag == -1)\r\n        return -1;\r\n\r\n    flag |= O_NONBLOCK;\r\n    if(fcntl(fd, F_SETFL, flag) == -1)\r\n        return -1;\r\n    return 0;\r\n}"
  },
  {
    "path": "old_version/old_version_0.1/util.h",
    "content": "#ifndef UTIL\r\n#define UTIL\r\n#include <cstdlib>\r\n\r\nssize_t readn(int fd, void *buff, size_t n);\r\nssize_t writen(int fd, void *buff, size_t n);\r\nvoid handle_for_sigpipe();\r\nint setSocketNonBlocking(int fd);\r\n\r\n#endif"
  },
  {
    "path": "old_version/old_version_0.2/Makefile",
    "content": "SOURCE  := $(wildcard *.cpp)\r\nOBJS    := $(patsubst %.c,%.o,$(patsubst %.cpp,%.o,$(SOURCE)))\r\n\r\nTARGET  := myserver\r\nCC      := g++\r\nLIBS    := -lpthread -lopencv_core -lopencv_imgproc -lopencv_highgui\r\nINCLUDE:= -I./usr/local/include/opencv\r\nCFLAGS  := -std=c++11 -g -Wall -O3 $(INCLUDE)\r\nCXXFLAGS:= $(CFLAGS)\r\n\r\n.PHONY : objs clean veryclean rebuild all\r\nall : $(TARGET)\r\nobjs : $(OBJS)\r\nrebuild: veryclean all\r\nclean :\r\n\trm -fr *.o\r\nveryclean : clean\r\n\trm -rf $(TARGET)\r\n\r\n$(TARGET) : $(OBJS)\r\n\t$(CC) $(CXXFLAGS) -o $@ $(OBJS) $(LDFLAGS) $(LIBS)"
  },
  {
    "path": "old_version/old_version_0.2/Makefile1",
    "content": "cc=g++ -std=c++11\r\nmain: main.o requestData.o epoll.o threadpool.o\r\n\t$(cc) -o main main.o requestData.o epoll.o threadpool.o -lpthread -lopencv_core -lopencv_imgproc -lopencv_highgui -lopencv_ml \r\nmain.o: main.cpp\r\n\t$(cc) -I/usr/local/include/opencv -c main.cpp\r\nrequestData.o: requestData.h requestData.cpp\r\n\t$(cc) -I/usr/local/include/opencv -c requestData.cpp\r\nepoll.o: epoll.h epoll.cpp\r\n\t$(cc) -I/usr/local/include/opencv -c epoll.cpp\r\nthreadpool.o: threadpool.h threadpool.cpp\r\n\t$(cc) -I/usr/local/include/opencv -c threadpool.cpp\r\n.PHONY: clean\r\nclean:\r\n\t-rm main *.o\r\n\r\n\r\n\r\n\r\n.PHONY : everything objs clean veryclean rebuild\r\neverything :\r\n\t$(TARGET)\r\nall :\r\n\t$(TARGET)\r\nobjs :\r\n\t$(OBJS)\r\nrebuild:\r\n\tveryclean everything \r\nclean :\r\n\trm -fr *.o\r\nveryclean :\r\n\tclean rm -fr $(TARGET)"
  },
  {
    "path": "old_version/old_version_0.2/epoll.cpp",
    "content": "#include \"epoll.h\"\r\n#include <sys/epoll.h>\r\n#include <errno.h>\r\n#include \"threadpool.h\"\r\n\r\nstruct epoll_event* events;\r\n\r\nint epoll_init()\r\n{\r\n    int epoll_fd = epoll_create(LISTENQ + 1);\r\n    if(epoll_fd == -1)\r\n        return -1;\r\n    //events = (struct epoll_event*)malloc(sizeof(struct epoll_event) * MAXEVENTS);\r\n    events = new epoll_event[MAXEVENTS];\r\n    return epoll_fd;\r\n}\r\n\r\n// 注册新描述符\r\nint epoll_add(int epoll_fd, int fd, void *request, __uint32_t events)\r\n{\r\n    struct epoll_event event;\r\n    event.data.ptr = request;\r\n    event.events = events;\r\n    //printf(\"add to epoll %d\\n\", fd);\r\n    if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &event) < 0)\r\n    {\r\n        perror(\"epoll_add error\");\r\n        return -1;\r\n    }\r\n    return 0;\r\n}\r\n\r\n// 修改描述符状态\r\nint epoll_mod(int epoll_fd, int fd, void *request, __uint32_t events)\r\n{\r\n    struct epoll_event event;\r\n    event.data.ptr = request;\r\n    event.events = events;\r\n    if(epoll_ctl(epoll_fd, EPOLL_CTL_MOD, fd, &event) < 0)\r\n    {\r\n        perror(\"epoll_mod error\");\r\n        return -1;\r\n    }\r\n    return 0;\r\n}\r\n\r\n// 从epoll中删除描述符\r\nint epoll_del(int epoll_fd, int fd, void *request, __uint32_t events)\r\n{\r\n    struct epoll_event event;\r\n    event.data.ptr = request;\r\n    event.events = events;\r\n    if(epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fd, &event) < 0)\r\n    {\r\n        perror(\"epoll_del error\");\r\n        return -1;\r\n    } \r\n    return 0;\r\n}\r\n\r\n// 返回活跃事件数\r\nint my_epoll_wait(int epoll_fd, struct epoll_event* events, int max_events, int timeout)\r\n{\r\n    int ret_count = epoll_wait(epoll_fd, events, max_events, timeout);\r\n    if (ret_count < 0)\r\n    {\r\n        perror(\"epoll wait error\");\r\n    }\r\n    return ret_count;\r\n}"
  },
  {
    "path": "old_version/old_version_0.2/epoll.h",
    "content": "#pragma once\r\n#ifndef EVENTPOLL\r\n#define EVENTPOLL\r\n#include \"requestData.h\"\r\n\r\nconst int MAXEVENTS = 5000;\r\nconst int LISTENQ = 1024;\r\n\r\nint epoll_init();\r\nint epoll_add(int epoll_fd, int fd, void *request, __uint32_t events);\r\nint epoll_mod(int epoll_fd, int fd, void *request, __uint32_t events);\r\nint epoll_del(int epoll_fd, int fd, void *request, __uint32_t events);\r\nint my_epoll_wait(int epoll_fd, struct epoll_event *events, int max_events, int timeout);\r\n\r\n#endif"
  },
  {
    "path": "old_version/old_version_0.2/improvement.txt",
    "content": "1. 指针可以改成shared_ptr，不需要delete。\r\n2. 想法在某些地方写成单例模式。\r\n3. readn 和 writen 改成写到不能写后或读到不能读后加入epoll等待，要记录写到哪或读到哪，维护这样一个状态。比较两者的性能差异。\r\n4. 信号处理部分可以将 epoll_wait 替换为更安全的 epoll_pwait。\r\n\r\n\r\n踩坑：\r\n1. 对EPOLLONESHOT的误解，原以为当epoll_wait监听到相应的事件触发后，epoll会把与事件关联的fd从epoll描述符中禁止掉并且彻底删除，实际上并不会删除，man手册上的解释如下：\r\nEPOLLONESHOT (since Linux 2.6.2)\r\n              Sets the one-shot behavior for the associated file descriptor.\r\n              This means that after an event is pulled out with\r\n              epoll_wait(2) the associated file descriptor is internally\r\n              disabled and no other events will be reported by the epoll\r\n              interface.  The user must call epoll_ctl() with EPOLL_CTL_MOD\r\n              to rearm the file descriptor with a new event mask.\r\n另外：\r\nLinux Programmer's Manual 中第六个问题：\r\n        Q6  Will closing a file descriptor cause it to be removed from all\r\n           epoll sets automatically?\r\n\r\n        A6  Yes, but be aware of the following point.  A file descriptor is a\r\n           reference to an open file description (see open(2)).  Whenever a\r\n           file descriptor is duplicated via dup(2), dup2(2), fcntl(2)\r\n           F_DUPFD, or fork(2), a new file descriptor referring to the same\r\n           open file description is created.  An open file description con‐\r\n           tinues to exist until all file descriptors referring to it have\r\n           been closed.  A file descriptor is removed from an epoll set only\r\n           after all the file descriptors referring to the underlying open\r\n           file description have been closed (or before if the file descrip‐\r\n           tor is explicitly removed using epoll_ctl(2) EPOLL_CTL_DEL).\r\n           This means that even after a file descriptor that is part of an\r\n           epoll set has been closed, events may be reported for that file\r\n           descriptor if other file descriptors referring to the same under‐\r\n           lying file description remain open.\r\n\r\n当调用close()关闭对应的fd时，会使相应的引用计数减一，只有减到0时，epoll才会真的删掉它，所以，比较安全的做法是：\r\n先del掉它，再close它(如果不确定close是否真的关闭了这个文件。)。\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\nubuntu 配置opencv：\r\nvim /etc/ld.so.conf\r\n加一行include /usr/local/opencv/*\r\n/sbin/ldconfig –v"
  },
  {
    "path": "old_version/old_version_0.2/index.html",
    "content": "Hello World !"
  },
  {
    "path": "old_version/old_version_0.2/main.cpp",
    "content": "#include \"requestData.h\"\r\n#include \"epoll.h\"\r\n#include \"threadpool.h\"\r\n#include \"util.h\"\r\n\r\n#include <sys/epoll.h>\r\n#include <queue>\r\n#include <sys/time.h>\r\n#include <sys/socket.h>\r\n#include <netinet/in.h>\r\n#include <stdio.h>\r\n#include <string.h>\r\n#include <cstdlib>\r\n#include <iostream>\r\n#include <vector>\r\n#include <unistd.h>\r\n\r\nusing namespace std;\r\n\r\nconst int THREADPOOL_THREAD_NUM = 4;\r\nconst int QUEUE_SIZE = 65535;\r\n\r\nconst int PORT = 8888;\r\nconst int ASK_STATIC_FILE = 1;\r\nconst int ASK_IMAGE_STITCH = 2;\r\n\r\nconst string PATH = \"/\";\r\n\r\nconst int TIMER_TIME_OUT = 500;\r\n\r\n\r\nextern struct epoll_event* events;\r\nvoid acceptConnection(int listen_fd, int epoll_fd, const string &path);\r\n\r\nextern priority_queue<mytimer*, deque<mytimer*>, timerCmp> myTimerQueue;\r\n\r\nint socket_bind_listen(int port)\r\n{\r\n    // 检查port值，取正确区间范围\r\n    if (port < 1024 || port > 65535)\r\n        return -1;\r\n\r\n    // 创建socket(IPv4 + TCP)，返回监听描述符\r\n    int listen_fd = 0;\r\n    if((listen_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1)\r\n        return -1;\r\n\r\n    // 消除bind时\"Address already in use\"错误\r\n    int optval = 1;\r\n    if(setsockopt(listen_fd, SOL_SOCKET,  SO_REUSEADDR, &optval, sizeof(optval)) == -1)\r\n        return -1;\r\n\r\n    // 设置服务器IP和Port，和监听描述副绑定\r\n    struct sockaddr_in server_addr;\r\n    bzero((char*)&server_addr, sizeof(server_addr));\r\n    server_addr.sin_family = AF_INET;\r\n    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);\r\n    server_addr.sin_port = htons((unsigned short)port);\r\n    if(bind(listen_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1)\r\n        return -1;\r\n\r\n    // 开始监听，最大等待队列长为LISTENQ\r\n    if(listen(listen_fd, LISTENQ) == -1)\r\n        return -1;\r\n\r\n    // 无效监听描述符\r\n    if(listen_fd == -1)\r\n    {\r\n        close(listen_fd);\r\n        return -1;\r\n    }\r\n\r\n    return listen_fd;\r\n}\r\n\r\nvoid myHandler(void *args)\r\n{\r\n    requestData *req_data = (requestData*)args;\r\n    req_data->handleRequest();\r\n}\r\n\r\nvoid acceptConnection(int listen_fd, int epoll_fd, const string &path)\r\n{\r\n    struct sockaddr_in client_addr;\r\n    memset(&client_addr, 0, sizeof(struct sockaddr_in));\r\n    socklen_t client_addr_len = 0;\r\n    int accept_fd = 0;\r\n    while((accept_fd = accept(listen_fd, (struct sockaddr*)&client_addr, &client_addr_len)) > 0)\r\n    {\r\n        /*\r\n        // TCP的保活机制默认是关闭的\r\n        int optval = 0;\r\n        socklen_t len_optval = 4;\r\n        getsockopt(accept_fd, SOL_SOCKET,  SO_KEEPALIVE, &optval, &len_optval);\r\n        cout << \"optval ==\" << optval << endl;\r\n        */\r\n        \r\n        // 设为非阻塞模式\r\n        int ret = setSocketNonBlocking(accept_fd);\r\n        if (ret < 0)\r\n        {\r\n            perror(\"Set non block failed!\");\r\n            return;\r\n        }\r\n\r\n        requestData *req_info = new requestData(epoll_fd, accept_fd, path);\r\n\r\n        // 文件描述符可以读，边缘触发(Edge Triggered)模式，保证一个socket连接在任一时刻只被一个线程处理\r\n        __uint32_t _epo_event = EPOLLIN | EPOLLET | EPOLLONESHOT;\r\n        epoll_add(epoll_fd, accept_fd, static_cast<void*>(req_info), _epo_event);\r\n        // 新增时间信息\r\n        mytimer *mtimer = new mytimer(req_info, TIMER_TIME_OUT);\r\n        req_info->addTimer(mtimer);\r\n        MutexLockGuard();\r\n        myTimerQueue.push(mtimer);\r\n    }\r\n    //if(accept_fd == -1)\r\n     //   perror(\"accept\");\r\n}\r\n// 分发处理函数\r\nvoid handle_events(int epoll_fd, int listen_fd, struct epoll_event* events, int events_num, const string &path, threadpool_t* tp)\r\n{\r\n    for(int i = 0; i < events_num; i++)\r\n    {\r\n        // 获取有事件产生的描述符\r\n        requestData* request = (requestData*)(events[i].data.ptr);\r\n        int fd = request->getFd();\r\n\r\n        // 有事件发生的描述符为监听描述符\r\n        if(fd == listen_fd)\r\n        {\r\n            //cout << \"This is listen_fd\" << endl;\r\n            acceptConnection(listen_fd, epoll_fd, path);\r\n        }\r\n        else\r\n        {\r\n            // 排除错误事件\r\n            if ((events[i].events & EPOLLERR) || (events[i].events & EPOLLHUP)\r\n                || (!(events[i].events & EPOLLIN)))\r\n            {\r\n                printf(\"error event\\n\");\r\n                delete request;\r\n                continue;\r\n            }\r\n\r\n            // 将请求任务加入到线程池中\r\n            // 加入线程池之前将Timer和request分离\r\n            request->seperateTimer();\r\n            int rc = threadpool_add(tp, myHandler, events[i].data.ptr, 0);\r\n        }\r\n    }\r\n}\r\n\r\n/* 处理逻辑是这样的~\r\n因为(1) 优先队列不支持随机访问\r\n(2) 即使支持，随机删除某节点后破坏了堆的结构，需要重新更新堆结构。\r\n所以对于被置为deleted的时间节点，会延迟到它(1)超时 或 (2)它前面的节点都被删除时，它才会被删除。\r\n一个点被置为deleted,它最迟会在TIMER_TIME_OUT时间后被删除。\r\n这样做有两个好处：\r\n(1) 第一个好处是不需要遍历优先队列，省时。\r\n(2) 第二个好处是给超时时间一个容忍的时间，就是设定的超时时间是删除的下限(并不是一到超时时间就立即删除)，如果监听的请求在超时后的下一次请求中又一次出现了，\r\n就不用再重新申请requestData节点了，这样可以继续重复利用前面的requestData，减少了一次delete和一次new的时间。\r\n*/\r\n\r\nvoid handle_expired_event()\r\n{\r\n    MutexLockGuard();\r\n    while (!myTimerQueue.empty())\r\n    {\r\n        mytimer *ptimer_now = myTimerQueue.top();\r\n        if (ptimer_now->isDeleted())\r\n        {\r\n            myTimerQueue.pop();\r\n            delete ptimer_now;\r\n        }\r\n        else if (ptimer_now->isvalid() == false)\r\n        {\r\n            myTimerQueue.pop();\r\n            delete ptimer_now;\r\n        }\r\n        else\r\n        {\r\n            break;\r\n        }\r\n    }\r\n}\r\n\r\nint main()\r\n{\r\n    handle_for_sigpipe();\r\n    int epoll_fd = epoll_init();\r\n    if (epoll_fd < 0)\r\n    {\r\n        perror(\"epoll init failed\");\r\n        return 1;\r\n    }\r\n    threadpool_t *threadpool = threadpool_create(THREADPOOL_THREAD_NUM, QUEUE_SIZE, 0);\r\n    int listen_fd = socket_bind_listen(PORT);\r\n    if (listen_fd < 0) \r\n    {\r\n        perror(\"socket bind failed\");\r\n        return 1;\r\n    }\r\n    if (setSocketNonBlocking(listen_fd) < 0)\r\n    {\r\n        perror(\"set socket non block failed\");\r\n        return 1;\r\n    }\r\n    __uint32_t event = EPOLLIN | EPOLLET;\r\n    requestData *req = new requestData();\r\n    req->setFd(listen_fd);\r\n    epoll_add(epoll_fd, listen_fd, static_cast<void*>(req), event);\r\n    while (true)\r\n    {\r\n        int events_num = my_epoll_wait(epoll_fd, events, MAXEVENTS, -1);\r\n        //printf(\"%zu\\n\", myTimerQueue.size());        \r\n        if (events_num == 0)\r\n            continue;\r\n        printf(\"%d\\n\", events_num);\r\n        //printf(\"%zu\\n\", myTimerQueue.size());    \r\n        // else\r\n        //     cout << \"one connection has come!\" << endl;\r\n        // 遍历events数组，根据监听种类及描述符类型分发操作\r\n        handle_events(epoll_fd, listen_fd, events, events_num, PATH, threadpool);\r\n\r\n        handle_expired_event();\r\n    }\r\n    return 0;\r\n}"
  },
  {
    "path": "old_version/old_version_0.2/requestData.cpp",
    "content": "#include \"requestData.h\"\r\n#include \"util.h\"\r\n#include \"epoll.h\"\r\n#include <sys/epoll.h>\r\n#include <unistd.h>\r\n#include <sys/time.h>\r\n#include <unordered_map>\r\n#include <fcntl.h>\r\n#include <sys/stat.h>\r\n#include <sys/mman.h>\r\n#include <queue>\r\n\r\n#include <opencv/cv.h>\r\n#include <opencv2/core/core.hpp>\r\n#include <opencv2/highgui/highgui.hpp>\r\n#include <opencv2/opencv.hpp>\r\nusing namespace cv;\r\n\r\n//test\r\n#include <iostream>\r\nusing namespace std;\r\n\r\npthread_mutex_t MutexLockGuard::lock = PTHREAD_MUTEX_INITIALIZER;\r\npthread_mutex_t MimeType::lock = PTHREAD_MUTEX_INITIALIZER;\r\nstd::unordered_map<std::string, std::string> MimeType::mime;\r\n\r\nstd::string MimeType::getMime(const std::string &suffix)\r\n{\r\n    if (mime.size() == 0)\r\n    {\r\n        pthread_mutex_lock(&lock);\r\n        if (mime.size() == 0)\r\n        {\r\n            mime[\".html\"] = \"text/html\";\r\n            mime[\".avi\"] = \"video/x-msvideo\";\r\n            mime[\".bmp\"] = \"image/bmp\";\r\n            mime[\".c\"] = \"text/plain\";\r\n            mime[\".doc\"] = \"application/msword\";\r\n            mime[\".gif\"] = \"image/gif\";\r\n            mime[\".gz\"] = \"application/x-gzip\";\r\n            mime[\".htm\"] = \"text/html\";\r\n            mime[\".ico\"] = \"application/x-ico\";\r\n            mime[\".jpg\"] = \"image/jpeg\";\r\n            mime[\".png\"] = \"image/png\";\r\n            mime[\".txt\"] = \"text/plain\";\r\n            mime[\".mp3\"] = \"audio/mp3\";\r\n            mime[\"default\"] = \"text/html\";\r\n        }\r\n        pthread_mutex_unlock(&lock);\r\n    }\r\n    if (mime.find(suffix) == mime.end())\r\n        return mime[\"default\"];\r\n    else\r\n        return mime[suffix];\r\n}\r\n\r\n\r\npriority_queue<mytimer*, deque<mytimer*>, timerCmp> myTimerQueue;\r\n\r\nrequestData::requestData(): \r\n    now_read_pos(0), \r\n    state(STATE_PARSE_URI), \r\n    h_state(h_start), \r\n    keep_alive(false), \r\n    againTimes(0), \r\n    timer(NULL)\r\n{\r\n    cout << \"requestData constructed !\" << endl;\r\n}\r\n\r\nrequestData::requestData(int _epollfd, int _fd, std::string _path):\r\n    now_read_pos(0), \r\n    state(STATE_PARSE_URI), \r\n    h_state(h_start), \r\n    keep_alive(false), \r\n    againTimes(0), \r\n    timer(NULL),\r\n    path(_path), \r\n    fd(_fd), \r\n    epollfd(_epollfd)\r\n{}\r\n\r\nrequestData::~requestData()\r\n{\r\n    cout << \"~requestData()\" << endl;\r\n    struct epoll_event ev;\r\n    // 超时的一定都是读请求，没有\"被动\"写。\r\n    ev.events = EPOLLIN | EPOLLET | EPOLLONESHOT;\r\n    ev.data.ptr = (void*)this;\r\n    epoll_ctl(epollfd, EPOLL_CTL_DEL, fd, &ev);\r\n    if (timer != NULL)\r\n    {\r\n        timer->clearReq();\r\n        timer = NULL;\r\n    }\r\n    close(fd);\r\n}\r\n\r\nvoid requestData::addTimer(mytimer *mtimer)\r\n{\r\n    if (timer == NULL)\r\n        timer = mtimer;\r\n}\r\n\r\nint requestData::getFd()\r\n{\r\n    return fd;\r\n}\r\nvoid requestData::setFd(int _fd)\r\n{\r\n    fd = _fd;\r\n}\r\n\r\nvoid requestData::reset()\r\n{\r\n    againTimes = 0;\r\n    content.clear();\r\n    file_name.clear();\r\n    path.clear();\r\n    now_read_pos = 0;\r\n    state = STATE_PARSE_URI;\r\n    h_state = h_start;\r\n    headers.clear();\r\n    keep_alive = false;\r\n}\r\n\r\nvoid requestData::seperateTimer()\r\n{\r\n    if (timer)\r\n    {\r\n        timer->clearReq();\r\n        timer = NULL;\r\n    }\r\n}\r\n\r\nvoid requestData::handleRequest()\r\n{\r\n    char buff[MAX_BUFF];\r\n    bool isError = false;\r\n    while (true)\r\n    {\r\n        int read_num = readn(fd, buff, MAX_BUFF);\r\n        if (read_num < 0)\r\n        {\r\n            perror(\"1\");\r\n            isError = true;\r\n            break;\r\n        }\r\n        else if (read_num == 0)\r\n        {\r\n            // 有请求出现但是读不到数据，可能是Request Aborted，或者来自网络的数据没有达到等原因\r\n            perror(\"read_num == 0\");\r\n            if (errno == EAGAIN)\r\n            {\r\n                if (againTimes > AGAIN_MAX_TIMES)\r\n                    isError = true;\r\n                else\r\n                    ++againTimes;\r\n            }\r\n            else if (errno != 0)\r\n                isError = true;\r\n            break;\r\n        }\r\n        string now_read(buff, buff + read_num);\r\n        content += now_read;\r\n\r\n        if (state == STATE_PARSE_URI)\r\n        {\r\n            int flag = this->parse_URI();\r\n            if (flag == PARSE_URI_AGAIN)\r\n            {\r\n                break;\r\n            }\r\n            else if (flag == PARSE_URI_ERROR)\r\n            {\r\n                perror(\"2\");\r\n                isError = true;\r\n                break;\r\n            }\r\n        }\r\n        if (state == STATE_PARSE_HEADERS)\r\n        {\r\n            int flag = this->parse_Headers();\r\n            if (flag == PARSE_HEADER_AGAIN)\r\n            {  \r\n                break;\r\n            }\r\n            else if (flag == PARSE_HEADER_ERROR)\r\n            {\r\n                perror(\"3\");\r\n                isError = true;\r\n                break;\r\n            }\r\n            if(method == METHOD_POST)\r\n            {\r\n                state = STATE_RECV_BODY;\r\n            }\r\n            else \r\n            {\r\n                state = STATE_ANALYSIS;\r\n            }\r\n        }\r\n        if (state == STATE_RECV_BODY)\r\n        {\r\n            int content_length = -1;\r\n            if (headers.find(\"Content-length\") != headers.end())\r\n            {\r\n                content_length = stoi(headers[\"Content-length\"]);\r\n            }\r\n            else\r\n            {\r\n                isError = true;\r\n                break;\r\n            }\r\n            if (content.size() < content_length)\r\n                continue;\r\n            state = STATE_ANALYSIS;\r\n        }\r\n        if (state == STATE_ANALYSIS)\r\n        {\r\n            int flag = this->analysisRequest();\r\n            if (flag < 0)\r\n            {\r\n                isError = true;\r\n                break;\r\n            }\r\n            else if (flag == ANALYSIS_SUCCESS)\r\n            {\r\n\r\n                state = STATE_FINISH;\r\n                break;\r\n            }\r\n            else\r\n            {\r\n                isError = true;\r\n                break;\r\n            }\r\n        }\r\n    }\r\n\r\n    if (isError)\r\n    {\r\n        delete this;\r\n        return;\r\n    }\r\n    // 加入epoll继续\r\n    if (state == STATE_FINISH)\r\n    {\r\n        if (keep_alive)\r\n        {\r\n            printf(\"ok\\n\");\r\n            this->reset();\r\n        }\r\n        else\r\n        {\r\n            delete this;\r\n            return;\r\n        }\r\n    }\r\n    // 一定要先加时间信息，否则可能会出现刚加进去，下个in触发来了，然后分离失败后，又加入队列，最后超时被删，然后正在线程中进行的任务出错，double free错误。\r\n    // 新增时间信息\r\n    mytimer *mtimer = new mytimer(this, 500);\r\n    timer = mtimer;\r\n\r\n    {\r\n        MutexLockGuard();\r\n        myTimerQueue.push(mtimer);\r\n    }\r\n\r\n    __uint32_t _epo_event = EPOLLIN | EPOLLET | EPOLLONESHOT;\r\n    int ret = epoll_mod(epollfd, fd, static_cast<void*>(this), _epo_event);\r\n    if (ret < 0)\r\n    {\r\n        // 返回错误处理\r\n        delete this;\r\n        return;\r\n    }\r\n}\r\n\r\nint requestData::parse_URI()\r\n{\r\n    string &str = content;\r\n    // 读到完整的请求行再开始解析请求\r\n    int pos = str.find('\\r', now_read_pos);\r\n    if (pos < 0)\r\n    {\r\n        return PARSE_URI_AGAIN;\r\n    }\r\n    // 去掉请求行所占的空间，节省空间\r\n    string request_line = str.substr(0, pos);\r\n    if (str.size() > pos + 1)\r\n        str = str.substr(pos + 1);\r\n    else \r\n        str.clear();\r\n    // Method\r\n    pos = request_line.find(\"GET\");\r\n    if (pos < 0)\r\n    {\r\n        pos = request_line.find(\"POST\");\r\n        if (pos < 0)\r\n        {\r\n            return PARSE_URI_ERROR;\r\n        }\r\n        else\r\n        {\r\n            method = METHOD_POST;\r\n        }\r\n    }\r\n    else\r\n    {\r\n        method = METHOD_GET;\r\n    }\r\n    //printf(\"method = %d\\n\", method);\r\n    // filename\r\n    pos = request_line.find(\"/\", pos);\r\n    if (pos < 0)\r\n    {\r\n        return PARSE_URI_ERROR;\r\n    }\r\n    else\r\n    {\r\n        int _pos = request_line.find(' ', pos);\r\n        if (_pos < 0)\r\n            return PARSE_URI_ERROR;\r\n        else\r\n        {\r\n            if (_pos - pos > 1)\r\n            {\r\n                file_name = request_line.substr(pos + 1, _pos - pos - 1);\r\n                int __pos = file_name.find('?');\r\n                if (__pos >= 0)\r\n                {\r\n                    file_name = file_name.substr(0, __pos);\r\n                }\r\n            }\r\n                \r\n            else\r\n                file_name = \"index.html\";\r\n        }\r\n        pos = _pos;\r\n    }\r\n    //cout << \"file_name: \" << file_name << endl;\r\n    // HTTP 版本号\r\n    pos = request_line.find(\"/\", pos);\r\n    if (pos < 0)\r\n    {\r\n        return PARSE_URI_ERROR;\r\n    }\r\n    else\r\n    {\r\n        if (request_line.size() - pos <= 3)\r\n        {\r\n            return PARSE_URI_ERROR;\r\n        }\r\n        else\r\n        {\r\n            string ver = request_line.substr(pos + 1, 3);\r\n            if (ver == \"1.0\")\r\n                HTTPversion = HTTP_10;\r\n            else if (ver == \"1.1\")\r\n                HTTPversion = HTTP_11;\r\n            else\r\n                return PARSE_URI_ERROR;\r\n        }\r\n    }\r\n    state = STATE_PARSE_HEADERS;\r\n    return PARSE_URI_SUCCESS;\r\n}\r\n\r\nint requestData::parse_Headers()\r\n{\r\n    string &str = content;\r\n    int key_start = -1, key_end = -1, value_start = -1, value_end = -1;\r\n    int now_read_line_begin = 0;\r\n    bool notFinish = true;\r\n    for (int i = 0; i < str.size() && notFinish; ++i)\r\n    {\r\n        switch(h_state)\r\n        {\r\n            case h_start:\r\n            {\r\n                if (str[i] == '\\n' || str[i] == '\\r')\r\n                    break;\r\n                h_state = h_key;\r\n                key_start = i;\r\n                now_read_line_begin = i;\r\n                break;\r\n            }\r\n            case h_key:\r\n            {\r\n                if (str[i] == ':')\r\n                {\r\n                    key_end = i;\r\n                    if (key_end - key_start <= 0)\r\n                        return PARSE_HEADER_ERROR;\r\n                    h_state = h_colon;\r\n                }\r\n                else if (str[i] == '\\n' || str[i] == '\\r')\r\n                    return PARSE_HEADER_ERROR;\r\n                break;  \r\n            }\r\n            case h_colon:\r\n            {\r\n                if (str[i] == ' ')\r\n                {\r\n                    h_state = h_spaces_after_colon;\r\n                }\r\n                else\r\n                    return PARSE_HEADER_ERROR;\r\n                break;  \r\n            }\r\n            case h_spaces_after_colon:\r\n            {\r\n                h_state = h_value;\r\n                value_start = i;\r\n                break;  \r\n            }\r\n            case h_value:\r\n            {\r\n                if (str[i] == '\\r')\r\n                {\r\n                    h_state = h_CR;\r\n                    value_end = i;\r\n                    if (value_end - value_start <= 0)\r\n                        return PARSE_HEADER_ERROR;\r\n                }\r\n                else if (i - value_start > 255)\r\n                    return PARSE_HEADER_ERROR;\r\n                break;  \r\n            }\r\n            case h_CR:\r\n            {\r\n                if (str[i] == '\\n')\r\n                {\r\n                    h_state = h_LF;\r\n                    string key(str.begin() + key_start, str.begin() + key_end);\r\n                    string value(str.begin() + value_start, str.begin() + value_end);\r\n                    headers[key] = value;\r\n                    now_read_line_begin = i;\r\n                }\r\n                else\r\n                    return PARSE_HEADER_ERROR;\r\n                break;  \r\n            }\r\n            case h_LF:\r\n            {\r\n                if (str[i] == '\\r')\r\n                {\r\n                    h_state = h_end_CR;\r\n                }\r\n                else\r\n                {\r\n                    key_start = i;\r\n                    h_state = h_key;\r\n                }\r\n                break;\r\n            }\r\n            case h_end_CR:\r\n            {\r\n                if (str[i] == '\\n')\r\n                {\r\n                    h_state = h_end_LF;\r\n                }\r\n                else\r\n                    return PARSE_HEADER_ERROR;\r\n                break;\r\n            }\r\n            case h_end_LF:\r\n            {\r\n                notFinish = false;\r\n                key_start = i;\r\n                now_read_line_begin = i;\r\n                break;\r\n            }\r\n        }\r\n    }\r\n    if (h_state == h_end_LF)\r\n    {\r\n        str = str.substr(now_read_line_begin);\r\n        return PARSE_HEADER_SUCCESS;\r\n    }\r\n    str = str.substr(now_read_line_begin);\r\n    return PARSE_HEADER_AGAIN;\r\n}\r\n\r\nint requestData::analysisRequest()\r\n{\r\n    if (method == METHOD_POST)\r\n    {\r\n        //get content\r\n        char header[MAX_BUFF];\r\n        sprintf(header, \"HTTP/1.1 %d %s\\r\\n\", 200, \"OK\");\r\n        if(headers.find(\"Connection\") != headers.end() && headers[\"Connection\"] == \"keep-alive\")\r\n        {\r\n            keep_alive = true;\r\n            sprintf(header, \"%sConnection: keep-alive\\r\\n\", header);\r\n            sprintf(header, \"%sKeep-Alive: timeout=%d\\r\\n\", header, EPOLL_WAIT_TIME);\r\n        }\r\n        //cout << \"content=\" << content << endl;\r\n        // test char*\r\n        char *send_content = \"I have receiced this.\";\r\n\r\n        sprintf(header, \"%sContent-length: %zu\\r\\n\", header, strlen(send_content));\r\n        sprintf(header, \"%s\\r\\n\", header);\r\n        size_t send_len = (size_t)writen(fd, header, strlen(header));\r\n        if(send_len != strlen(header))\r\n        {\r\n            perror(\"Send header failed\");\r\n            return ANALYSIS_ERROR;\r\n        }\r\n        \r\n        send_len = (size_t)writen(fd, send_content, strlen(send_content));\r\n        if(send_len != strlen(send_content))\r\n        {\r\n            perror(\"Send content failed\");\r\n            return ANALYSIS_ERROR;\r\n        }\r\n        cout << \"content size ==\" << content.size() << endl;\r\n        vector<char> data(content.begin(), content.end());\r\n        Mat test = imdecode(data, CV_LOAD_IMAGE_ANYDEPTH|CV_LOAD_IMAGE_ANYCOLOR);\r\n        imwrite(\"receive.bmp\", test);\r\n        return ANALYSIS_SUCCESS;\r\n    }\r\n    else if (method == METHOD_GET)\r\n    {\r\n        char header[MAX_BUFF];\r\n        sprintf(header, \"HTTP/1.1 %d %s\\r\\n\", 200, \"OK\");\r\n        if(headers.find(\"Connection\") != headers.end() && headers[\"Connection\"] == \"keep-alive\")\r\n        {\r\n            keep_alive = true;\r\n            sprintf(header, \"%sConnection: keep-alive\\r\\n\", header);\r\n            sprintf(header, \"%sKeep-Alive: timeout=%d\\r\\n\", header, EPOLL_WAIT_TIME);\r\n        }\r\n        int dot_pos = file_name.find('.');\r\n        const char* filetype;\r\n        if (dot_pos < 0) \r\n            filetype = MimeType::getMime(\"default\").c_str();\r\n        else\r\n            filetype = MimeType::getMime(file_name.substr(dot_pos)).c_str();\r\n        struct stat sbuf;\r\n        if (stat(file_name.c_str(), &sbuf) < 0)\r\n        {\r\n            handleError(fd, 404, \"Not Found!\");\r\n            return ANALYSIS_ERROR;\r\n        }\r\n\r\n        sprintf(header, \"%sContent-type: %s\\r\\n\", header, filetype);\r\n        // 通过Content-length返回文件大小\r\n        sprintf(header, \"%sContent-length: %ld\\r\\n\", header, sbuf.st_size);\r\n\r\n        sprintf(header, \"%s\\r\\n\", header);\r\n        size_t send_len = (size_t)writen(fd, header, strlen(header));\r\n        if(send_len != strlen(header))\r\n        {\r\n            perror(\"Send header failed\");\r\n            return ANALYSIS_ERROR;\r\n        }\r\n        int src_fd = open(file_name.c_str(), O_RDONLY, 0);\r\n        char *src_addr = static_cast<char*>(mmap(NULL, sbuf.st_size, PROT_READ, MAP_PRIVATE, src_fd, 0));\r\n        close(src_fd);\r\n    \r\n        // 发送文件并校验完整性\r\n        send_len = writen(fd, src_addr, sbuf.st_size);\r\n        if(send_len != sbuf.st_size)\r\n        {\r\n            perror(\"Send file failed\");\r\n            return ANALYSIS_ERROR;\r\n        }\r\n        munmap(src_addr, sbuf.st_size);\r\n        return ANALYSIS_SUCCESS;\r\n    }\r\n    else\r\n        return ANALYSIS_ERROR;\r\n}\r\n\r\nvoid requestData::handleError(int fd, int err_num, string short_msg)\r\n{\r\n    short_msg = \" \" + short_msg;\r\n    char send_buff[MAX_BUFF];\r\n    string body_buff, header_buff;\r\n    body_buff += \"<html><title>TKeed Error</title>\";\r\n    body_buff += \"<body bgcolor=\\\"ffffff\\\">\";\r\n    body_buff += to_string(err_num) + short_msg;\r\n    body_buff += \"<hr><em> LinYa's Web Server</em>\\n</body></html>\";\r\n\r\n    header_buff += \"HTTP/1.1 \" + to_string(err_num) + short_msg + \"\\r\\n\";\r\n    header_buff += \"Content-type: text/html\\r\\n\";\r\n    header_buff += \"Connection: close\\r\\n\";\r\n    header_buff += \"Content-length: \" + to_string(body_buff.size()) + \"\\r\\n\";\r\n    header_buff += \"\\r\\n\";\r\n    sprintf(send_buff, \"%s\", header_buff.c_str());\r\n    writen(fd, send_buff, strlen(send_buff));\r\n    sprintf(send_buff, \"%s\", body_buff.c_str());\r\n    writen(fd, send_buff, strlen(send_buff));\r\n}\r\n\r\nmytimer::mytimer(requestData *_request_data, int timeout): deleted(false), request_data(_request_data)\r\n{\r\n    //cout << \"mytimer()\" << endl;\r\n    struct timeval now;\r\n    gettimeofday(&now, NULL);\r\n    // 以毫秒计\r\n    expired_time = ((now.tv_sec * 1000) + (now.tv_usec / 1000)) + timeout;\r\n}\r\n\r\nmytimer::~mytimer()\r\n{\r\n    cout << \"~mytimer()\" << endl;\r\n    if (request_data != NULL)\r\n    {\r\n        cout << \"request_data=\" << request_data << endl;\r\n        delete request_data;\r\n        request_data = NULL;\r\n    }\r\n}\r\n\r\nvoid mytimer::update(int timeout)\r\n{\r\n    struct timeval now;\r\n    gettimeofday(&now, NULL);\r\n    expired_time = ((now.tv_sec * 1000) + (now.tv_usec / 1000)) + timeout;\r\n}\r\n\r\nbool mytimer::isvalid()\r\n{\r\n    struct timeval now;\r\n    gettimeofday(&now, NULL);\r\n    size_t temp = ((now.tv_sec * 1000) + (now.tv_usec / 1000));\r\n    if (temp < expired_time)\r\n    {\r\n        return true;\r\n    }\r\n    else\r\n    {\r\n        this->setDeleted();\r\n        return false;\r\n    }\r\n}\r\n\r\nvoid mytimer::clearReq()\r\n{\r\n    request_data = NULL;\r\n    this->setDeleted();\r\n}\r\n\r\nvoid mytimer::setDeleted()\r\n{\r\n    deleted = true;\r\n}\r\n\r\nbool mytimer::isDeleted() const\r\n{\r\n    return deleted;\r\n}\r\n\r\nsize_t mytimer::getExpTime() const\r\n{\r\n    return expired_time;\r\n}\r\n\r\nbool timerCmp::operator()(const mytimer *a, const mytimer *b) const\r\n{\r\n    return a->getExpTime() > b->getExpTime();\r\n}\r\n\r\n\r\nMutexLockGuard::MutexLockGuard()\r\n{\r\n    pthread_mutex_lock(&lock);\r\n}\r\n\r\nMutexLockGuard::~MutexLockGuard()\r\n{\r\n    pthread_mutex_unlock(&lock);\r\n}"
  },
  {
    "path": "old_version/old_version_0.2/requestData.h",
    "content": "#ifndef REQUESTDATA\r\n#define REQUESTDATA\r\n#include <string>\r\n#include <unordered_map>\r\n\r\nconst int STATE_PARSE_URI = 1;\r\nconst int STATE_PARSE_HEADERS = 2;\r\nconst int STATE_RECV_BODY = 3;\r\nconst int STATE_ANALYSIS = 4;\r\nconst int STATE_FINISH = 5;\r\n\r\nconst int MAX_BUFF = 4096;\r\n\r\n// 有请求出现但是读不到数据,可能是Request Aborted,\r\n// 或者来自网络的数据没有达到等原因,\r\n// 对这样的请求尝试超过一定的次数就抛弃\r\nconst int AGAIN_MAX_TIMES = 200;\r\n\r\nconst int PARSE_URI_AGAIN = -1;\r\nconst int PARSE_URI_ERROR = -2;\r\nconst int PARSE_URI_SUCCESS = 0;\r\n\r\nconst int PARSE_HEADER_AGAIN = -1;\r\nconst int PARSE_HEADER_ERROR = -2;\r\nconst int PARSE_HEADER_SUCCESS = 0;\r\n\r\nconst int ANALYSIS_ERROR = -2;\r\nconst int ANALYSIS_SUCCESS = 0;\r\n\r\nconst int METHOD_POST = 1;\r\nconst int METHOD_GET = 2;\r\nconst int HTTP_10 = 1;\r\nconst int HTTP_11 = 2;\r\n\r\nconst int EPOLL_WAIT_TIME = 500;\r\n\r\nclass MimeType\r\n{\r\nprivate:\r\n    static pthread_mutex_t lock;\r\n    static std::unordered_map<std::string, std::string> mime;\r\n    MimeType();\r\n    MimeType(const MimeType &m);\r\npublic:\r\n    static std::string getMime(const std::string &suffix);\r\n};\r\n\r\nenum HeadersState\r\n{\r\n    h_start = 0,\r\n    h_key,\r\n    h_colon,\r\n    h_spaces_after_colon,\r\n    h_value,\r\n    h_CR,\r\n    h_LF,\r\n    h_end_CR,\r\n    h_end_LF\r\n};\r\n\r\nstruct mytimer;\r\nstruct requestData;\r\n\r\nstruct requestData\r\n{\r\nprivate:\r\n    int againTimes;\r\n    std::string path;\r\n    int fd;\r\n    int epollfd;\r\n    // content的内容用完就清\r\n    std::string content;\r\n    int method;\r\n    int HTTPversion;\r\n    std::string file_name;\r\n    int now_read_pos;\r\n    int state;\r\n    int h_state;\r\n    bool isfinish;\r\n    bool keep_alive;\r\n    std::unordered_map<std::string, std::string> headers;\r\n    mytimer *timer;\r\n\r\nprivate:\r\n    int parse_URI();\r\n    int parse_Headers();\r\n    int analysisRequest();\r\n\r\npublic:\r\n\r\n    requestData();\r\n    requestData(int _epollfd, int _fd, std::string _path);\r\n    ~requestData();\r\n    void addTimer(mytimer *mtimer);\r\n    void reset();\r\n    void seperateTimer();\r\n    int getFd();\r\n    void setFd(int _fd);\r\n    void handleRequest();\r\n    void handleError(int fd, int err_num, std::string short_msg);\r\n};\r\n\r\nstruct mytimer\r\n{\r\n    bool deleted;\r\n    size_t expired_time;\r\n    requestData *request_data;\r\n\r\n    mytimer(requestData *_request_data, int timeout);\r\n    ~mytimer();\r\n    void update(int timeout);\r\n    bool isvalid();\r\n    void clearReq();\r\n    void setDeleted();\r\n    bool isDeleted() const;\r\n    size_t getExpTime() const;\r\n};\r\n\r\nstruct timerCmp\r\n{\r\n    bool operator()(const mytimer *a, const mytimer *b) const;\r\n};\r\n\r\n\r\nclass MutexLockGuard\r\n{\r\npublic:\r\n    explicit MutexLockGuard();\r\n    ~MutexLockGuard();\r\n\r\nprivate:\r\n    static pthread_mutex_t lock;\r\n\r\nprivate:\r\n    MutexLockGuard(const MutexLockGuard&);\r\n    MutexLockGuard& operator=(const MutexLockGuard&);\r\n};\r\n#endif"
  },
  {
    "path": "old_version/old_version_0.2/threadpool.cpp",
    "content": "#include \"threadpool.h\"\r\n\r\nthreadpool_t *threadpool_create(int thread_count, int queue_size, int flags)\r\n{\r\n    threadpool_t *pool;\r\n    int i;\r\n    //(void) flags;\r\n    do\r\n    {\r\n        if(thread_count <= 0 || thread_count > MAX_THREADS || queue_size <= 0 || queue_size > MAX_QUEUE) {\r\n            return NULL;\r\n        }\r\n    \r\n        if((pool = (threadpool_t *)malloc(sizeof(threadpool_t))) == NULL) \r\n        {\r\n            break;\r\n        }\r\n    \r\n        /* Initialize */\r\n        pool->thread_count = 0;\r\n        pool->queue_size = queue_size;\r\n        pool->head = pool->tail = pool->count = 0;\r\n        pool->shutdown = pool->started = 0;\r\n    \r\n        /* Allocate thread and task queue */\r\n        pool->threads = (pthread_t *)malloc(sizeof(pthread_t) * thread_count);\r\n        pool->queue = (threadpool_task_t *)malloc(sizeof(threadpool_task_t) * queue_size);\r\n    \r\n        /* Initialize mutex and conditional variable first */\r\n        if((pthread_mutex_init(&(pool->lock), NULL) != 0) ||\r\n           (pthread_cond_init(&(pool->notify), NULL) != 0) ||\r\n           (pool->threads == NULL) ||\r\n           (pool->queue == NULL)) \r\n        {\r\n            break;\r\n        }\r\n    \r\n        /* Start worker threads */\r\n        for(i = 0; i < thread_count; i++) {\r\n            if(pthread_create(&(pool->threads[i]), NULL, threadpool_thread, (void*)pool) != 0) \r\n            {\r\n                threadpool_destroy(pool, 0);\r\n                return NULL;\r\n            }\r\n            pool->thread_count++;\r\n            pool->started++;\r\n        }\r\n    \r\n        return pool;\r\n    } while(false);\r\n    \r\n    if (pool != NULL) \r\n    {\r\n        threadpool_free(pool);\r\n    }\r\n    return NULL;\r\n}\r\n\r\nint threadpool_add(threadpool_t *pool, void (*function)(void *), void *argument, int flags)\r\n{\r\n    //printf(\"add to thread pool !\\n\");\r\n    int err = 0;\r\n    int next;\r\n    //(void) flags;\r\n    if(pool == NULL || function == NULL)\r\n    {\r\n        return THREADPOOL_INVALID;\r\n    }\r\n    if(pthread_mutex_lock(&(pool->lock)) != 0)\r\n    {\r\n        return THREADPOOL_LOCK_FAILURE;\r\n    }\r\n    next = (pool->tail + 1) % pool->queue_size;\r\n    do \r\n    {\r\n        /* Are we full ? */\r\n        if(pool->count == pool->queue_size) {\r\n            err = THREADPOOL_QUEUE_FULL;\r\n            break;\r\n        }\r\n        /* Are we shutting down ? */\r\n        if(pool->shutdown) {\r\n            err = THREADPOOL_SHUTDOWN;\r\n            break;\r\n        }\r\n        /* Add task to queue */\r\n        pool->queue[pool->tail].function = function;\r\n        pool->queue[pool->tail].argument = argument;\r\n        pool->tail = next;\r\n        pool->count += 1;\r\n        \r\n        /* pthread_cond_broadcast */\r\n        if(pthread_cond_signal(&(pool->notify)) != 0) {\r\n            err = THREADPOOL_LOCK_FAILURE;\r\n            break;\r\n        }\r\n    } while(false);\r\n\r\n    if(pthread_mutex_unlock(&pool->lock) != 0) {\r\n        err = THREADPOOL_LOCK_FAILURE;\r\n    }\r\n\r\n    return err;\r\n}\r\n\r\nint threadpool_destroy(threadpool_t *pool, int flags)\r\n{\r\n    printf(\"Thread pool destroy !\\n\");\r\n    int i, err = 0;\r\n\r\n    if(pool == NULL)\r\n    {\r\n        return THREADPOOL_INVALID;\r\n    }\r\n\r\n    if(pthread_mutex_lock(&(pool->lock)) != 0) \r\n    {\r\n        return THREADPOOL_LOCK_FAILURE;\r\n    }\r\n\r\n    do \r\n    {\r\n        /* Already shutting down */\r\n        if(pool->shutdown) {\r\n            err = THREADPOOL_SHUTDOWN;\r\n            break;\r\n        }\r\n\r\n        pool->shutdown = (flags & THREADPOOL_GRACEFUL) ?\r\n            graceful_shutdown : immediate_shutdown;\r\n\r\n        /* Wake up all worker threads */\r\n        if((pthread_cond_broadcast(&(pool->notify)) != 0) ||\r\n           (pthread_mutex_unlock(&(pool->lock)) != 0)) {\r\n            err = THREADPOOL_LOCK_FAILURE;\r\n            break;\r\n        }\r\n\r\n        /* Join all worker thread */\r\n        for(i = 0; i < pool->thread_count; ++i)\r\n        {\r\n            if(pthread_join(pool->threads[i], NULL) != 0)\r\n            {\r\n                err = THREADPOOL_THREAD_FAILURE;\r\n            }\r\n        }\r\n    } while(false);\r\n\r\n    /* Only if everything went well do we deallocate the pool */\r\n    if(!err) \r\n    {\r\n        threadpool_free(pool);\r\n    }\r\n    return err;\r\n}\r\n\r\nint threadpool_free(threadpool_t *pool)\r\n{\r\n    if(pool == NULL || pool->started > 0)\r\n    {\r\n        return -1;\r\n    }\r\n\r\n    /* Did we manage to allocate ? */\r\n    if(pool->threads) \r\n    {\r\n        free(pool->threads);\r\n        free(pool->queue);\r\n \r\n        /* Because we allocate pool->threads after initializing the\r\n           mutex and condition variable, we're sure they're\r\n           initialized. Let's lock the mutex just in case. */\r\n        pthread_mutex_lock(&(pool->lock));\r\n        pthread_mutex_destroy(&(pool->lock));\r\n        pthread_cond_destroy(&(pool->notify));\r\n    }\r\n    free(pool);    \r\n    return 0;\r\n}\r\n\r\n\r\nstatic void *threadpool_thread(void *threadpool)\r\n{\r\n    threadpool_t *pool = (threadpool_t *)threadpool;\r\n    threadpool_task_t task;\r\n\r\n    for(;;)\r\n    {\r\n        /* Lock must be taken to wait on conditional variable */\r\n        pthread_mutex_lock(&(pool->lock));\r\n\r\n        /* Wait on condition variable, check for spurious wakeups.\r\n           When returning from pthread_cond_wait(), we own the lock. */\r\n        while((pool->count == 0) && (!pool->shutdown)) \r\n        {\r\n            pthread_cond_wait(&(pool->notify), &(pool->lock));\r\n        }\r\n\r\n        if((pool->shutdown == immediate_shutdown) ||\r\n           ((pool->shutdown == graceful_shutdown) &&\r\n            (pool->count == 0)))\r\n        {\r\n            break;\r\n        }\r\n\r\n        /* Grab our task */\r\n        task.function = pool->queue[pool->head].function;\r\n        task.argument = pool->queue[pool->head].argument;\r\n        pool->head = (pool->head + 1) % pool->queue_size;\r\n        pool->count -= 1;\r\n\r\n        /* Unlock */\r\n        pthread_mutex_unlock(&(pool->lock));\r\n\r\n        /* Get to work */\r\n        (*(task.function))(task.argument);\r\n    }\r\n\r\n    --pool->started;\r\n\r\n    pthread_mutex_unlock(&(pool->lock));\r\n    pthread_exit(NULL);\r\n    return(NULL);\r\n}"
  },
  {
    "path": "old_version/old_version_0.2/threadpool.h",
    "content": "#ifndef THREADPOOL\r\n#define THREADPOOL\r\n#include \"requestData.h\"\r\n#include <pthread.h>\r\n\r\nconst int THREADPOOL_INVALID = -1;\r\nconst int THREADPOOL_LOCK_FAILURE = -2;\r\nconst int THREADPOOL_QUEUE_FULL = -3;\r\nconst int THREADPOOL_SHUTDOWN = -4;\r\nconst int THREADPOOL_THREAD_FAILURE = -5;\r\nconst int THREADPOOL_GRACEFUL = 1;\r\n\r\nconst int MAX_THREADS = 1024;\r\nconst int MAX_QUEUE = 65535;\r\n\r\ntypedef enum \r\n{\r\n    immediate_shutdown = 1,\r\n    graceful_shutdown  = 2\r\n} threadpool_shutdown_t;\r\n\r\n/**\r\n *  @struct threadpool_task\r\n *  @brief the work struct\r\n *\r\n *  @var function Pointer to the function that will perform the task.\r\n *  @var argument Argument to be passed to the function.\r\n */\r\n\r\ntypedef struct {\r\n    void (*function)(void *);\r\n    void *argument;\r\n} threadpool_task_t;\r\n\r\n/**\r\n *  @struct threadpool\r\n *  @brief The threadpool struct\r\n *\r\n *  @var notify       Condition variable to notify worker threads.\r\n *  @var threads      Array containing worker threads ID.\r\n *  @var thread_count Number of threads\r\n *  @var queue        Array containing the task queue.\r\n *  @var queue_size   Size of the task queue.\r\n *  @var head         Index of the first element.\r\n *  @var tail         Index of the next element.\r\n *  @var count        Number of pending tasks\r\n *  @var shutdown     Flag indicating if the pool is shutting down\r\n *  @var started      Number of started threads\r\n */\r\nstruct threadpool_t\r\n{\r\n    pthread_mutex_t lock;\r\n    pthread_cond_t notify;\r\n    pthread_t *threads;\r\n    threadpool_task_t *queue;\r\n    int thread_count;\r\n    int queue_size;\r\n    int head;\r\n    int tail;\r\n    int count;\r\n    int shutdown;\r\n    int started;\r\n};\r\n\r\nthreadpool_t *threadpool_create(int thread_count, int queue_size, int flags);\r\nint threadpool_add(threadpool_t *pool, void (*function)(void *), void *argument, int flags);\r\nint threadpool_destroy(threadpool_t *pool, int flags);\r\nint threadpool_free(threadpool_t *pool);\r\nstatic void *threadpool_thread(void *threadpool);\r\n\r\n#endif"
  },
  {
    "path": "old_version/old_version_0.2/util.cpp",
    "content": "#include \"util.h\"\r\n#include <unistd.h>\r\n#include <fcntl.h>\r\n#include <signal.h>\r\n#include <errno.h>\r\n#include <string.h>\r\n\r\nssize_t readn(int fd, void *buff, size_t n)\r\n{\r\n    size_t nleft = n;\r\n    ssize_t nread = 0;\r\n    ssize_t readSum = 0;\r\n    char *ptr = (char*)buff;\r\n    while (nleft > 0)\r\n    {\r\n        if ((nread = read(fd, ptr, nleft)) < 0)\r\n        {\r\n            if (errno == EINTR)\r\n                nread = 0;\r\n            else if (errno == EAGAIN)\r\n            {\r\n                return readSum;\r\n            }\r\n            else\r\n            {\r\n                return -1;\r\n            }  \r\n        }\r\n        else if (nread == 0)\r\n            break;\r\n        readSum += nread;\r\n        nleft -= nread;\r\n        ptr += nread;\r\n    }\r\n    return readSum;\r\n}\r\n\r\nssize_t writen(int fd, void *buff, size_t n)\r\n{\r\n    size_t nleft = n;\r\n    ssize_t nwritten = 0;\r\n    ssize_t writeSum = 0;\r\n    char *ptr = (char*)buff;\r\n    while (nleft > 0)\r\n    {\r\n        if ((nwritten = write(fd, ptr, nleft)) <= 0)\r\n        {\r\n            if (nwritten < 0)\r\n            {\r\n                if (errno == EINTR || errno == EAGAIN)\r\n                {\r\n                    nwritten = 0;\r\n                    continue;\r\n                }\r\n                else\r\n                    return -1;\r\n            }\r\n        }\r\n        writeSum += nwritten;\r\n        nleft -= nwritten;\r\n        ptr += nwritten;\r\n    }\r\n    return writeSum;\r\n}\r\n\r\nvoid handle_for_sigpipe()\r\n{\r\n    struct sigaction sa;\r\n    memset(&sa, '\\0', sizeof(sa));\r\n    sa.sa_handler = SIG_IGN;\r\n    sa.sa_flags = 0;\r\n    if(sigaction(SIGPIPE, &sa, NULL))\r\n        return;\r\n}\r\n\r\nint setSocketNonBlocking(int fd)\r\n{\r\n    int flag = fcntl(fd, F_GETFL, 0);\r\n    if(flag == -1)\r\n        return -1;\r\n\r\n    flag |= O_NONBLOCK;\r\n    if(fcntl(fd, F_SETFL, flag) == -1)\r\n        return -1;\r\n    return 0;\r\n}"
  },
  {
    "path": "old_version/old_version_0.2/util.h",
    "content": "#ifndef UTIL\r\n#define UTIL\r\n#include <cstdlib>\r\n\r\nssize_t readn(int fd, void *buff, size_t n);\r\nssize_t writen(int fd, void *buff, size_t n);\r\nvoid handle_for_sigpipe();\r\nint setSocketNonBlocking(int fd);\r\n\r\n#endif"
  },
  {
    "path": "old_version/old_version_0.3/Makefile",
    "content": "SOURCE  := $(wildcard *.cpp)\r\nOBJS    := $(patsubst %.c,%.o,$(patsubst %.cpp,%.o,$(SOURCE)))\r\n\r\nTARGET  := myserver\r\nCC      := g++\r\nLIBS    := -lpthread -lopencv_core -lopencv_imgproc -lopencv_highgui\r\nINCLUDE:= -I./usr/local/include/opencv\r\nCFLAGS  := -std=c++11 -g -Wall -O3 $(INCLUDE)\r\nCXXFLAGS:= $(CFLAGS)\r\n\r\n.PHONY : objs clean veryclean rebuild all\r\nall : $(TARGET)\r\nobjs : $(OBJS)\r\nrebuild: veryclean all\r\nclean :\r\n\trm -fr *.o\r\nveryclean : clean\r\n\trm -rf $(TARGET)\r\n\r\n$(TARGET) : $(OBJS)\r\n\t$(CC) $(CXXFLAGS) -o $@ $(OBJS) $(LDFLAGS) $(LIBS)"
  },
  {
    "path": "old_version/old_version_0.3/Makefile1",
    "content": "cc=g++ -std=c++11\r\nmain: main.o requestData.o epoll.o threadpool.o\r\n\t$(cc) -o main main.o requestData.o epoll.o threadpool.o -lpthread -lopencv_core -lopencv_imgproc -lopencv_highgui -lopencv_ml \r\nmain.o: main.cpp\r\n\t$(cc) -I/usr/local/include/opencv -c main.cpp\r\nrequestData.o: requestData.h requestData.cpp\r\n\t$(cc) -I/usr/local/include/opencv -c requestData.cpp\r\nepoll.o: epoll.h epoll.cpp\r\n\t$(cc) -I/usr/local/include/opencv -c epoll.cpp\r\nthreadpool.o: threadpool.h threadpool.cpp\r\n\t$(cc) -I/usr/local/include/opencv -c threadpool.cpp\r\n.PHONY: clean\r\nclean:\r\n\t-rm main *.o\r\n\r\n\r\n\r\n\r\n.PHONY : everything objs clean veryclean rebuild\r\neverything :\r\n\t$(TARGET)\r\nall :\r\n\t$(TARGET)\r\nobjs :\r\n\t$(OBJS)\r\nrebuild:\r\n\tveryclean everything \r\nclean :\r\n\trm -fr *.o\r\nveryclean :\r\n\tclean rm -fr $(TARGET)"
  },
  {
    "path": "old_version/old_version_0.3/config.h",
    "content": "#pragma once\r\n\r\n"
  },
  {
    "path": "old_version/old_version_0.3/epoll.cpp",
    "content": "#include \"epoll.h\"\r\n#include \"threadpool.h\"\r\n#include \"util.h\"\r\n#include <sys/epoll.h>\r\n#include <errno.h>\r\n#include <sys/socket.h>\r\n#include <netinet/in.h>\r\n#include <string.h>\r\n#include <queue>\r\n#include <deque>\r\n\r\nint TIMER_TIME_OUT = 500;\r\nextern std::priority_queue<std::shared_ptr<mytimer>, std::deque<std::shared_ptr<mytimer>>, timerCmp> myTimerQueue;\r\n\r\nepoll_event *Epoll::events;\r\nstd::unordered_map<int, std::shared_ptr<requestData>> Epoll::fd2req;\r\nint Epoll::epoll_fd = 0;\r\nconst std::string Epoll::PATH = \"/\";\r\n\r\nint Epoll::epoll_init(int maxevents, int listen_num)\r\n{\r\n    epoll_fd = epoll_create(listen_num + 1);\r\n    if(epoll_fd == -1)\r\n        return -1;\r\n    //events.reset(new epoll_event[maxevents], [](epoll_event *data){delete [] data;});\r\n    events = new epoll_event[maxevents];\r\n    return 0;\r\n}\r\n\r\n// 注册新描述符\r\nint Epoll::epoll_add(int fd, std::shared_ptr<requestData> request, __uint32_t events)\r\n{\r\n    struct epoll_event event;\r\n    event.data.fd = fd;\r\n    event.events = events;\r\n    if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &event) < 0)\r\n    {\r\n        perror(\"epoll_add error\");\r\n        return -1;\r\n    }\r\n    fd2req[fd] = request;\r\n    return 0;\r\n}\r\n\r\n// 修改描述符状态\r\nint Epoll::epoll_mod(int fd, std::shared_ptr<requestData> request, __uint32_t events)\r\n{\r\n    struct epoll_event event;\r\n    event.data.fd = fd;\r\n    event.events = events;\r\n    if(epoll_ctl(epoll_fd, EPOLL_CTL_MOD, fd, &event) < 0)\r\n    {\r\n        perror(\"epoll_mod error\");\r\n        return -1;\r\n    }\r\n    fd2req[fd] = request;\r\n    return 0;\r\n}\r\n\r\n// 从epoll中删除描述符\r\nint Epoll::epoll_del(int fd, __uint32_t events)\r\n{\r\n    struct epoll_event event;\r\n    event.data.fd = fd;\r\n    event.events = events;\r\n    if(epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fd, &event) < 0)\r\n    {\r\n        perror(\"epoll_del error\");\r\n        return -1;\r\n    }\r\n    auto fd_ite = fd2req.find(fd);\r\n    if (fd_ite != fd2req.end())\r\n        fd2req.erase(fd_ite);\r\n    return 0;\r\n}\r\n\r\n// 返回活跃事件数\r\nvoid Epoll::my_epoll_wait(int listen_fd, int max_events, int timeout)\r\n{\r\n    // printf(\"fd2req.size()==%d\\n\", fd2req.size());\r\n    int event_count = epoll_wait(epoll_fd, events, max_events, timeout);\r\n    if (event_count < 0)\r\n        perror(\"epoll wait error\");\r\n    std::vector<std::shared_ptr<requestData>> req_data = getEventsRequest(listen_fd, event_count, PATH);\r\n    if (req_data.size() > 0)\r\n    {\r\n        for (auto &req: req_data)\r\n        {\r\n            if (ThreadPool::threadpool_add(req) < 0)\r\n            {\r\n                // 线程池满了或者关闭了等原因，抛弃本次监听到的请求。\r\n                break;\r\n            }\r\n        }\r\n    }\r\n}\r\n#include <iostream>\r\n#include <arpa/inet.h>\r\nusing namespace std;\r\nvoid Epoll::acceptConnection(int listen_fd, int epoll_fd, const std::string path)\r\n{\r\n    struct sockaddr_in client_addr;\r\n    memset(&client_addr, 0, sizeof(struct sockaddr_in));\r\n    socklen_t client_addr_len = 0;\r\n    int accept_fd = 0;\r\n    while((accept_fd = accept(listen_fd, (struct sockaddr*)&client_addr, &client_addr_len)) > 0)\r\n    {\r\n        \r\n        cout << inet_addr(client_addr.sin_addr.s_addr) << endl;\r\n        cout << client_addr.sin_port << endl;\r\n        /*\r\n        // TCP的保活机制默认是关闭的\r\n        int optval = 0;\r\n        socklen_t len_optval = 4;\r\n        getsockopt(accept_fd, SOL_SOCKET,  SO_KEEPALIVE, &optval, &len_optval);\r\n        cout << \"optval ==\" << optval << endl;\r\n        */\r\n        \r\n        // 设为非阻塞模式\r\n        int ret = setSocketNonBlocking(accept_fd);\r\n        if (ret < 0)\r\n        {\r\n            perror(\"Set non block failed!\");\r\n            return;\r\n        }\r\n\r\n        std::shared_ptr<requestData> req_info(new requestData(epoll_fd, accept_fd, path));\r\n\r\n        // 文件描述符可以读，边缘触发(Edge Triggered)模式，保证一个socket连接在任一时刻只被一个线程处理\r\n        __uint32_t _epo_event = EPOLLIN | EPOLLET | EPOLLONESHOT;\r\n        Epoll::epoll_add(accept_fd, req_info, _epo_event);\r\n        // 新增时间信息\r\n        std::shared_ptr<mytimer> mtimer(new mytimer(req_info, TIMER_TIME_OUT));\r\n        req_info->addTimer(mtimer);\r\n        MutexLockGuard lock;\r\n        myTimerQueue.push(mtimer);\r\n    }\r\n    //if(accept_fd == -1)\r\n     //   perror(\"accept\");\r\n}\r\n\r\n// 分发处理函数\r\nstd::vector<std::shared_ptr<requestData>> Epoll::getEventsRequest(int listen_fd, int events_num, const std::string path)\r\n{\r\n    std::vector<std::shared_ptr<requestData>> req_data;\r\n    for(int i = 0; i < events_num; ++i)\r\n    {\r\n        // 获取有事件产生的描述符\r\n        int fd = events[i].data.fd;\r\n\r\n        // 有事件发生的描述符为监听描述符\r\n        if(fd == listen_fd)\r\n        {\r\n            //cout << \"This is listen_fd\" << endl;\r\n            acceptConnection(listen_fd, epoll_fd, path);\r\n        }\r\n        else if (fd < 3)\r\n        {\r\n            break;\r\n        }\r\n        else\r\n        {\r\n            // 排除错误事件\r\n            if ((events[i].events & EPOLLERR) || (events[i].events & EPOLLHUP)\r\n                || (!(events[i].events & EPOLLIN)))\r\n            {\r\n                //printf(\"error event\\n\");\r\n                auto fd_ite = fd2req.find(fd);\r\n                if (fd_ite != fd2req.end())\r\n                    fd2req.erase(fd_ite);\r\n                //printf(\"fd = %d, here\\n\", fd);\r\n                continue;\r\n            }\r\n\r\n            // 将请求任务加入到线程池中\r\n            // 加入线程池之前将Timer和request分离\r\n            std::shared_ptr<requestData> cur_req(fd2req[fd]);\r\n            //printf(\"cur_req.use_count=%d\\n\", cur_req.use_count());\r\n            cur_req->seperateTimer();\r\n            req_data.push_back(cur_req);\r\n            auto fd_ite = fd2req.find(fd);\r\n            if (fd_ite != fd2req.end())\r\n                fd2req.erase(fd_ite);\r\n        }\r\n    }\r\n    return req_data;\r\n}"
  },
  {
    "path": "old_version/old_version_0.3/epoll.h",
    "content": "#pragma once\r\n#include \"requestData.h\"\r\n#include <vector>\r\n#include <unordered_map>\r\n#include <sys/epoll.h>\r\n#include <memory>\r\n\r\nclass Epoll\r\n{\r\nprivate:\r\n    static epoll_event *events;\r\n    static std::unordered_map<int, std::shared_ptr<requestData>> fd2req;\r\n    static int epoll_fd;\r\n    static const std::string PATH;\r\npublic:\r\n    static int epoll_init(int maxevents, int listen_num);\r\n    static int epoll_add(int fd, std::shared_ptr<requestData> request, __uint32_t events);\r\n    static int epoll_mod(int fd, std::shared_ptr<requestData> request, __uint32_t events);\r\n    static int epoll_del(int fd, __uint32_t events);\r\n    static void my_epoll_wait(int listen_fd, int max_events, int timeout);\r\n    static void acceptConnection(int listen_fd, int epoll_fd, const std::string path);\r\n    static std::vector<std::shared_ptr<requestData>> getEventsRequest(int listen_fd, int events_num, const std::string path);\r\n};"
  },
  {
    "path": "old_version/old_version_0.3/improvement.txt",
    "content": "1. 指针可以改成shared_ptr，不需要delete。\r\n2. 想法在某些地方写成单例模式。\r\n3. readn 和 writen 改成写到不能写后或读到不能读后加入epoll等待，要记录写到哪或读到哪，维护这样一个状态。比较两者的性能差异。\r\n4. 信号处理部分可以将 epoll_wait 替换为更安全的 epoll_pwait。\r\n\r\n\r\n踩坑：\r\n1. 对EPOLLONESHOT的误解，原以为当epoll_wait监听到相应的事件触发后，epoll会把与事件关联的fd从epoll描述符中禁止掉并且彻底删除，实际上并不会删除，man手册上的解释如下：\r\nEPOLLONESHOT (since Linux 2.6.2)\r\n              Sets the one-shot behavior for the associated file descriptor.\r\n              This means that after an event is pulled out with\r\n              epoll_wait(2) the associated file descriptor is internally\r\n              disabled and no other events will be reported by the epoll\r\n              interface.  The user must call epoll_ctl() with EPOLL_CTL_MOD\r\n              to rearm the file descriptor with a new event mask.\r\n另外：\r\nLinux Programmer's Manual 中第六个问题：\r\n        Q6  Will closing a file descriptor cause it to be removed from all\r\n           epoll sets automatically?\r\n\r\n        A6  Yes, but be aware of the following point.  A file descriptor is a\r\n           reference to an open file description (see open(2)).  Whenever a\r\n           file descriptor is duplicated via dup(2), dup2(2), fcntl(2)\r\n           F_DUPFD, or fork(2), a new file descriptor referring to the same\r\n           open file description is created.  An open file description con‐\r\n           tinues to exist until all file descriptors referring to it have\r\n           been closed.  A file descriptor is removed from an epoll set only\r\n           after all the file descriptors referring to the underlying open\r\n           file description have been closed (or before if the file descrip‐\r\n           tor is explicitly removed using epoll_ctl(2) EPOLL_CTL_DEL).\r\n           This means that even after a file descriptor that is part of an\r\n           epoll set has been closed, events may be reported for that file\r\n           descriptor if other file descriptors referring to the same under‐\r\n           lying file description remain open.\r\n\r\n当调用close()关闭对应的fd时，会使相应的引用计数减一，只有减到0时，epoll才会真的删掉它，所以，比较安全的做法是：\r\n先del掉它，再close它(如果不确定close是否真的关闭了这个文件。)。\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\nubuntu 配置opencv：\r\nvim /etc/ld.so.conf\r\n加一行include /usr/local/opencv/*\r\n/sbin/ldconfig –v"
  },
  {
    "path": "old_version/old_version_0.3/index.html",
    "content": "Hello World !"
  },
  {
    "path": "old_version/old_version_0.3/main.cpp",
    "content": "#include \"requestData.h\"\r\n#include \"epoll.h\"\r\n#include \"threadpool.h\"\r\n#include \"util.h\"\r\n#include \"config.h\"\r\n#include <sys/epoll.h>\r\n#include <queue>\r\n#include <sys/time.h>\r\n#include <sys/socket.h>\r\n#include <netinet/in.h>\r\n#include <stdio.h>\r\n#include <string.h>\r\n#include <cstdlib>\r\n#include <iostream>\r\n#include <vector>\r\n#include <unistd.h>\r\n#include <memory>\r\n\r\nusing namespace std;\r\n\r\nstatic const int MAXEVENTS = 5000;\r\nstatic const int LISTENQ = 1024;\r\nconst int THREADPOOL_THREAD_NUM = 4;\r\nconst int QUEUE_SIZE = 65535;\r\n\r\nconst int PORT = 8888;\r\nconst int ASK_STATIC_FILE = 1;\r\nconst int ASK_IMAGE_STITCH = 2;\r\n\r\nconst string PATH = \"/\";\r\n\r\nconst int TIMER_TIME_OUT = 500;\r\n\r\n\r\nvoid acceptConnection(int listen_fd, int epoll_fd, const string &path);\r\n\r\nextern std::priority_queue<shared_ptr<mytimer>, std::deque<shared_ptr<mytimer>>, timerCmp> myTimerQueue;\r\n\r\n\r\nint socket_bind_listen(int port)\r\n{\r\n    // 检查port值，取正确区间范围\r\n    if (port < 1024 || port > 65535)\r\n        return -1;\r\n\r\n    // 创建socket(IPv4 + TCP)，返回监听描述符\r\n    int listen_fd = 0;\r\n    if((listen_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1)\r\n        return -1;\r\n\r\n    // 消除bind时\"Address already in use\"错误\r\n    int optval = 1;\r\n    if(setsockopt(listen_fd, SOL_SOCKET,  SO_REUSEADDR, &optval, sizeof(optval)) == -1)\r\n        return -1;\r\n\r\n    // 设置服务器IP和Port，和监听描述副绑定\r\n    struct sockaddr_in server_addr;\r\n    bzero((char*)&server_addr, sizeof(server_addr));\r\n    server_addr.sin_family = AF_INET;\r\n    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);\r\n    server_addr.sin_port = htons((unsigned short)port);\r\n    if(bind(listen_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1)\r\n        return -1;\r\n\r\n    // 开始监听，最大等待队列长为LISTENQ\r\n    if(listen(listen_fd, LISTENQ) == -1)\r\n        return -1;\r\n\r\n    // 无效监听描述符\r\n    if(listen_fd == -1)\r\n    {\r\n        close(listen_fd);\r\n        return -1;\r\n    }\r\n    return listen_fd;\r\n}\r\n\r\n/* 处理逻辑是这样的~\r\n因为(1) 优先队列不支持随机访问\r\n(2) 即使支持，随机删除某节点后破坏了堆的结构，需要重新更新堆结构。\r\n所以对于被置为deleted的时间节点，会延迟到它(1)超时 或 (2)它前面的节点都被删除时，它才会被删除。\r\n一个点被置为deleted,它最迟会在TIMER_TIME_OUT时间后被删除。\r\n这样做有两个好处：\r\n(1) 第一个好处是不需要遍历优先队列，省时。\r\n(2) 第二个好处是给超时时间一个容忍的时间，就是设定的超时时间是删除的下限(并不是一到超时时间就立即删除)，如果监听的请求在超时后的下一次请求中又一次出现了，\r\n就不用再重新申请requestData节点了，这样可以继续重复利用前面的requestData，减少了一次delete和一次new的时间。\r\n*/\r\n\r\nvoid handle_expired_event()\r\n{\r\n    MutexLockGuard lock;\r\n    while (!myTimerQueue.empty())\r\n    {\r\n        shared_ptr<mytimer> ptimer_now = myTimerQueue.top();\r\n        if (ptimer_now->isDeleted())\r\n        {\r\n            myTimerQueue.pop();\r\n            //delete ptimer_now;\r\n        }\r\n        else if (ptimer_now->isvalid() == false)\r\n        {\r\n            myTimerQueue.pop();\r\n            //delete ptimer_now;\r\n        }\r\n        else\r\n        {\r\n            break;\r\n        }\r\n    }\r\n}\r\n\r\nint main()\r\n{\r\n    handle_for_sigpipe();\r\n    if (Epoll::epoll_init(MAXEVENTS, LISTENQ) < 0)\r\n    {\r\n        perror(\"epoll init failed\");\r\n        return 1;\r\n    }\r\n    if (ThreadPool::threadpool_create(THREADPOOL_THREAD_NUM, QUEUE_SIZE) < 0)\r\n    {\r\n        printf(\"Threadpool create failed\\n\");\r\n        return 1;\r\n    }\r\n    int listen_fd = socket_bind_listen(PORT);\r\n    if (listen_fd < 0) \r\n    {\r\n        perror(\"socket bind failed\");\r\n        return 1;\r\n    }\r\n    if (setSocketNonBlocking(listen_fd) < 0)\r\n    {\r\n        perror(\"set socket non block failed\");\r\n        return 1;\r\n    }\r\n    shared_ptr<requestData> request(new requestData());\r\n    request->setFd(listen_fd);\r\n    if (Epoll::epoll_add(listen_fd, request, EPOLLIN | EPOLLET) < 0)\r\n    {\r\n        perror(\"epoll add error\");\r\n        return 1;\r\n    }\r\n    while (true)\r\n    {\r\n        Epoll::my_epoll_wait(listen_fd, MAXEVENTS, -1);\r\n        handle_expired_event();\r\n    }\r\n    return 0;\r\n}"
  },
  {
    "path": "old_version/old_version_0.3/requestData.cpp",
    "content": "#include \"requestData.h\"\r\n#include \"util.h\"\r\n#include \"epoll.h\"\r\n#include <sys/epoll.h>\r\n#include <unistd.h>\r\n#include <sys/time.h>\r\n#include <unordered_map>\r\n#include <fcntl.h>\r\n#include <sys/stat.h>\r\n#include <sys/mman.h>\r\n#include <queue>\r\n#include <cstdlib>\r\n#include <opencv/cv.h>\r\n#include <opencv2/core/core.hpp>\r\n#include <opencv2/highgui/highgui.hpp>\r\n#include <opencv2/opencv.hpp>\r\nusing namespace cv;\r\n\r\n//test\r\n#include <iostream>\r\nusing namespace std;\r\n\r\npthread_mutex_t MutexLockGuard::lock = PTHREAD_MUTEX_INITIALIZER;\r\npthread_mutex_t MimeType::lock = PTHREAD_MUTEX_INITIALIZER;\r\nstd::unordered_map<std::string, std::string> MimeType::mime;\r\n\r\nstd::string MimeType::getMime(const std::string &suffix)\r\n{\r\n    if (mime.size() == 0)\r\n    {\r\n        pthread_mutex_lock(&lock);\r\n        if (mime.size() == 0)\r\n        {\r\n            mime[\".html\"] = \"text/html\";\r\n            mime[\".avi\"] = \"video/x-msvideo\";\r\n            mime[\".bmp\"] = \"image/bmp\";\r\n            mime[\".c\"] = \"text/plain\";\r\n            mime[\".doc\"] = \"application/msword\";\r\n            mime[\".gif\"] = \"image/gif\";\r\n            mime[\".gz\"] = \"application/x-gzip\";\r\n            mime[\".htm\"] = \"text/html\";\r\n            mime[\".ico\"] = \"application/x-ico\";\r\n            mime[\".jpg\"] = \"image/jpeg\";\r\n            mime[\".png\"] = \"image/png\";\r\n            mime[\".txt\"] = \"text/plain\";\r\n            mime[\".mp3\"] = \"audio/mp3\";\r\n            mime[\"default\"] = \"text/html\";\r\n        }\r\n        pthread_mutex_unlock(&lock);\r\n    }\r\n    if (mime.find(suffix) == mime.end())\r\n        return mime[\"default\"];\r\n    else\r\n        return mime[suffix];\r\n}\r\n\r\n\r\nstd::priority_queue<shared_ptr<mytimer>, std::deque<shared_ptr<mytimer>>, timerCmp> myTimerQueue;\r\n\r\nrequestData::requestData(): \r\n    now_read_pos(0), \r\n    state(STATE_PARSE_URI), \r\n    h_state(h_start), \r\n    keep_alive(false), \r\n    againTimes(0)\r\n{\r\n    cout << \"requestData()\" << endl;\r\n}\r\n\r\nrequestData::requestData(int _epollfd, int _fd, std::string _path):\r\n    now_read_pos(0), \r\n    state(STATE_PARSE_URI), \r\n    h_state(h_start), \r\n    keep_alive(false), \r\n    againTimes(0), \r\n    path(_path), \r\n    fd(_fd), \r\n    epollfd(_epollfd)\r\n{\r\n    cout << \"requestData()\" << endl;\r\n}\r\n\r\nrequestData::~requestData()\r\n{\r\n    cout << \"~requestData()\" << endl;\r\n    //Epoll::epoll_del(fd, EPOLLIN | EPOLLET | EPOLLONESHOT);\r\n    //struct epoll_event ev;\r\n    // 超时的一定都是读请求，没有\"被动\"写。\r\n    //ev.events = EPOLLIN | EPOLLET | EPOLLONESHOT;\r\n    //ev.data.fd = fd;\r\n    //epoll_ctl(epollfd, EPOLL_CTL_DEL, fd, &ev);\r\n    // if (timer.lock())\r\n    // {\r\n    //     shared_ptr<mytimer> my_timer(timer.lock())\r\n    //     my_timer->clearReq();\r\n    //     timer.reset();\r\n    // }\r\n    close(fd);\r\n}\r\n\r\nvoid requestData::addTimer(shared_ptr<mytimer> mtimer)\r\n{\r\n    // shared_ptr重载了bool, 但weak_ptr没有\r\n    //if (!timer.lock())\r\n    timer = mtimer;\r\n}\r\n\r\nint requestData::getFd()\r\n{\r\n    return fd;\r\n}\r\nvoid requestData::setFd(int _fd)\r\n{\r\n    fd = _fd;\r\n}\r\n\r\nvoid requestData::reset()\r\n{\r\n    againTimes = 0;\r\n    content.clear();\r\n    file_name.clear();\r\n    path.clear();\r\n    now_read_pos = 0;\r\n    state = STATE_PARSE_URI;\r\n    h_state = h_start;\r\n    headers.clear();\r\n    keep_alive = false;\r\n    if (timer.lock())\r\n    {\r\n        shared_ptr<mytimer> my_timer(timer.lock());\r\n        my_timer->clearReq();\r\n        timer.reset();\r\n    }\r\n}\r\n\r\nvoid requestData::seperateTimer()\r\n{\r\n    if (timer.lock())\r\n    {\r\n        shared_ptr<mytimer> my_timer(timer.lock());\r\n        my_timer->clearReq();\r\n        timer.reset();\r\n    }\r\n}\r\n\r\nvoid requestData::handleRequest()\r\n{\r\n    char buff[MAX_BUFF];\r\n    bool isError = false;\r\n    while (true)\r\n    {\r\n        int read_num = readn(fd, buff, MAX_BUFF);\r\n        if (read_num < 0)\r\n        {\r\n            perror(\"1\");\r\n            isError = true;\r\n            break;\r\n        }\r\n        else if (read_num == 0)\r\n        {\r\n            // 有请求出现但是读不到数据，可能是Request Aborted，或者来自网络的数据没有达到等原因\r\n            perror(\"read_num == 0\");\r\n            if (errno == EAGAIN)\r\n            {\r\n                if (againTimes > AGAIN_MAX_TIMES)\r\n                    isError = true;\r\n                else\r\n                    ++againTimes;\r\n            }\r\n            else if (errno != 0)\r\n                isError = true;\r\n            break;\r\n        }\r\n        string now_read(buff, buff + read_num);\r\n        content += now_read;\r\n\r\n        if (state == STATE_PARSE_URI)\r\n        {\r\n            int flag = this->parse_URI();\r\n            if (flag == PARSE_URI_AGAIN)\r\n            {\r\n                break;\r\n            }\r\n            else if (flag == PARSE_URI_ERROR)\r\n            {\r\n                perror(\"2\");\r\n                isError = true;\r\n                break;\r\n            }\r\n        }\r\n        if (state == STATE_PARSE_HEADERS)\r\n        {\r\n            int flag = this->parse_Headers();\r\n            if (flag == PARSE_HEADER_AGAIN)\r\n            {  \r\n                break;\r\n            }\r\n            else if (flag == PARSE_HEADER_ERROR)\r\n            {\r\n                perror(\"3\");\r\n                isError = true;\r\n                break;\r\n            }\r\n            if(method == METHOD_POST)\r\n            {\r\n                state = STATE_RECV_BODY;\r\n            }\r\n            else \r\n            {\r\n                state = STATE_ANALYSIS;\r\n            }\r\n        }\r\n        if (state == STATE_RECV_BODY)\r\n        {\r\n            int content_length = -1;\r\n            if (headers.find(\"Content-length\") != headers.end())\r\n            {\r\n                content_length = stoi(headers[\"Content-length\"]);\r\n            }\r\n            else\r\n            {\r\n                isError = true;\r\n                break;\r\n            }\r\n            if (content.size() < content_length)\r\n                continue;\r\n            state = STATE_ANALYSIS;\r\n        }\r\n        if (state == STATE_ANALYSIS)\r\n        {\r\n            int flag = this->analysisRequest();\r\n            if (flag < 0)\r\n            {\r\n                isError = true;\r\n                break;\r\n            }\r\n            else if (flag == ANALYSIS_SUCCESS)\r\n            {\r\n\r\n                state = STATE_FINISH;\r\n                break;\r\n            }\r\n            else\r\n            {\r\n                isError = true;\r\n                break;\r\n            }\r\n        }\r\n    }\r\n\r\n    if (isError)\r\n    {\r\n        //delete this;\r\n        return;\r\n    }\r\n    // 加入epoll继续\r\n    if (state == STATE_FINISH)\r\n    {\r\n        if (keep_alive)\r\n        {\r\n            //printf(\"ok\\n\");\r\n            this->reset();\r\n        }\r\n        else\r\n        {\r\n            //delete this;\r\n            return;\r\n        }\r\n    }\r\n    // 一定要先加时间信息，否则可能会出现刚加进去，下个in触发来了，然后分离失败后，又加入队列，最后超时被删，然后正在线程中进行的任务出错，double free错误。\r\n    // 新增时间信息\r\n    //cout << \"shared_from_this().use_count() ==\" << shared_from_this().use_count() << endl;\r\n    shared_ptr<mytimer> mtimer(new mytimer(shared_from_this(), 500));\r\n    this->addTimer(mtimer);\r\n    {\r\n        MutexLockGuard lock;\r\n        myTimerQueue.push(mtimer);\r\n    }\r\n\r\n    __uint32_t _epo_event = EPOLLIN | EPOLLET | EPOLLONESHOT;\r\n    int ret = Epoll::epoll_mod(fd, shared_from_this(), _epo_event);\r\n    //cout << \"shared_from_this().use_count() ==\" << shared_from_this().use_count() << endl;\r\n    if (ret < 0)\r\n    {\r\n        // 返回错误处理\r\n        //delete this;\r\n        return;\r\n    }\r\n}\r\n\r\nint requestData::parse_URI()\r\n{\r\n    string &str = content;\r\n    // 读到完整的请求行再开始解析请求\r\n    int pos = str.find('\\r', now_read_pos);\r\n    if (pos < 0)\r\n    {\r\n        return PARSE_URI_AGAIN;\r\n    }\r\n    // 去掉请求行所占的空间，节省空间\r\n    string request_line = str.substr(0, pos);\r\n    if (str.size() > pos + 1)\r\n        str = str.substr(pos + 1);\r\n    else \r\n        str.clear();\r\n    // Method\r\n    pos = request_line.find(\"GET\");\r\n    if (pos < 0)\r\n    {\r\n        pos = request_line.find(\"POST\");\r\n        if (pos < 0)\r\n        {\r\n            return PARSE_URI_ERROR;\r\n        }\r\n        else\r\n        {\r\n            method = METHOD_POST;\r\n        }\r\n    }\r\n    else\r\n    {\r\n        method = METHOD_GET;\r\n    }\r\n    //printf(\"method = %d\\n\", method);\r\n    // filename\r\n    pos = request_line.find(\"/\", pos);\r\n    if (pos < 0)\r\n    {\r\n        return PARSE_URI_ERROR;\r\n    }\r\n    else\r\n    {\r\n        int _pos = request_line.find(' ', pos);\r\n        if (_pos < 0)\r\n            return PARSE_URI_ERROR;\r\n        else\r\n        {\r\n            if (_pos - pos > 1)\r\n            {\r\n                file_name = request_line.substr(pos + 1, _pos - pos - 1);\r\n                int __pos = file_name.find('?');\r\n                if (__pos >= 0)\r\n                {\r\n                    file_name = file_name.substr(0, __pos);\r\n                }\r\n            }\r\n                \r\n            else\r\n                file_name = \"index.html\";\r\n        }\r\n        pos = _pos;\r\n    }\r\n    //cout << \"file_name: \" << file_name << endl;\r\n    // HTTP 版本号\r\n    pos = request_line.find(\"/\", pos);\r\n    if (pos < 0)\r\n    {\r\n        return PARSE_URI_ERROR;\r\n    }\r\n    else\r\n    {\r\n        if (request_line.size() - pos <= 3)\r\n        {\r\n            return PARSE_URI_ERROR;\r\n        }\r\n        else\r\n        {\r\n            string ver = request_line.substr(pos + 1, 3);\r\n            if (ver == \"1.0\")\r\n                HTTPversion = HTTP_10;\r\n            else if (ver == \"1.1\")\r\n                HTTPversion = HTTP_11;\r\n            else\r\n                return PARSE_URI_ERROR;\r\n        }\r\n    }\r\n    state = STATE_PARSE_HEADERS;\r\n    return PARSE_URI_SUCCESS;\r\n}\r\n\r\nint requestData::parse_Headers()\r\n{\r\n    string &str = content;\r\n    int key_start = -1, key_end = -1, value_start = -1, value_end = -1;\r\n    int now_read_line_begin = 0;\r\n    bool notFinish = true;\r\n    for (int i = 0; i < str.size() && notFinish; ++i)\r\n    {\r\n        switch(h_state)\r\n        {\r\n            case h_start:\r\n            {\r\n                if (str[i] == '\\n' || str[i] == '\\r')\r\n                    break;\r\n                h_state = h_key;\r\n                key_start = i;\r\n                now_read_line_begin = i;\r\n                break;\r\n            }\r\n            case h_key:\r\n            {\r\n                if (str[i] == ':')\r\n                {\r\n                    key_end = i;\r\n                    if (key_end - key_start <= 0)\r\n                        return PARSE_HEADER_ERROR;\r\n                    h_state = h_colon;\r\n                }\r\n                else if (str[i] == '\\n' || str[i] == '\\r')\r\n                    return PARSE_HEADER_ERROR;\r\n                break;  \r\n            }\r\n            case h_colon:\r\n            {\r\n                if (str[i] == ' ')\r\n                {\r\n                    h_state = h_spaces_after_colon;\r\n                }\r\n                else\r\n                    return PARSE_HEADER_ERROR;\r\n                break;  \r\n            }\r\n            case h_spaces_after_colon:\r\n            {\r\n                h_state = h_value;\r\n                value_start = i;\r\n                break;  \r\n            }\r\n            case h_value:\r\n            {\r\n                if (str[i] == '\\r')\r\n                {\r\n                    h_state = h_CR;\r\n                    value_end = i;\r\n                    if (value_end - value_start <= 0)\r\n                        return PARSE_HEADER_ERROR;\r\n                }\r\n                else if (i - value_start > 255)\r\n                    return PARSE_HEADER_ERROR;\r\n                break;  \r\n            }\r\n            case h_CR:\r\n            {\r\n                if (str[i] == '\\n')\r\n                {\r\n                    h_state = h_LF;\r\n                    string key(str.begin() + key_start, str.begin() + key_end);\r\n                    string value(str.begin() + value_start, str.begin() + value_end);\r\n                    headers[key] = value;\r\n                    now_read_line_begin = i;\r\n                }\r\n                else\r\n                    return PARSE_HEADER_ERROR;\r\n                break;  \r\n            }\r\n            case h_LF:\r\n            {\r\n                if (str[i] == '\\r')\r\n                {\r\n                    h_state = h_end_CR;\r\n                }\r\n                else\r\n                {\r\n                    key_start = i;\r\n                    h_state = h_key;\r\n                }\r\n                break;\r\n            }\r\n            case h_end_CR:\r\n            {\r\n                if (str[i] == '\\n')\r\n                {\r\n                    h_state = h_end_LF;\r\n                }\r\n                else\r\n                    return PARSE_HEADER_ERROR;\r\n                break;\r\n            }\r\n            case h_end_LF:\r\n            {\r\n                notFinish = false;\r\n                key_start = i;\r\n                now_read_line_begin = i;\r\n                break;\r\n            }\r\n        }\r\n    }\r\n    if (h_state == h_end_LF)\r\n    {\r\n        str = str.substr(now_read_line_begin);\r\n        return PARSE_HEADER_SUCCESS;\r\n    }\r\n    str = str.substr(now_read_line_begin);\r\n    return PARSE_HEADER_AGAIN;\r\n}\r\n\r\nint requestData::analysisRequest()\r\n{\r\n    if (method == METHOD_POST)\r\n    {\r\n        //get content\r\n        char header[MAX_BUFF];\r\n        sprintf(header, \"HTTP/1.1 %d %s\\r\\n\", 200, \"OK\");\r\n        if(headers.find(\"Connection\") != headers.end() && headers[\"Connection\"] == \"keep-alive\")\r\n        {\r\n            keep_alive = true;\r\n            sprintf(header, \"%sConnection: keep-alive\\r\\n\", header);\r\n            sprintf(header, \"%sKeep-Alive: timeout=%d\\r\\n\", header, EPOLL_WAIT_TIME);\r\n        }\r\n        //cout << \"content=\" << content << endl;\r\n        // test char*\r\n        char *send_content = \"I have receiced this.\";\r\n\r\n        sprintf(header, \"%sContent-length: %zu\\r\\n\", header, strlen(send_content));\r\n        sprintf(header, \"%s\\r\\n\", header);\r\n        size_t send_len = (size_t)writen(fd, header, strlen(header));\r\n        if(send_len != strlen(header))\r\n        {\r\n            perror(\"Send header failed\");\r\n            return ANALYSIS_ERROR;\r\n        }\r\n        \r\n        send_len = (size_t)writen(fd, send_content, strlen(send_content));\r\n        if(send_len != strlen(send_content))\r\n        {\r\n            perror(\"Send content failed\");\r\n            return ANALYSIS_ERROR;\r\n        }\r\n        cout << \"content size ==\" << content.size() << endl;\r\n        vector<char> data(content.begin(), content.end());\r\n        Mat test = imdecode(data, CV_LOAD_IMAGE_ANYDEPTH|CV_LOAD_IMAGE_ANYCOLOR);\r\n        imwrite(\"receive.bmp\", test);\r\n        return ANALYSIS_SUCCESS;\r\n    }\r\n    else if (method == METHOD_GET)\r\n    {\r\n        char header[MAX_BUFF];\r\n        sprintf(header, \"HTTP/1.1 %d %s\\r\\n\", 200, \"OK\");\r\n        if(headers.find(\"Connection\") != headers.end() && headers[\"Connection\"] == \"keep-alive\")\r\n        {\r\n            keep_alive = true;\r\n            sprintf(header, \"%sConnection: keep-alive\\r\\n\", header);\r\n            sprintf(header, \"%sKeep-Alive: timeout=%d\\r\\n\", header, EPOLL_WAIT_TIME);\r\n        }\r\n        int dot_pos = file_name.find('.');\r\n        const char* filetype;\r\n        if (dot_pos < 0) \r\n            filetype = MimeType::getMime(\"default\").c_str();\r\n        else\r\n            filetype = MimeType::getMime(file_name.substr(dot_pos)).c_str();\r\n        struct stat sbuf;\r\n        if (stat(file_name.c_str(), &sbuf) < 0)\r\n        {\r\n            handleError(fd, 404, \"Not Found!\");\r\n            return ANALYSIS_ERROR;\r\n        }\r\n\r\n        sprintf(header, \"%sContent-type: %s\\r\\n\", header, filetype);\r\n        // 通过Content-length返回文件大小\r\n        sprintf(header, \"%sContent-length: %ld\\r\\n\", header, sbuf.st_size);\r\n\r\n        sprintf(header, \"%s\\r\\n\", header);\r\n        size_t send_len = (size_t)writen(fd, header, strlen(header));\r\n        if(send_len != strlen(header))\r\n        {\r\n            perror(\"Send header failed\");\r\n            return ANALYSIS_ERROR;\r\n        }\r\n        int src_fd = open(file_name.c_str(), O_RDONLY, 0);\r\n        char *src_addr = static_cast<char*>(mmap(NULL, sbuf.st_size, PROT_READ, MAP_PRIVATE, src_fd, 0));\r\n        close(src_fd);\r\n    \r\n        // 发送文件并校验完整性\r\n        send_len = writen(fd, src_addr, sbuf.st_size);\r\n        if(send_len != sbuf.st_size)\r\n        {\r\n            perror(\"Send file failed\");\r\n            return ANALYSIS_ERROR;\r\n        }\r\n        munmap(src_addr, sbuf.st_size);\r\n        return ANALYSIS_SUCCESS;\r\n    }\r\n    else\r\n        return ANALYSIS_ERROR;\r\n}\r\n\r\nvoid requestData::handleError(int fd, int err_num, string short_msg)\r\n{\r\n    short_msg = \" \" + short_msg;\r\n    char send_buff[MAX_BUFF];\r\n    string body_buff, header_buff;\r\n    body_buff += \"<html><title>TKeed Error</title>\";\r\n    body_buff += \"<body bgcolor=\\\"ffffff\\\">\";\r\n    body_buff += to_string(err_num) + short_msg;\r\n    body_buff += \"<hr><em> LinYa's Web Server</em>\\n</body></html>\";\r\n\r\n    header_buff += \"HTTP/1.1 \" + to_string(err_num) + short_msg + \"\\r\\n\";\r\n    header_buff += \"Content-type: text/html\\r\\n\";\r\n    header_buff += \"Connection: close\\r\\n\";\r\n    header_buff += \"Content-length: \" + to_string(body_buff.size()) + \"\\r\\n\";\r\n    header_buff += \"\\r\\n\";\r\n    sprintf(send_buff, \"%s\", header_buff.c_str());\r\n    writen(fd, send_buff, strlen(send_buff));\r\n    sprintf(send_buff, \"%s\", body_buff.c_str());\r\n    writen(fd, send_buff, strlen(send_buff));\r\n}\r\n\r\nmytimer::mytimer(shared_ptr<requestData> _request_data, int timeout): \r\n    deleted(false), \r\n    request_data(_request_data)\r\n{\r\n    cout << \"mytimer()\" << endl;\r\n    struct timeval now;\r\n    gettimeofday(&now, NULL);\r\n    // 以毫秒计\r\n    expired_time = ((now.tv_sec * 1000) + (now.tv_usec / 1000)) + timeout;\r\n}\r\n\r\nmytimer::~mytimer()\r\n{\r\n    cout << \"~mytimer()\" << endl;\r\n    if (request_data)\r\n    {\r\n        Epoll::epoll_del(request_data->getFd(), EPOLLIN | EPOLLET | EPOLLONESHOT);\r\n    }\r\n    //request_data.reset();\r\n    // if (request_data)\r\n    // {\r\n    //     cout << \"request_data=\" << request_data << endl;\r\n    //     delete request_data;\r\n    //     request_data = NULL;\r\n    // }\r\n}\r\n\r\nvoid mytimer::update(int timeout)\r\n{\r\n    struct timeval now;\r\n    gettimeofday(&now, NULL);\r\n    expired_time = ((now.tv_sec * 1000) + (now.tv_usec / 1000)) + timeout;\r\n}\r\n\r\nbool mytimer::isvalid()\r\n{\r\n    struct timeval now;\r\n    gettimeofday(&now, NULL);\r\n    size_t temp = ((now.tv_sec * 1000) + (now.tv_usec / 1000));\r\n    if (temp < expired_time)\r\n    {\r\n        return true;\r\n    }\r\n    else\r\n    {\r\n        this->setDeleted();\r\n        return false;\r\n    }\r\n}\r\n\r\nvoid mytimer::clearReq()\r\n{\r\n    request_data.reset();\r\n    this->setDeleted();\r\n}\r\n\r\nvoid mytimer::setDeleted()\r\n{\r\n    deleted = true;\r\n}\r\n\r\nbool mytimer::isDeleted() const\r\n{\r\n    return deleted;\r\n}\r\n\r\nsize_t mytimer::getExpTime() const\r\n{\r\n    return expired_time;\r\n}\r\n\r\nbool timerCmp::operator()(shared_ptr<mytimer> &a, shared_ptr<mytimer> &b) const\r\n{\r\n    return a->getExpTime() > b->getExpTime();\r\n}\r\n\r\n\r\nMutexLockGuard::MutexLockGuard()\r\n{\r\n    pthread_mutex_lock(&lock);\r\n}\r\n\r\nMutexLockGuard::~MutexLockGuard()\r\n{\r\n    pthread_mutex_unlock(&lock);\r\n}"
  },
  {
    "path": "old_version/old_version_0.3/requestData.h",
    "content": "#pragma once\r\n#include <string>\r\n#include <unordered_map>\r\n#include <memory>\r\n\r\nconst int STATE_PARSE_URI = 1;\r\nconst int STATE_PARSE_HEADERS = 2;\r\nconst int STATE_RECV_BODY = 3;\r\nconst int STATE_ANALYSIS = 4;\r\nconst int STATE_FINISH = 5;\r\n\r\nconst int MAX_BUFF = 4096;\r\n\r\n// 有请求出现但是读不到数据,可能是Request Aborted,\r\n// 或者来自网络的数据没有达到等原因,\r\n// 对这样的请求尝试超过一定的次数就抛弃\r\nconst int AGAIN_MAX_TIMES = 200;\r\n\r\nconst int PARSE_URI_AGAIN = -1;\r\nconst int PARSE_URI_ERROR = -2;\r\nconst int PARSE_URI_SUCCESS = 0;\r\n\r\nconst int PARSE_HEADER_AGAIN = -1;\r\nconst int PARSE_HEADER_ERROR = -2;\r\nconst int PARSE_HEADER_SUCCESS = 0;\r\n\r\nconst int ANALYSIS_ERROR = -2;\r\nconst int ANALYSIS_SUCCESS = 0;\r\n\r\nconst int METHOD_POST = 1;\r\nconst int METHOD_GET = 2;\r\nconst int HTTP_10 = 1;\r\nconst int HTTP_11 = 2;\r\n\r\nconst int EPOLL_WAIT_TIME = 500;\r\n\r\nclass MimeType\r\n{\r\nprivate:\r\n    static pthread_mutex_t lock;\r\n    static std::unordered_map<std::string, std::string> mime;\r\n    MimeType();\r\n    MimeType(const MimeType &m);\r\npublic:\r\n    static std::string getMime(const std::string &suffix);\r\n};\r\n\r\nenum HeadersState\r\n{\r\n    h_start = 0,\r\n    h_key,\r\n    h_colon,\r\n    h_spaces_after_colon,\r\n    h_value,\r\n    h_CR,\r\n    h_LF,\r\n    h_end_CR,\r\n    h_end_LF\r\n};\r\n\r\nstruct mytimer;\r\nclass requestData;\r\n\r\nclass requestData : public std::enable_shared_from_this<requestData>\r\n{\r\nprivate:\r\n    int againTimes;\r\n    std::string path;\r\n    int fd;\r\n    int epollfd;\r\n    // content的内容用完就清\r\n    std::string content;\r\n    int method;\r\n    int HTTPversion;\r\n    std::string file_name;\r\n    int now_read_pos;\r\n    int state;\r\n    int h_state;\r\n    bool isfinish;\r\n    bool keep_alive;\r\n    std::unordered_map<std::string, std::string> headers;\r\n    std::weak_ptr<mytimer> timer;\r\n\r\nprivate:\r\n    int parse_URI();\r\n    int parse_Headers();\r\n    int analysisRequest();\r\n\r\npublic:\r\n\r\n    requestData();\r\n    requestData(int _epollfd, int _fd, std::string _path);\r\n    ~requestData();\r\n    void addTimer(std::shared_ptr<mytimer> mtimer);\r\n    void reset();\r\n    void seperateTimer();\r\n    int getFd();\r\n    void setFd(int _fd);\r\n    void handleRequest();\r\n    void handleError(int fd, int err_num, std::string short_msg);\r\n};\r\n\r\nstruct mytimer\r\n{\r\n    bool deleted;\r\n    size_t expired_time;\r\n    std::shared_ptr<requestData> request_data;\r\n\r\n    mytimer(std::shared_ptr<requestData> _request_data, int timeout);\r\n    ~mytimer();\r\n    void update(int timeout);\r\n    bool isvalid();\r\n    void clearReq();\r\n    void setDeleted();\r\n    bool isDeleted() const;\r\n    size_t getExpTime() const;\r\n};\r\n\r\nstruct timerCmp\r\n{\r\n    bool operator()(std::shared_ptr<mytimer> &a, std::shared_ptr<mytimer> &b) const;\r\n};\r\n\r\n\r\nclass MutexLockGuard\r\n{\r\npublic:\r\n    explicit MutexLockGuard();\r\n    ~MutexLockGuard();\r\n\r\nprivate:\r\n    static pthread_mutex_t lock;\r\n\r\nprivate:\r\n    MutexLockGuard(const MutexLockGuard&);\r\n    MutexLockGuard& operator=(const MutexLockGuard&);\r\n};"
  },
  {
    "path": "old_version/old_version_0.3/threadpool.cpp",
    "content": "#include \"threadpool.h\"\r\n\r\n\r\npthread_mutex_t ThreadPool::lock = PTHREAD_MUTEX_INITIALIZER;\r\npthread_cond_t ThreadPool::notify = PTHREAD_COND_INITIALIZER;\r\nstd::vector<pthread_t> ThreadPool::threads;\r\nstd::vector<ThreadPoolTask> ThreadPool::queue;\r\nint ThreadPool::thread_count = 0;\r\nint ThreadPool::queue_size = 0;\r\nint ThreadPool::head = 0;\r\nint ThreadPool::tail = 0;\r\nint ThreadPool::count = 0;\r\nint ThreadPool::shutdown = 0;\r\nint ThreadPool::started = 0;\r\n\r\nint ThreadPool::threadpool_create(int _thread_count, int _queue_size)\r\n{\r\n    bool err = false;\r\n    do\r\n    {\r\n        if(_thread_count <= 0 || _thread_count > MAX_THREADS || _queue_size <= 0 || _queue_size > MAX_QUEUE) \r\n        {\r\n            _thread_count = 4;\r\n            _queue_size = 1024;\r\n        }\r\n    \r\n        thread_count = 0;\r\n        queue_size = _queue_size;\r\n        head = tail = count = 0;\r\n        shutdown = started = 0;\r\n\r\n        threads.resize(_thread_count);\r\n        queue.resize(_queue_size);\r\n    \r\n        /* Start worker threads */\r\n        for(int i = 0; i < _thread_count; ++i) \r\n        {\r\n            if(pthread_create(&threads[i], NULL, threadpool_thread, (void*)(0)) != 0) \r\n            {\r\n                //threadpool_destroy(pool, 0);\r\n                return -1;\r\n            }\r\n            ++thread_count;\r\n            ++started;\r\n        }\r\n    } while(false);\r\n    \r\n    if (err) \r\n    {\r\n        //threadpool_free(pool);\r\n        return -1;\r\n    }\r\n    return 0;\r\n}\r\n\r\nvoid myHandler(std::shared_ptr<void> req)\r\n{\r\n    std::shared_ptr<requestData> request = std::static_pointer_cast<requestData>(req);\r\n    request->handleRequest();\r\n}\r\n\r\nint ThreadPool::threadpool_add(std::shared_ptr<void> args, std::function<void(std::shared_ptr<void>)> fun)\r\n{\r\n    int next, err = 0;\r\n    if(pthread_mutex_lock(&lock) != 0)\r\n        return THREADPOOL_LOCK_FAILURE;\r\n    do \r\n    {\r\n        next = (tail + 1) % queue_size;\r\n        // 队列满\r\n        if(count == queue_size) \r\n        {\r\n            err = THREADPOOL_QUEUE_FULL;\r\n            break;\r\n        }\r\n        // 已关闭\r\n        if(shutdown)\r\n        {\r\n            err = THREADPOOL_SHUTDOWN;\r\n            break;\r\n        }\r\n        queue[tail].fun = fun;\r\n        queue[tail].args = args;\r\n        tail = next;\r\n        ++count;\r\n        \r\n        /* pthread_cond_broadcast */\r\n        if(pthread_cond_signal(&notify) != 0) \r\n        {\r\n            err = THREADPOOL_LOCK_FAILURE;\r\n            break;\r\n        }\r\n    } while(false);\r\n\r\n    if(pthread_mutex_unlock(&lock) != 0)\r\n        err = THREADPOOL_LOCK_FAILURE;\r\n    return err;\r\n}\r\n\r\n/*\r\nint threadpool_destroy(threadpool_t *pool, int flags)\r\n{\r\n    printf(\"Thread pool destroy !\\n\");\r\n    int i, err = 0;\r\n\r\n    if(pthread_mutex_lock(&(pool->lock)) != 0) \r\n    {\r\n        return THREADPOOL_LOCK_FAILURE;\r\n    }\r\n\r\n    do \r\n    {\r\n        if(pool->shutdown) {\r\n            err = THREADPOOL_SHUTDOWN;\r\n            break;\r\n        }\r\n\r\n        pool->shutdown = (flags & THREADPOOL_GRACEFUL) ?\r\n            graceful_shutdown : immediate_shutdown;\r\n\r\n        if((pthread_cond_broadcast(&(pool->notify)) != 0) ||\r\n           (pthread_mutex_unlock(&(pool->lock)) != 0)) {\r\n            err = THREADPOOL_LOCK_FAILURE;\r\n            break;\r\n        }\r\n\r\n        for(i = 0; i < pool->thread_count; ++i)\r\n        {\r\n            if(pthread_join(pool->threads[i], NULL) != 0)\r\n            {\r\n                err = THREADPOOL_THREAD_FAILURE;\r\n            }\r\n        }\r\n    } while(false);\r\n\r\n    if(!err) \r\n    {\r\n        threadpool_free(pool);\r\n    }\r\n    return err;\r\n}\r\n\r\nint threadpool_free(threadpool_t *pool)\r\n{\r\n    if(pool == NULL || pool->started > 0)\r\n    {\r\n        return -1;\r\n    }\r\n\r\n    if(pool->threads) \r\n    {\r\n        free(pool->threads);\r\n        free(pool->queue);\r\n \r\n\r\n        pthread_mutex_lock(&(pool->lock));\r\n        pthread_mutex_destroy(&(pool->lock));\r\n        pthread_cond_destroy(&(pool->notify));\r\n    }\r\n    free(pool);    \r\n    return 0;\r\n}\r\n*/\r\n\r\nvoid *ThreadPool::threadpool_thread(void *args)\r\n{\r\n    while (true)\r\n    {\r\n        ThreadPoolTask task;\r\n        pthread_mutex_lock(&lock);\r\n        while((count == 0) && (!shutdown)) \r\n        {\r\n            pthread_cond_wait(&notify, &lock);\r\n        }\r\n        if((shutdown == immediate_shutdown) ||\r\n           ((shutdown == graceful_shutdown) && (count == 0)))\r\n        {\r\n            break;\r\n        }\r\n        task.fun = queue[head].fun;\r\n        task.args = queue[head].args;\r\n        queue[head].fun = NULL;\r\n        queue[head].args.reset();\r\n        head = (head + 1) % queue_size;\r\n        --count;\r\n        pthread_mutex_unlock(&lock);\r\n        (task.fun)(task.args);\r\n    }\r\n\r\n    --started;\r\n\r\n    pthread_mutex_unlock(&lock);\r\n    pthread_exit(NULL);\r\n    return(NULL);\r\n}"
  },
  {
    "path": "old_version/old_version_0.3/threadpool.h",
    "content": "#pragma once\r\n#include \"requestData.h\"\r\n#include <pthread.h>\r\n#include <functional>\r\n#include <memory>\r\n#include <vector>\r\n\r\n\r\nconst int THREADPOOL_INVALID = -1;\r\nconst int THREADPOOL_LOCK_FAILURE = -2;\r\nconst int THREADPOOL_QUEUE_FULL = -3;\r\nconst int THREADPOOL_SHUTDOWN = -4;\r\nconst int THREADPOOL_THREAD_FAILURE = -5;\r\nconst int THREADPOOL_GRACEFUL = 1;\r\n\r\nconst int MAX_THREADS = 1024;\r\nconst int MAX_QUEUE = 65535;\r\n\r\ntypedef enum \r\n{\r\n    immediate_shutdown = 1,\r\n    graceful_shutdown  = 2\r\n} threadpool_shutdown_t;\r\n\r\nstruct ThreadPoolTask\r\n{\r\n    std::function<void(std::shared_ptr<void>)> fun;\r\n    std::shared_ptr<void> args;\r\n};\r\n\r\n/**\r\n *  @struct threadpool\r\n *  @brief The threadpool struct\r\n *\r\n *  @var notify       Condition variable to notify worker threads.\r\n *  @var threads      Array containing worker threads ID.\r\n *  @var thread_count Number of threads\r\n *  @var queue        Array containing the task queue.\r\n *  @var queue_size   Size of the task queue.\r\n *  @var head         Index of the first element.\r\n *  @var tail         Index of the next element.\r\n *  @var count        Number of pending tasks\r\n *  @var shutdown     Flag indicating if the pool is shutting down\r\n *  @var started      Number of started threads\r\n */\r\nvoid myHandler(std::shared_ptr<void> req);\r\nclass ThreadPool\r\n{\r\nprivate:\r\n    static pthread_mutex_t lock;\r\n    static pthread_cond_t notify;\r\n    static std::vector<pthread_t> threads;\r\n    static std::vector<ThreadPoolTask> queue;\r\n    static int thread_count;\r\n    static int queue_size;\r\n    static int head;\r\n    // tail 指向尾节点的下一节点\r\n    static int tail;\r\n    static int count;\r\n    static int shutdown;\r\n    static int started;\r\npublic:\r\n    static int threadpool_create(int _thread_count, int _queue_size);\r\n    static int threadpool_add(std::shared_ptr<void> args, std::function<void(std::shared_ptr<void>)> fun = myHandler);\r\n    static int threadpool_destroy();\r\n    static int threadpool_free();\r\n    static void *threadpool_thread(void *args);\r\n};\r\n"
  },
  {
    "path": "old_version/old_version_0.3/util.cpp",
    "content": "#include \"util.h\"\r\n#include <unistd.h>\r\n#include <fcntl.h>\r\n#include <signal.h>\r\n#include <errno.h>\r\n#include <string.h>\r\n\r\nssize_t readn(int fd, void *buff, size_t n)\r\n{\r\n    size_t nleft = n;\r\n    ssize_t nread = 0;\r\n    ssize_t readSum = 0;\r\n    char *ptr = (char*)buff;\r\n    while (nleft > 0)\r\n    {\r\n        if ((nread = read(fd, ptr, nleft)) < 0)\r\n        {\r\n            if (errno == EINTR)\r\n                nread = 0;\r\n            else if (errno == EAGAIN)\r\n            {\r\n                return readSum;\r\n            }\r\n            else\r\n            {\r\n                return -1;\r\n            }  \r\n        }\r\n        else if (nread == 0)\r\n            break;\r\n        readSum += nread;\r\n        nleft -= nread;\r\n        ptr += nread;\r\n    }\r\n    return readSum;\r\n}\r\n\r\nssize_t writen(int fd, void *buff, size_t n)\r\n{\r\n    size_t nleft = n;\r\n    ssize_t nwritten = 0;\r\n    ssize_t writeSum = 0;\r\n    char *ptr = (char*)buff;\r\n    while (nleft > 0)\r\n    {\r\n        if ((nwritten = write(fd, ptr, nleft)) <= 0)\r\n        {\r\n            if (nwritten < 0)\r\n            {\r\n                if (errno == EINTR || errno == EAGAIN)\r\n                {\r\n                    nwritten = 0;\r\n                    continue;\r\n                }\r\n                else\r\n                    return -1;\r\n            }\r\n        }\r\n        writeSum += nwritten;\r\n        nleft -= nwritten;\r\n        ptr += nwritten;\r\n    }\r\n    return writeSum;\r\n}\r\n\r\nvoid handle_for_sigpipe()\r\n{\r\n    struct sigaction sa;\r\n    memset(&sa, '\\0', sizeof(sa));\r\n    sa.sa_handler = SIG_IGN;\r\n    sa.sa_flags = 0;\r\n    if(sigaction(SIGPIPE, &sa, NULL))\r\n        return;\r\n}\r\n\r\nint setSocketNonBlocking(int fd)\r\n{\r\n    int flag = fcntl(fd, F_GETFL, 0);\r\n    if(flag == -1)\r\n        return -1;\r\n\r\n    flag |= O_NONBLOCK;\r\n    if(fcntl(fd, F_SETFL, flag) == -1)\r\n        return -1;\r\n    return 0;\r\n}"
  },
  {
    "path": "old_version/old_version_0.3/util.h",
    "content": "#pragma once\r\n#include <cstdlib>\r\n\r\nssize_t readn(int fd, void *buff, size_t n);\r\nssize_t writen(int fd, void *buff, size_t n);\r\nvoid handle_for_sigpipe();\r\nint setSocketNonBlocking(int fd);"
  },
  {
    "path": "old_version/old_version_0.4/Makefile",
    "content": "SOURCE  := $(wildcard *.cpp)\r\nOBJS    := $(patsubst %.c,%.o,$(patsubst %.cpp,%.o,$(SOURCE)))\r\n\r\nTARGET  := myserver\r\nCC      := g++\r\nLIBS    := -lpthread -lopencv_core -lopencv_imgproc -lopencv_highgui\r\nINCLUDE:= -I./usr/local/include/opencv\r\nCFLAGS  := -std=c++11 -g -Wall -O3 $(INCLUDE) -D_PTHREADS\r\nCXXFLAGS:= $(CFLAGS)\r\n\r\n.PHONY : objs clean veryclean rebuild all\r\nall : $(TARGET)\r\nobjs : $(OBJS)\r\nrebuild: veryclean all\r\nclean :\r\n\trm -fr *.o\r\nveryclean : clean\r\n\trm -rf $(TARGET)\r\n\r\n$(TARGET) : $(OBJS)\r\n\t$(CC) $(CXXFLAGS) -o $@ $(OBJS) $(LDFLAGS) $(LIBS)"
  },
  {
    "path": "old_version/old_version_0.4/Makefile1",
    "content": "cc=g++ -std=c++11\r\nmain: main.o RequestData.o epoll.o threadpool.o\r\n\t$(cc) -o main main.o RequestData.o epoll.o threadpool.o -lpthread -lopencv_core -lopencv_imgproc -lopencv_highgui -lopencv_ml \r\nmain.o: main.cpp\r\n\t$(cc) -I/usr/local/include/opencv -c main.cpp\r\nRequestData.o: requestData.h RequestData.cpp\r\n\t$(cc) -I/usr/local/include/opencv -c RequestData.cpp\r\nepoll.o: epoll.h epoll.cpp\r\n\t$(cc) -I/usr/local/include/opencv -c epoll.cpp\r\nthreadpool.o: threadpool.h threadpool.cpp\r\n\t$(cc) -I/usr/local/include/opencv -c threadpool.cpp\r\n.PHONY: clean\r\nclean:\r\n\t-rm main *.o\r\n\r\n\r\n\r\n\r\n.PHONY : everything objs clean veryclean rebuild\r\neverything :\r\n\t$(TARGET)\r\nall :\r\n\t$(TARGET)\r\nobjs :\r\n\t$(OBJS)\r\nrebuild:\r\n\tveryclean everything \r\nclean :\r\n\trm -fr *.o\r\nveryclean :\r\n\tclean rm -fr $(TARGET)"
  },
  {
    "path": "old_version/old_version_0.4/base/mutexLock.hpp",
    "content": "#pragma once\r\n#include \"nocopyable.hpp\"\r\n#include <pthread.h>\r\n#include <cstdio>\r\n\r\nclass MutexLock: noncopyable\r\n{\r\npublic:\r\n    MutexLock()\r\n    {\r\n        pthread_mutex_init(&mutex, NULL);\r\n    }\r\n    ~MutexLock()\r\n    {\r\n        pthread_mutex_lock(&mutex);\r\n        pthread_mutex_destroy(&mutex);\r\n    }\r\n    void lock()\r\n    {\r\n        pthread_mutex_lock(&mutex);\r\n    }\r\n    void unlock()\r\n    {\r\n        pthread_mutex_unlock(&mutex);\r\n    }\r\nprivate:\r\n    pthread_mutex_t mutex;\r\n};\r\n\r\n\r\nclass MutexLockGuard: noncopyable\r\n{\r\npublic:\r\n    explicit MutexLockGuard(MutexLock &_mutex):\r\n    mutex(_mutex)\r\n    {\r\n        mutex.lock();\r\n    }\r\n    ~MutexLockGuard()\r\n    {\r\n        mutex.unlock();\r\n    }\r\nprivate:\r\n    MutexLock &mutex;\r\n};"
  },
  {
    "path": "old_version/old_version_0.4/base/nocopyable.hpp",
    "content": "#pragma once\r\nclass noncopyable\r\n{\r\nprotected:\r\n    noncopyable() {}\r\n    ~noncopyable() {}\r\nprivate:\r\n    noncopyable(const noncopyable&);\r\n    const noncopyable& operator=(const noncopyable&);\r\n};"
  },
  {
    "path": "old_version/old_version_0.4/config.h",
    "content": "#pragma once\r\n\r\n"
  },
  {
    "path": "old_version/old_version_0.4/epoll.cpp",
    "content": "#include \"epoll.h\"\r\n#include \"threadpool.h\"\r\n#include \"util.h\"\r\n#include <sys/epoll.h>\r\n#include <errno.h>\r\n#include <sys/socket.h>\r\n#include <netinet/in.h>\r\n#include <string.h>\r\n#include <queue>\r\n#include <deque>\r\n\r\nint TIMER_TIME_OUT = 500;\r\n\r\nepoll_event *Epoll::events;\r\nstd::unordered_map<int, std::shared_ptr<RequestData>> Epoll::fd2req;\r\nint Epoll::epoll_fd = 0;\r\nconst std::string Epoll::PATH = \"/\";\r\n\r\nTimerManager Epoll::timer_manager;\r\n\r\nint Epoll::epoll_init(int maxevents, int listen_num)\r\n{\r\n    epoll_fd = epoll_create(listen_num + 1);\r\n    if(epoll_fd == -1)\r\n        return -1;\r\n    //events.reset(new epoll_event[maxevents], [](epoll_event *data){delete [] data;});\r\n    events = new epoll_event[maxevents];\r\n    return 0;\r\n}\r\n\r\n// 注册新描述符\r\nint Epoll::epoll_add(int fd, SP_ReqData request, __uint32_t events)\r\n{\r\n    struct epoll_event event;\r\n    event.data.fd = fd;\r\n    event.events = events;\r\n    if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &event) < 0)\r\n    {\r\n        perror(\"epoll_add error\");\r\n        return -1;\r\n    }\r\n    fd2req[fd] = request;\r\n    return 0;\r\n}\r\n\r\n// 修改描述符状态\r\nint Epoll::epoll_mod(int fd, SP_ReqData request, __uint32_t events)\r\n{\r\n    struct epoll_event event;\r\n    event.data.fd = fd;\r\n    event.events = events;\r\n    if(epoll_ctl(epoll_fd, EPOLL_CTL_MOD, fd, &event) < 0)\r\n    {\r\n        perror(\"epoll_mod error\");\r\n        return -1;\r\n    }\r\n    fd2req[fd] = request;\r\n    return 0;\r\n}\r\n\r\n// 从epoll中删除描述符\r\nint Epoll::epoll_del(int fd, __uint32_t events)\r\n{\r\n    struct epoll_event event;\r\n    event.data.fd = fd;\r\n    event.events = events;\r\n    if(epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fd, &event) < 0)\r\n    {\r\n        perror(\"epoll_del error\");\r\n        return -1;\r\n    }\r\n    auto fd_ite = fd2req.find(fd);\r\n    if (fd_ite != fd2req.end())\r\n        fd2req.erase(fd_ite);\r\n    return 0;\r\n}\r\n\r\n// 返回活跃事件数\r\nvoid Epoll::my_epoll_wait(int listen_fd, int max_events, int timeout)\r\n{\r\n    // printf(\"fd2req.size()==%d\\n\", fd2req.size());\r\n    int event_count = epoll_wait(epoll_fd, events, max_events, timeout);\r\n    if (event_count < 0)\r\n        perror(\"epoll wait error\");\r\n    std::vector<SP_ReqData> req_data = getEventsRequest(listen_fd, event_count, PATH);\r\n    if (req_data.size() > 0)\r\n    {\r\n        for (auto &req: req_data)\r\n        {\r\n            if (ThreadPool::threadpool_add(req) < 0)\r\n            {\r\n                // 线程池满了或者关闭了等原因，抛弃本次监听到的请求。\r\n                break;\r\n            }\r\n        }\r\n    }\r\n    timer_manager.handle_expired_event();\r\n}\r\n#include <iostream>\r\n#include <arpa/inet.h>\r\nusing namespace std;\r\nvoid Epoll::acceptConnection(int listen_fd, int epoll_fd, const std::string path)\r\n{\r\n    struct sockaddr_in client_addr;\r\n    memset(&client_addr, 0, sizeof(struct sockaddr_in));\r\n    //socklen_t client_addr_len = 0;\r\n    socklen_t client_addr_len = sizeof(client_addr);\r\n    int accept_fd = 0;\r\n    while((accept_fd = accept(listen_fd, (struct sockaddr*)&client_addr, &client_addr_len)) > 0)\r\n    {\r\n        cout << inet_ntoa(client_addr.sin_addr) << endl;\r\n        cout << ntohs(client_addr.sin_port) << endl;\r\n        /*\r\n        // TCP的保活机制默认是关闭的\r\n        int optval = 0;\r\n        socklen_t len_optval = 4;\r\n        getsockopt(accept_fd, SOL_SOCKET,  SO_KEEPALIVE, &optval, &len_optval);\r\n        cout << \"optval ==\" << optval << endl;\r\n        */\r\n        \r\n        // 设为非阻塞模式\r\n        int ret = setSocketNonBlocking(accept_fd);\r\n        if (ret < 0)\r\n        {\r\n            perror(\"Set non block failed!\");\r\n            return;\r\n        }\r\n\r\n        SP_ReqData req_info(new RequestData(epoll_fd, accept_fd, path));\r\n\r\n        // 文件描述符可以读，边缘触发(Edge Triggered)模式，保证一个socket连接在任一时刻只被一个线程处理\r\n        __uint32_t _epo_event = EPOLLIN | EPOLLET | EPOLLONESHOT;\r\n        Epoll::epoll_add(accept_fd, req_info, _epo_event);\r\n        // 新增时间信息\r\n        timer_manager.addTimer(req_info, TIMER_TIME_OUT);\r\n    }\r\n    //if(accept_fd == -1)\r\n     //   perror(\"accept\");\r\n}\r\n\r\n// 分发处理函数\r\nstd::vector<std::shared_ptr<RequestData>> Epoll::getEventsRequest(int listen_fd, int events_num, const std::string path)\r\n{\r\n    std::vector<SP_ReqData> req_data;\r\n    for(int i = 0; i < events_num; ++i)\r\n    {\r\n        // 获取有事件产生的描述符\r\n        int fd = events[i].data.fd;\r\n\r\n        // 有事件发生的描述符为监听描述符\r\n        if(fd == listen_fd)\r\n        {\r\n            //cout << \"This is listen_fd\" << endl;\r\n            acceptConnection(listen_fd, epoll_fd, path);\r\n        }\r\n        else if (fd < 3)\r\n        {\r\n            break;\r\n        }\r\n        else\r\n        {\r\n            // 排除错误事件\r\n            if ((events[i].events & EPOLLERR) || (events[i].events & EPOLLHUP)\r\n                || (!(events[i].events & EPOLLIN)))\r\n            {\r\n                //printf(\"error event\\n\");\r\n                auto fd_ite = fd2req.find(fd);\r\n                if (fd_ite != fd2req.end())\r\n                    fd2req.erase(fd_ite);\r\n                continue;\r\n            }\r\n\r\n            // 将请求任务加入到线程池中\r\n            // 加入线程池之前将Timer和request分离\r\n            SP_ReqData cur_req(fd2req[fd]);\r\n            //printf(\"cur_req.use_count=%d\\n\", cur_req.use_count());\r\n            cur_req->seperateTimer();\r\n            req_data.push_back(cur_req);\r\n            auto fd_ite = fd2req.find(fd);\r\n            if (fd_ite != fd2req.end())\r\n                fd2req.erase(fd_ite);\r\n        }\r\n    }\r\n    return req_data;\r\n}\r\n\r\nvoid Epoll::add_timer(shared_ptr<RequestData> request_data, int timeout)\r\n{\r\n    timer_manager.addTimer(request_data, timeout);\r\n}"
  },
  {
    "path": "old_version/old_version_0.4/epoll.h",
    "content": "#pragma once\r\n#include \"requestData.h\"\r\n#include \"timer.h\"\r\n#include <vector>\r\n#include <unordered_map>\r\n#include <sys/epoll.h>\r\n#include <memory>\r\n\r\nclass Epoll\r\n{\r\npublic:\r\n    typedef std::shared_ptr<RequestData> SP_ReqData;\r\nprivate:\r\n    static epoll_event *events;\r\n    static std::unordered_map<int, SP_ReqData> fd2req;\r\n    static int epoll_fd;\r\n    static const std::string PATH;\r\n\r\n    static TimerManager timer_manager;\r\npublic:\r\n    static int epoll_init(int maxevents, int listen_num);\r\n    static int epoll_add(int fd, SP_ReqData request, __uint32_t events);\r\n    static int epoll_mod(int fd, SP_ReqData request, __uint32_t events);\r\n    static int epoll_del(int fd, __uint32_t events = (EPOLLIN | EPOLLET | EPOLLONESHOT));\r\n    static void my_epoll_wait(int listen_fd, int max_events, int timeout);\r\n    static void acceptConnection(int listen_fd, int epoll_fd, const std::string path);\r\n    static std::vector<SP_ReqData> getEventsRequest(int listen_fd, int events_num, const std::string path);\r\n\r\n    static void add_timer(SP_ReqData request_data, int timeout);\r\n};"
  },
  {
    "path": "old_version/old_version_0.4/improvement.txt",
    "content": "1. 指针可以改成shared_ptr，不需要delete。\r\n2. 想法在某些地方写成单例模式。\r\n3. readn 和 writen 改成写到不能写后或读到不能读后加入epoll等待，要记录写到哪或读到哪，维护这样一个状态。比较两者的性能差异。\r\n4. 信号处理部分可以将 epoll_wait 替换为更安全的 epoll_pwait。\r\n\r\n\r\n踩坑：\r\n1. 对EPOLLONESHOT的误解，原以为当epoll_wait监听到相应的事件触发后，epoll会把与事件关联的fd从epoll描述符中禁止掉并且彻底删除，实际上并不会删除，man手册上的解释如下：\r\nEPOLLONESHOT (since Linux 2.6.2)\r\n              Sets the one-shot behavior for the associated file descriptor.\r\n              This means that after an event is pulled out with\r\n              epoll_wait(2) the associated file descriptor is internally\r\n              disabled and no other events will be reported by the epoll\r\n              interface.  The user must call epoll_ctl() with EPOLL_CTL_MOD\r\n              to rearm the file descriptor with a new event mask.\r\n另外：\r\nLinux Programmer's Manual 中第六个问题：\r\n        Q6  Will closing a file descriptor cause it to be removed from all\r\n           epoll sets automatically?\r\n\r\n        A6  Yes, but be aware of the following point.  A file descriptor is a\r\n           reference to an open file description (see open(2)).  Whenever a\r\n           file descriptor is duplicated via dup(2), dup2(2), fcntl(2)\r\n           F_DUPFD, or fork(2), a new file descriptor referring to the same\r\n           open file description is created.  An open file description con‐\r\n           tinues to exist until all file descriptors referring to it have\r\n           been closed.  A file descriptor is removed from an epoll set only\r\n           after all the file descriptors referring to the underlying open\r\n           file description have been closed (or before if the file descrip‐\r\n           tor is explicitly removed using epoll_ctl(2) EPOLL_CTL_DEL).\r\n           This means that even after a file descriptor that is part of an\r\n           epoll set has been closed, events may be reported for that file\r\n           descriptor if other file descriptors referring to the same under‐\r\n           lying file description remain open.\r\n\r\n当调用close()关闭对应的fd时，会使相应的引用计数减一，只有减到0时，epoll才会真的删掉它，所以，比较安全的做法是：\r\n先del掉它，再close它(如果不确定close是否真的关闭了这个文件。)。\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\nubuntu 配置opencv：\r\nvim /etc/ld.so.conf\r\n加一行include /usr/local/opencv/*\r\n/sbin/ldconfig –v"
  },
  {
    "path": "old_version/old_version_0.4/index.html",
    "content": "Hello World !"
  },
  {
    "path": "old_version/old_version_0.4/main.cpp",
    "content": "#include \"requestData.h\"\r\n#include \"epoll.h\"\r\n#include \"threadpool.h\"\r\n#include \"util.h\"\r\n#include \"config.h\"\r\n#include <sys/epoll.h>\r\n#include <queue>\r\n#include <sys/time.h>\r\n#include <sys/socket.h>\r\n#include <netinet/in.h>\r\n#include <stdio.h>\r\n#include <string.h>\r\n#include <cstdlib>\r\n#include <iostream>\r\n#include <vector>\r\n#include <unistd.h>\r\n#include <memory>\r\n\r\nusing namespace std;\r\n\r\nstatic const int MAXEVENTS = 5000;\r\nstatic const int LISTENQ = 1024;\r\nconst int THREADPOOL_THREAD_NUM = 4;\r\nconst int QUEUE_SIZE = 65535;\r\n\r\nconst int PORT = 8888;\r\nconst int ASK_STATIC_FILE = 1;\r\nconst int ASK_IMAGE_STITCH = 2;\r\n\r\nconst int TIMER_TIME_OUT = 500;\r\n\r\nvoid acceptConnection(int listen_fd, int epoll_fd, const string &path);\r\n\r\nint socket_bind_listen(int port)\r\n{\r\n    // 检查port值，取正确区间范围\r\n    if (port < 1024 || port > 65535)\r\n        return -1;\r\n\r\n    // 创建socket(IPv4 + TCP)，返回监听描述符\r\n    int listen_fd = 0;\r\n    if((listen_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1)\r\n        return -1;\r\n\r\n    // 消除bind时\"Address already in use\"错误\r\n    int optval = 1;\r\n    if(setsockopt(listen_fd, SOL_SOCKET,  SO_REUSEADDR, &optval, sizeof(optval)) == -1)\r\n        return -1;\r\n\r\n    // 设置服务器IP和Port，和监听描述副绑定\r\n    struct sockaddr_in server_addr;\r\n    bzero((char*)&server_addr, sizeof(server_addr));\r\n    server_addr.sin_family = AF_INET;\r\n    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);\r\n    server_addr.sin_port = htons((unsigned short)port);\r\n    if(bind(listen_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1)\r\n        return -1;\r\n\r\n    // 开始监听，最大等待队列长为LISTENQ\r\n    if(listen(listen_fd, LISTENQ) == -1)\r\n        return -1;\r\n\r\n    // 无效监听描述符\r\n    if(listen_fd == -1)\r\n    {\r\n        close(listen_fd);\r\n        return -1;\r\n    }\r\n    return listen_fd;\r\n}\r\n\r\n\r\nint main()\r\n{\r\n    #ifndef _PTHREADS\r\n        printf(\"_PTHREADS is not defined !\\n\");\r\n    #endif\r\n    handle_for_sigpipe();\r\n    if (Epoll::epoll_init(MAXEVENTS, LISTENQ) < 0)\r\n    {\r\n        perror(\"epoll init failed\");\r\n        return 1;\r\n    }\r\n    if (ThreadPool::threadpool_create(THREADPOOL_THREAD_NUM, QUEUE_SIZE) < 0)\r\n    {\r\n        printf(\"Threadpool create failed\\n\");\r\n        return 1;\r\n    }\r\n    int listen_fd = socket_bind_listen(PORT);\r\n    if (listen_fd < 0) \r\n    {\r\n        perror(\"socket bind failed\");\r\n        return 1;\r\n    }\r\n    if (setSocketNonBlocking(listen_fd) < 0)\r\n    {\r\n        perror(\"set socket non block failed\");\r\n        return 1;\r\n    }\r\n    shared_ptr<RequestData> request(new RequestData());\r\n    request->setFd(listen_fd);\r\n    if (Epoll::epoll_add(listen_fd, request, EPOLLIN | EPOLLET) < 0)\r\n    {\r\n        perror(\"epoll add error\");\r\n        return 1;\r\n    }\r\n    \r\n    while (true)\r\n    {\r\n        //sleep(10);\r\n        Epoll::my_epoll_wait(listen_fd, MAXEVENTS, -1);\r\n        \r\n        //ThreadPool::threadpool_destroy();\r\n        //break;\r\n    }\r\n    return 0;\r\n}"
  },
  {
    "path": "old_version/old_version_0.4/requestData.cpp",
    "content": "#include \"requestData.h\"\r\n#include \"util.h\"\r\n#include \"epoll.h\"\r\n#include <sys/epoll.h>\r\n#include <unistd.h>\r\n#include <fcntl.h>\r\n#include <sys/stat.h>\r\n#include <sys/mman.h>\r\n#include <queue>\r\n#include <cstdlib>\r\n#include <opencv/cv.h>\r\n#include <opencv2/core/core.hpp>\r\n#include <opencv2/highgui/highgui.hpp>\r\n#include <opencv2/opencv.hpp>\r\nusing namespace cv;\r\n\r\n//test\r\n#include <iostream>\r\nusing namespace std;\r\n\r\npthread_once_t MimeType::once_control = PTHREAD_ONCE_INIT;\r\nstd::unordered_map<std::string, std::string> MimeType::mime;\r\n\r\n\r\nvoid MimeType::init()\r\n{\r\n    mime[\".html\"] = \"text/html\";\r\n    mime[\".avi\"] = \"video/x-msvideo\";\r\n    mime[\".bmp\"] = \"image/bmp\";\r\n    mime[\".c\"] = \"text/plain\";\r\n    mime[\".doc\"] = \"application/msword\";\r\n    mime[\".gif\"] = \"image/gif\";\r\n    mime[\".gz\"] = \"application/x-gzip\";\r\n    mime[\".htm\"] = \"text/html\";\r\n    mime[\".ico\"] = \"application/x-ico\";\r\n    mime[\".jpg\"] = \"image/jpeg\";\r\n    mime[\".png\"] = \"image/png\";\r\n    mime[\".txt\"] = \"text/plain\";\r\n    mime[\".mp3\"] = \"audio/mp3\";\r\n    mime[\"default\"] = \"text/html\";\r\n}\r\n\r\nstd::string MimeType::getMime(const std::string &suffix)\r\n{\r\n    pthread_once(&once_control, MimeType::init);\r\n    if (mime.find(suffix) == mime.end())\r\n        return mime[\"default\"];\r\n    else\r\n        return mime[suffix];\r\n}\r\n\r\nRequestData::RequestData(): \r\n    now_read_pos(0), \r\n    state(STATE_PARSE_URI), \r\n    h_state(h_start), \r\n    keep_alive(false), \r\n    againTimes(0)\r\n{\r\n    cout << \"RequestData()\" << endl;\r\n}\r\n\r\nRequestData::RequestData(int _epollfd, int _fd, std::string _path):\r\n    now_read_pos(0), \r\n    state(STATE_PARSE_URI), \r\n    h_state(h_start), \r\n    keep_alive(false), \r\n    againTimes(0), \r\n    path(_path), \r\n    fd(_fd), \r\n    epollfd(_epollfd)\r\n{\r\n    cout << \"RequestData()\" << endl;\r\n}\r\n\r\nRequestData::~RequestData()\r\n{\r\n    cout << \"~RequestData()\" << endl;\r\n    //Epoll::epoll_del(fd, EPOLLIN | EPOLLET | EPOLLONESHOT);\r\n    //struct epoll_event ev;\r\n    // 超时的一定都是读请求，没有\"被动\"写。\r\n    //ev.events = EPOLLIN | EPOLLET | EPOLLONESHOT;\r\n    //ev.data.fd = fd;\r\n    //epoll_ctl(epollfd, EPOLL_CTL_DEL, fd, &ev);\r\n    // if (timer.lock())\r\n    // {\r\n    //     shared_ptr<TimerNode> my_timer(timer.lock())\r\n    //     my_timer->clearReq();\r\n    //     timer.reset();\r\n    // }\r\n    close(fd);\r\n}\r\n\r\nvoid RequestData::linkTimer(shared_ptr<TimerNode> mtimer)\r\n{\r\n    // shared_ptr重载了bool, 但weak_ptr没有\r\n    //if (!timer.lock())\r\n    timer = mtimer;\r\n}\r\n\r\nint RequestData::getFd()\r\n{\r\n    return fd;\r\n}\r\nvoid RequestData::setFd(int _fd)\r\n{\r\n    fd = _fd;\r\n}\r\n\r\nvoid RequestData::reset()\r\n{\r\n    againTimes = 0;\r\n    content.clear();\r\n    file_name.clear();\r\n    path.clear();\r\n    now_read_pos = 0;\r\n    state = STATE_PARSE_URI;\r\n    h_state = h_start;\r\n    headers.clear();\r\n    keep_alive = false;\r\n    if (timer.lock())\r\n    {\r\n        shared_ptr<TimerNode> my_timer(timer.lock());\r\n        my_timer->clearReq();\r\n        timer.reset();\r\n    }\r\n}\r\n\r\nvoid RequestData::seperateTimer()\r\n{\r\n    if (timer.lock())\r\n    {\r\n        shared_ptr<TimerNode> my_timer(timer.lock());\r\n        my_timer->clearReq();\r\n        timer.reset();\r\n    }\r\n}\r\n\r\nvoid RequestData::handleRequest()\r\n{\r\n    char buff[MAX_BUFF];\r\n    bool isError = false;\r\n    while (true)\r\n    {\r\n        int read_num = readn(fd, buff, MAX_BUFF);\r\n        if (read_num < 0)\r\n        {\r\n            perror(\"1\");\r\n            isError = true;\r\n            break;\r\n        }\r\n        else if (read_num == 0)\r\n        {\r\n            // 有请求出现但是读不到数据，可能是Request Aborted，或者来自网络的数据没有达到等原因\r\n            perror(\"read_num == 0\");\r\n            if (errno == EAGAIN)\r\n            {\r\n                if (againTimes > AGAIN_MAX_TIMES)\r\n                    isError = true;\r\n                else\r\n                    ++againTimes;\r\n            }\r\n            else if (errno != 0)\r\n                isError = true;\r\n            break;\r\n        }\r\n        string now_read(buff, buff + read_num);\r\n        content += now_read;\r\n\r\n        if (state == STATE_PARSE_URI)\r\n        {\r\n            int flag = this->parse_URI();\r\n            if (flag == PARSE_URI_AGAIN)\r\n            {\r\n                break;\r\n            }\r\n            else if (flag == PARSE_URI_ERROR)\r\n            {\r\n                perror(\"2\");\r\n                isError = true;\r\n                break;\r\n            }\r\n        }\r\n        if (state == STATE_PARSE_HEADERS)\r\n        {\r\n            int flag = this->parse_Headers();\r\n            if (flag == PARSE_HEADER_AGAIN)\r\n            {  \r\n                break;\r\n            }\r\n            else if (flag == PARSE_HEADER_ERROR)\r\n            {\r\n                perror(\"3\");\r\n                isError = true;\r\n                break;\r\n            }\r\n            if(method == METHOD_POST)\r\n            {\r\n                state = STATE_RECV_BODY;\r\n            }\r\n            else \r\n            {\r\n                state = STATE_ANALYSIS;\r\n            }\r\n        }\r\n        if (state == STATE_RECV_BODY)\r\n        {\r\n            int content_length = -1;\r\n            if (headers.find(\"Content-length\") != headers.end())\r\n            {\r\n                content_length = stoi(headers[\"Content-length\"]);\r\n            }\r\n            else\r\n            {\r\n                isError = true;\r\n                break;\r\n            }\r\n            if (content.size() < content_length)\r\n                continue;\r\n            state = STATE_ANALYSIS;\r\n        }\r\n        if (state == STATE_ANALYSIS)\r\n        {\r\n            int flag = this->analysisRequest();\r\n            if (flag < 0)\r\n            {\r\n                isError = true;\r\n                break;\r\n            }\r\n            else if (flag == ANALYSIS_SUCCESS)\r\n            {\r\n\r\n                state = STATE_FINISH;\r\n                break;\r\n            }\r\n            else\r\n            {\r\n                isError = true;\r\n                break;\r\n            }\r\n        }\r\n    }\r\n\r\n    if (isError)\r\n    {\r\n        //delete this;\r\n        return;\r\n    }\r\n    // 加入epoll继续\r\n    if (state == STATE_FINISH)\r\n    {\r\n        if (keep_alive)\r\n        {\r\n            //printf(\"ok\\n\");\r\n            this->reset();\r\n        }\r\n        else\r\n        {\r\n            //delete this;\r\n            return;\r\n        }\r\n    }\r\n    // 一定要先加时间信息，否则可能会出现刚加进去，下个in触发来了，然后分离失败后，又加入队列，最后超时被删，然后正在线程中进行的任务出错，double free错误。\r\n    // 新增时间信息\r\n    Epoll::add_timer(shared_from_this(), 500);\r\n    // shared_ptr<TimerNode> mtimer(new TimerNode(shared_from_this(), 500));\r\n    // this->linkTimer(mtimer);\r\n    // {\r\n    //     MutexLockGuard lock;\r\n    //     TimerNodeQueue.push(mtimer);\r\n    // }\r\n\r\n    __uint32_t _epo_event = EPOLLIN | EPOLLET | EPOLLONESHOT;\r\n    int ret = Epoll::epoll_mod(fd, shared_from_this(), _epo_event);\r\n    //cout << \"shared_from_this().use_count() ==\" << shared_from_this().use_count() << endl;\r\n    if (ret < 0)\r\n    {\r\n        // 返回错误处理\r\n        //delete this;\r\n        return;\r\n    }\r\n}\r\n\r\nint RequestData::parse_URI()\r\n{\r\n    string &str = content;\r\n    // 读到完整的请求行再开始解析请求\r\n    int pos = str.find('\\r', now_read_pos);\r\n    if (pos < 0)\r\n    {\r\n        return PARSE_URI_AGAIN;\r\n    }\r\n    // 去掉请求行所占的空间，节省空间\r\n    string request_line = str.substr(0, pos);\r\n    if (str.size() > pos + 1)\r\n        str = str.substr(pos + 1);\r\n    else \r\n        str.clear();\r\n    // Method\r\n    pos = request_line.find(\"GET\");\r\n    if (pos < 0)\r\n    {\r\n        pos = request_line.find(\"POST\");\r\n        if (pos < 0)\r\n        {\r\n            return PARSE_URI_ERROR;\r\n        }\r\n        else\r\n        {\r\n            method = METHOD_POST;\r\n        }\r\n    }\r\n    else\r\n    {\r\n        method = METHOD_GET;\r\n    }\r\n    //printf(\"method = %d\\n\", method);\r\n    // filename\r\n    pos = request_line.find(\"/\", pos);\r\n    if (pos < 0)\r\n    {\r\n        return PARSE_URI_ERROR;\r\n    }\r\n    else\r\n    {\r\n        int _pos = request_line.find(' ', pos);\r\n        if (_pos < 0)\r\n            return PARSE_URI_ERROR;\r\n        else\r\n        {\r\n            if (_pos - pos > 1)\r\n            {\r\n                file_name = request_line.substr(pos + 1, _pos - pos - 1);\r\n                int __pos = file_name.find('?');\r\n                if (__pos >= 0)\r\n                {\r\n                    file_name = file_name.substr(0, __pos);\r\n                }\r\n            }\r\n                \r\n            else\r\n                file_name = \"index.html\";\r\n        }\r\n        pos = _pos;\r\n    }\r\n    //cout << \"file_name: \" << file_name << endl;\r\n    // HTTP 版本号\r\n    pos = request_line.find(\"/\", pos);\r\n    if (pos < 0)\r\n    {\r\n        return PARSE_URI_ERROR;\r\n    }\r\n    else\r\n    {\r\n        if (request_line.size() - pos <= 3)\r\n        {\r\n            return PARSE_URI_ERROR;\r\n        }\r\n        else\r\n        {\r\n            string ver = request_line.substr(pos + 1, 3);\r\n            if (ver == \"1.0\")\r\n                HTTPversion = HTTP_10;\r\n            else if (ver == \"1.1\")\r\n                HTTPversion = HTTP_11;\r\n            else\r\n                return PARSE_URI_ERROR;\r\n        }\r\n    }\r\n    state = STATE_PARSE_HEADERS;\r\n    return PARSE_URI_SUCCESS;\r\n}\r\n\r\nint RequestData::parse_Headers()\r\n{\r\n    string &str = content;\r\n    int key_start = -1, key_end = -1, value_start = -1, value_end = -1;\r\n    int now_read_line_begin = 0;\r\n    bool notFinish = true;\r\n    for (int i = 0; i < str.size() && notFinish; ++i)\r\n    {\r\n        switch(h_state)\r\n        {\r\n            case h_start:\r\n            {\r\n                if (str[i] == '\\n' || str[i] == '\\r')\r\n                    break;\r\n                h_state = h_key;\r\n                key_start = i;\r\n                now_read_line_begin = i;\r\n                break;\r\n            }\r\n            case h_key:\r\n            {\r\n                if (str[i] == ':')\r\n                {\r\n                    key_end = i;\r\n                    if (key_end - key_start <= 0)\r\n                        return PARSE_HEADER_ERROR;\r\n                    h_state = h_colon;\r\n                }\r\n                else if (str[i] == '\\n' || str[i] == '\\r')\r\n                    return PARSE_HEADER_ERROR;\r\n                break;  \r\n            }\r\n            case h_colon:\r\n            {\r\n                if (str[i] == ' ')\r\n                {\r\n                    h_state = h_spaces_after_colon;\r\n                }\r\n                else\r\n                    return PARSE_HEADER_ERROR;\r\n                break;  \r\n            }\r\n            case h_spaces_after_colon:\r\n            {\r\n                h_state = h_value;\r\n                value_start = i;\r\n                break;  \r\n            }\r\n            case h_value:\r\n            {\r\n                if (str[i] == '\\r')\r\n                {\r\n                    h_state = h_CR;\r\n                    value_end = i;\r\n                    if (value_end - value_start <= 0)\r\n                        return PARSE_HEADER_ERROR;\r\n                }\r\n                else if (i - value_start > 255)\r\n                    return PARSE_HEADER_ERROR;\r\n                break;  \r\n            }\r\n            case h_CR:\r\n            {\r\n                if (str[i] == '\\n')\r\n                {\r\n                    h_state = h_LF;\r\n                    string key(str.begin() + key_start, str.begin() + key_end);\r\n                    string value(str.begin() + value_start, str.begin() + value_end);\r\n                    headers[key] = value;\r\n                    now_read_line_begin = i;\r\n                }\r\n                else\r\n                    return PARSE_HEADER_ERROR;\r\n                break;  \r\n            }\r\n            case h_LF:\r\n            {\r\n                if (str[i] == '\\r')\r\n                {\r\n                    h_state = h_end_CR;\r\n                }\r\n                else\r\n                {\r\n                    key_start = i;\r\n                    h_state = h_key;\r\n                }\r\n                break;\r\n            }\r\n            case h_end_CR:\r\n            {\r\n                if (str[i] == '\\n')\r\n                {\r\n                    h_state = h_end_LF;\r\n                }\r\n                else\r\n                    return PARSE_HEADER_ERROR;\r\n                break;\r\n            }\r\n            case h_end_LF:\r\n            {\r\n                notFinish = false;\r\n                key_start = i;\r\n                now_read_line_begin = i;\r\n                break;\r\n            }\r\n        }\r\n    }\r\n    if (h_state == h_end_LF)\r\n    {\r\n        str = str.substr(now_read_line_begin);\r\n        return PARSE_HEADER_SUCCESS;\r\n    }\r\n    str = str.substr(now_read_line_begin);\r\n    return PARSE_HEADER_AGAIN;\r\n}\r\n\r\nint RequestData::analysisRequest()\r\n{\r\n    if (method == METHOD_POST)\r\n    {\r\n        //get content\r\n        char header[MAX_BUFF];\r\n        sprintf(header, \"HTTP/1.1 %d %s\\r\\n\", 200, \"OK\");\r\n        if(headers.find(\"Connection\") != headers.end() && headers[\"Connection\"] == \"keep-alive\")\r\n        {\r\n            keep_alive = true;\r\n            sprintf(header, \"%sConnection: keep-alive\\r\\n\", header);\r\n            sprintf(header, \"%sKeep-Alive: timeout=%d\\r\\n\", header, EPOLL_WAIT_TIME);\r\n        }\r\n        //cout << \"content=\" << content << endl;\r\n        // test char*\r\n        char *send_content = \"I have receiced this.\";\r\n\r\n        sprintf(header, \"%sContent-length: %zu\\r\\n\", header, strlen(send_content));\r\n        sprintf(header, \"%s\\r\\n\", header);\r\n        size_t send_len = (size_t)writen(fd, header, strlen(header));\r\n        if(send_len != strlen(header))\r\n        {\r\n            perror(\"Send header failed\");\r\n            return ANALYSIS_ERROR;\r\n        }\r\n        \r\n        send_len = (size_t)writen(fd, send_content, strlen(send_content));\r\n        if(send_len != strlen(send_content))\r\n        {\r\n            perror(\"Send content failed\");\r\n            return ANALYSIS_ERROR;\r\n        }\r\n        cout << \"content size ==\" << content.size() << endl;\r\n        vector<char> data(content.begin(), content.end());\r\n        Mat test = imdecode(data, CV_LOAD_IMAGE_ANYDEPTH|CV_LOAD_IMAGE_ANYCOLOR);\r\n        imwrite(\"receive.bmp\", test);\r\n        return ANALYSIS_SUCCESS;\r\n    }\r\n    else if (method == METHOD_GET)\r\n    {\r\n        char header[MAX_BUFF];\r\n        sprintf(header, \"HTTP/1.1 %d %s\\r\\n\", 200, \"OK\");\r\n        if(headers.find(\"Connection\") != headers.end() && headers[\"Connection\"] == \"keep-alive\")\r\n        {\r\n            keep_alive = true;\r\n            sprintf(header, \"%sConnection: keep-alive\\r\\n\", header);\r\n            sprintf(header, \"%sKeep-Alive: timeout=%d\\r\\n\", header, EPOLL_WAIT_TIME);\r\n        }\r\n        int dot_pos = file_name.find('.');\r\n        const char* filetype;\r\n        if (dot_pos < 0) \r\n            filetype = MimeType::getMime(\"default\").c_str();\r\n        else\r\n            filetype = MimeType::getMime(file_name.substr(dot_pos)).c_str();\r\n        struct stat sbuf;\r\n        if (stat(file_name.c_str(), &sbuf) < 0)\r\n        {\r\n            handleError(fd, 404, \"Not Found!\");\r\n            return ANALYSIS_ERROR;\r\n        }\r\n\r\n        sprintf(header, \"%sContent-type: %s\\r\\n\", header, filetype);\r\n        // 通过Content-length返回文件大小\r\n        sprintf(header, \"%sContent-length: %ld\\r\\n\", header, sbuf.st_size);\r\n\r\n        sprintf(header, \"%s\\r\\n\", header);\r\n        size_t send_len = (size_t)writen(fd, header, strlen(header));\r\n        if(send_len != strlen(header))\r\n        {\r\n            perror(\"Send header failed\");\r\n            return ANALYSIS_ERROR;\r\n        }\r\n        int src_fd = open(file_name.c_str(), O_RDONLY, 0);\r\n        char *src_addr = static_cast<char*>(mmap(NULL, sbuf.st_size, PROT_READ, MAP_PRIVATE, src_fd, 0));\r\n        close(src_fd);\r\n    \r\n        // 发送文件并校验完整性\r\n        send_len = writen(fd, src_addr, sbuf.st_size);\r\n        if(send_len != sbuf.st_size)\r\n        {\r\n            perror(\"Send file failed\");\r\n            return ANALYSIS_ERROR;\r\n        }\r\n        munmap(src_addr, sbuf.st_size);\r\n        return ANALYSIS_SUCCESS;\r\n    }\r\n    else\r\n        return ANALYSIS_ERROR;\r\n}\r\n\r\nvoid RequestData::handleError(int fd, int err_num, string short_msg)\r\n{\r\n    short_msg = \" \" + short_msg;\r\n    char send_buff[MAX_BUFF];\r\n    string body_buff, header_buff;\r\n    body_buff += \"<html><title>TKeed Error</title>\";\r\n    body_buff += \"<body bgcolor=\\\"ffffff\\\">\";\r\n    body_buff += to_string(err_num) + short_msg;\r\n    body_buff += \"<hr><em> LinYa's Web Server</em>\\n</body></html>\";\r\n\r\n    header_buff += \"HTTP/1.1 \" + to_string(err_num) + short_msg + \"\\r\\n\";\r\n    header_buff += \"Content-type: text/html\\r\\n\";\r\n    header_buff += \"Connection: close\\r\\n\";\r\n    header_buff += \"Content-length: \" + to_string(body_buff.size()) + \"\\r\\n\";\r\n    header_buff += \"\\r\\n\";\r\n    sprintf(send_buff, \"%s\", header_buff.c_str());\r\n    writen(fd, send_buff, strlen(send_buff));\r\n    sprintf(send_buff, \"%s\", body_buff.c_str());\r\n    writen(fd, send_buff, strlen(send_buff));\r\n}"
  },
  {
    "path": "old_version/old_version_0.4/requestData.h",
    "content": "#pragma once\r\n#include \"timer.h\"\r\n#include <string>\r\n#include <unordered_map>\r\n#include <memory>\r\n\r\nconst int STATE_PARSE_URI = 1;\r\nconst int STATE_PARSE_HEADERS = 2;\r\nconst int STATE_RECV_BODY = 3;\r\nconst int STATE_ANALYSIS = 4;\r\nconst int STATE_FINISH = 5;\r\n\r\nconst int MAX_BUFF = 4096;\r\n\r\n// 有请求出现但是读不到数据,可能是Request Aborted,\r\n// 或者来自网络的数据没有达到等原因,\r\n// 对这样的请求尝试超过一定的次数就抛弃\r\nconst int AGAIN_MAX_TIMES = 200;\r\n\r\nconst int PARSE_URI_AGAIN = -1;\r\nconst int PARSE_URI_ERROR = -2;\r\nconst int PARSE_URI_SUCCESS = 0;\r\n\r\nconst int PARSE_HEADER_AGAIN = -1;\r\nconst int PARSE_HEADER_ERROR = -2;\r\nconst int PARSE_HEADER_SUCCESS = 0;\r\n\r\nconst int ANALYSIS_ERROR = -2;\r\nconst int ANALYSIS_SUCCESS = 0;\r\n\r\nconst int METHOD_POST = 1;\r\nconst int METHOD_GET = 2;\r\nconst int HTTP_10 = 1;\r\nconst int HTTP_11 = 2;\r\n\r\nconst int EPOLL_WAIT_TIME = 500;\r\n\r\nclass MimeType\r\n{\r\nprivate:\r\n    static void init();\r\n    static std::unordered_map<std::string, std::string> mime;\r\n    MimeType();\r\n    MimeType(const MimeType &m);\r\n\r\npublic:\r\n    static std::string getMime(const std::string &suffix);\r\n\r\nprivate:\r\n    static pthread_once_t once_control;\r\n};\r\n\r\nenum HeadersState\r\n{\r\n    h_start = 0,\r\n    h_key,\r\n    h_colon,\r\n    h_spaces_after_colon,\r\n    h_value,\r\n    h_CR,\r\n    h_LF,\r\n    h_end_CR,\r\n    h_end_LF\r\n};\r\n\r\nclass TimerNode;\r\n\r\nclass RequestData : public std::enable_shared_from_this<RequestData>\r\n{\r\nprivate:\r\n    int againTimes;\r\n    std::string path;\r\n    int fd;\r\n    int epollfd;\r\n    // content的内容用完就清\r\n    std::string content;\r\n    int method;\r\n    int HTTPversion;\r\n    std::string file_name;\r\n    int now_read_pos;\r\n    int state;\r\n    int h_state;\r\n    bool isfinish;\r\n    bool keep_alive;\r\n    std::unordered_map<std::string, std::string> headers;\r\n    std::weak_ptr<TimerNode> timer;\r\n\r\nprivate:\r\n    int parse_URI();\r\n    int parse_Headers();\r\n    int analysisRequest();\r\n\r\npublic:\r\n\r\n    RequestData();\r\n    RequestData(int _epollfd, int _fd, std::string _path);\r\n    ~RequestData();\r\n    void linkTimer(std::shared_ptr<TimerNode> mtimer);\r\n    void reset();\r\n    void seperateTimer();\r\n    int getFd();\r\n    void setFd(int _fd);\r\n    void handleRequest();\r\n    void handleError(int fd, int err_num, std::string short_msg);\r\n};\r\n\r\n"
  },
  {
    "path": "old_version/old_version_0.4/threadpool.cpp",
    "content": "#include \"threadpool.h\"\r\n\r\n\r\npthread_mutex_t ThreadPool::lock = PTHREAD_MUTEX_INITIALIZER;\r\npthread_cond_t ThreadPool::notify = PTHREAD_COND_INITIALIZER;\r\nstd::vector<pthread_t> ThreadPool::threads;\r\nstd::vector<ThreadPoolTask> ThreadPool::queue;\r\nint ThreadPool::thread_count = 0;\r\nint ThreadPool::queue_size = 0;\r\nint ThreadPool::head = 0;\r\nint ThreadPool::tail = 0;\r\nint ThreadPool::count = 0;\r\nint ThreadPool::shutdown = 0;\r\nint ThreadPool::started = 0;\r\n\r\nint ThreadPool::threadpool_create(int _thread_count, int _queue_size)\r\n{\r\n    bool err = false;\r\n    do\r\n    {\r\n        if(_thread_count <= 0 || _thread_count > MAX_THREADS || _queue_size <= 0 || _queue_size > MAX_QUEUE) \r\n        {\r\n            _thread_count = 4;\r\n            _queue_size = 1024;\r\n        }\r\n    \r\n        thread_count = 0;\r\n        queue_size = _queue_size;\r\n        head = tail = count = 0;\r\n        shutdown = started = 0;\r\n\r\n        threads.resize(_thread_count);\r\n        queue.resize(_queue_size);\r\n    \r\n        /* Start worker threads */\r\n        for(int i = 0; i < _thread_count; ++i) \r\n        {\r\n            if(pthread_create(&threads[i], NULL, threadpool_thread, (void*)(0)) != 0) \r\n            {\r\n                //threadpool_destroy(pool, 0);\r\n                return -1;\r\n            }\r\n            ++thread_count;\r\n            ++started;\r\n        }\r\n    } while(false);\r\n    \r\n    if (err) \r\n    {\r\n        //threadpool_free(pool);\r\n        return -1;\r\n    }\r\n    return 0;\r\n}\r\n\r\nvoid myHandler(std::shared_ptr<void> req)\r\n{\r\n    std::shared_ptr<RequestData> request = std::static_pointer_cast<RequestData>(req);\r\n    request->handleRequest();\r\n}\r\n\r\nint ThreadPool::threadpool_add(std::shared_ptr<void> args, std::function<void(std::shared_ptr<void>)> fun)\r\n{\r\n    int next, err = 0;\r\n    if(pthread_mutex_lock(&lock) != 0)\r\n        return THREADPOOL_LOCK_FAILURE;\r\n    do \r\n    {\r\n        next = (tail + 1) % queue_size;\r\n        // 队列满\r\n        if(count == queue_size) \r\n        {\r\n            err = THREADPOOL_QUEUE_FULL;\r\n            break;\r\n        }\r\n        // 已关闭\r\n        if(shutdown)\r\n        {\r\n            err = THREADPOOL_SHUTDOWN;\r\n            break;\r\n        }\r\n        queue[tail].fun = fun;\r\n        queue[tail].args = args;\r\n        tail = next;\r\n        ++count;\r\n        \r\n        /* pthread_cond_broadcast */\r\n        if(pthread_cond_signal(&notify) != 0) \r\n        {\r\n            err = THREADPOOL_LOCK_FAILURE;\r\n            break;\r\n        }\r\n    } while(false);\r\n\r\n    if(pthread_mutex_unlock(&lock) != 0)\r\n        err = THREADPOOL_LOCK_FAILURE;\r\n    return err;\r\n}\r\n\r\n\r\nint ThreadPool::threadpool_destroy(ShutDownOption shutdown_option)\r\n{\r\n    printf(\"Thread pool destroy !\\n\");\r\n    int i, err = 0;\r\n\r\n    if(pthread_mutex_lock(&lock) != 0) \r\n    {\r\n        return THREADPOOL_LOCK_FAILURE;\r\n    }\r\n    do \r\n    {\r\n        if(shutdown) {\r\n            err = THREADPOOL_SHUTDOWN;\r\n            break;\r\n        }\r\n        shutdown = shutdown_option;\r\n\r\n        if((pthread_cond_broadcast(&notify) != 0) ||\r\n           (pthread_mutex_unlock(&lock) != 0)) {\r\n            err = THREADPOOL_LOCK_FAILURE;\r\n            break;\r\n        }\r\n\r\n        for(i = 0; i < thread_count; ++i)\r\n        {\r\n            if(pthread_join(threads[i], NULL) != 0)\r\n            {\r\n                err = THREADPOOL_THREAD_FAILURE;\r\n            }\r\n        }\r\n    } while(false);\r\n\r\n    if(!err) \r\n    {\r\n        threadpool_free();\r\n    }\r\n    return err;\r\n}\r\n\r\nint ThreadPool::threadpool_free()\r\n{\r\n    if(started > 0)\r\n        return -1;\r\n    pthread_mutex_lock(&lock);\r\n    pthread_mutex_destroy(&lock);\r\n    pthread_cond_destroy(&notify);\r\n    return 0;\r\n}\r\n\r\n\r\nvoid *ThreadPool::threadpool_thread(void *args)\r\n{\r\n    while (true)\r\n    {\r\n        ThreadPoolTask task;\r\n        pthread_mutex_lock(&lock);\r\n        while((count == 0) && (!shutdown)) \r\n        {\r\n            pthread_cond_wait(&notify, &lock);\r\n        }\r\n        if((shutdown == immediate_shutdown) ||\r\n           ((shutdown == graceful_shutdown) && (count == 0)))\r\n        {\r\n            break;\r\n        }\r\n        task.fun = queue[head].fun;\r\n        task.args = queue[head].args;\r\n        queue[head].fun = NULL;\r\n        queue[head].args.reset();\r\n        head = (head + 1) % queue_size;\r\n        --count;\r\n        pthread_mutex_unlock(&lock);\r\n        (task.fun)(task.args);\r\n    }\r\n    --started;\r\n    pthread_mutex_unlock(&lock);\r\n    printf(\"This threadpool thread finishs!\\n\");\r\n    pthread_exit(NULL);\r\n    return(NULL);\r\n}"
  },
  {
    "path": "old_version/old_version_0.4/threadpool.h",
    "content": "#pragma once\r\n#include \"requestData.h\"\r\n#include <pthread.h>\r\n#include <functional>\r\n#include <memory>\r\n#include <vector>\r\n\r\n\r\nconst int THREADPOOL_INVALID = -1;\r\nconst int THREADPOOL_LOCK_FAILURE = -2;\r\nconst int THREADPOOL_QUEUE_FULL = -3;\r\nconst int THREADPOOL_SHUTDOWN = -4;\r\nconst int THREADPOOL_THREAD_FAILURE = -5;\r\nconst int THREADPOOL_GRACEFUL = 1;\r\n\r\nconst int MAX_THREADS = 1024;\r\nconst int MAX_QUEUE = 65535;\r\n\r\ntypedef enum\r\n{\r\n    immediate_shutdown = 1,\r\n    graceful_shutdown  = 2\r\n} ShutDownOption;\r\n\r\nstruct ThreadPoolTask\r\n{\r\n    std::function<void(std::shared_ptr<void>)> fun;\r\n    std::shared_ptr<void> args;\r\n};\r\n\r\n/**\r\n *  @struct threadpool\r\n *  @brief The threadpool struct\r\n *\r\n *  @var notify       Condition variable to notify worker threads.\r\n *  @var threads      Array containing worker threads ID.\r\n *  @var thread_count Number of threads\r\n *  @var queue        Array containing the task queue.\r\n *  @var queue_size   Size of the task queue.\r\n *  @var head         Index of the first element.\r\n *  @var tail         Index of the next element.\r\n *  @var count        Number of pending tasks\r\n *  @var shutdown     Flag indicating if the pool is shutting down\r\n *  @var started      Number of started threads\r\n */\r\nvoid myHandler(std::shared_ptr<void> req);\r\nclass ThreadPool\r\n{\r\nprivate:\r\n    static pthread_mutex_t lock;\r\n    static pthread_cond_t notify;\r\n    static std::vector<pthread_t> threads;\r\n    static std::vector<ThreadPoolTask> queue;\r\n    static int thread_count;\r\n    static int queue_size;\r\n    static int head;\r\n    // tail 指向尾节点的下一节点\r\n    static int tail;\r\n    static int count;\r\n    static int shutdown;\r\n    static int started;\r\npublic:\r\n    static int threadpool_create(int _thread_count, int _queue_size);\r\n    static int threadpool_add(std::shared_ptr<void> args, std::function<void(std::shared_ptr<void>)> fun = myHandler);\r\n    static int threadpool_destroy(ShutDownOption shutdown_option = graceful_shutdown);\r\n    static int threadpool_free();\r\n    static void *threadpool_thread(void *args);\r\n};\r\n"
  },
  {
    "path": "old_version/old_version_0.4/timer.cpp",
    "content": "#include \"timer.h\"\r\n#include \"epoll.h\"\r\n#include <unordered_map>\r\n#include <string>\r\n#include <sys/time.h>\r\n#include <unistd.h>\r\n#include <deque>\r\n#include <queue>\r\n\r\n#include <iostream>\r\nusing namespace std;\r\n\r\n\r\nTimerNode::TimerNode(SP_ReqData _request_data, int timeout): \r\n    deleted(false), \r\n    request_data(_request_data)\r\n{\r\n    cout << \"TimerNode()\" << endl;\r\n    struct timeval now;\r\n    gettimeofday(&now, NULL);\r\n    // 以毫秒计\r\n    expired_time = ((now.tv_sec * 1000) + (now.tv_usec / 1000)) + timeout;\r\n}\r\n\r\nTimerNode::~TimerNode()\r\n{\r\n    cout << \"~TimerNode()\" << endl;\r\n    if (request_data)\r\n    {\r\n        Epoll::epoll_del(request_data->getFd());\r\n    }\r\n    //request_data.reset();\r\n    // if (request_data)\r\n    // {\r\n    //     cout << \"request_data=\" << request_data << endl;\r\n    //     delete request_data;\r\n    //     request_data = NULL;\r\n    // }\r\n}\r\n\r\nvoid TimerNode::update(int timeout)\r\n{\r\n    struct timeval now;\r\n    gettimeofday(&now, NULL);\r\n    expired_time = ((now.tv_sec * 1000) + (now.tv_usec / 1000)) + timeout;\r\n}\r\n\r\nbool TimerNode::isvalid()\r\n{\r\n    struct timeval now;\r\n    gettimeofday(&now, NULL);\r\n    size_t temp = ((now.tv_sec * 1000) + (now.tv_usec / 1000));\r\n    if (temp < expired_time)\r\n    {\r\n        return true;\r\n    }\r\n    else\r\n    {\r\n        this->setDeleted();\r\n        return false;\r\n    }\r\n}\r\n\r\nvoid TimerNode::clearReq()\r\n{\r\n    request_data.reset();\r\n    this->setDeleted();\r\n}\r\n\r\nvoid TimerNode::setDeleted()\r\n{\r\n    deleted = true;\r\n}\r\n\r\nbool TimerNode::isDeleted() const\r\n{\r\n    return deleted;\r\n}\r\n\r\nsize_t TimerNode::getExpTime() const\r\n{\r\n    return expired_time;\r\n}\r\n\r\nTimerManager::TimerManager()\r\n{\r\n}\r\n\r\nTimerManager::~TimerManager()\r\n{\r\n}\r\n\r\nvoid TimerManager::addTimer(SP_ReqData request_data, int timeout)\r\n{\r\n    SP_TimerNode new_node(new TimerNode(request_data, timeout));\r\n    {\r\n        MutexLockGuard locker(lock);\r\n        TimerNodeQueue.push(new_node);\r\n    }\r\n    request_data->linkTimer(new_node);\r\n}\r\n\r\nvoid TimerManager::addTimer(SP_TimerNode timer_node)\r\n{\r\n\r\n}\r\n\r\n/* 处理逻辑是这样的~\r\n因为(1) 优先队列不支持随机访问\r\n(2) 即使支持，随机删除某节点后破坏了堆的结构，需要重新更新堆结构。\r\n所以对于被置为deleted的时间节点，会延迟到它(1)超时 或 (2)它前面的节点都被删除时，它才会被删除。\r\n一个点被置为deleted,它最迟会在TIMER_TIME_OUT时间后被删除。\r\n这样做有两个好处：\r\n(1) 第一个好处是不需要遍历优先队列，省时。\r\n(2) 第二个好处是给超时时间一个容忍的时间，就是设定的超时时间是删除的下限(并不是一到超时时间就立即删除)，如果监听的请求在超时后的下一次请求中又一次出现了，\r\n就不用再重新申请RequestData节点了，这样可以继续重复利用前面的RequestData，减少了一次delete和一次new的时间。\r\n*/\r\n\r\nvoid TimerManager::handle_expired_event()\r\n{\r\n    MutexLockGuard locker(lock);\r\n    while (!TimerNodeQueue.empty())\r\n    {\r\n        SP_TimerNode ptimer_now = TimerNodeQueue.top();\r\n        if (ptimer_now->isDeleted())\r\n        {\r\n            TimerNodeQueue.pop();\r\n            //delete ptimer_now;\r\n        }\r\n        else if (ptimer_now->isvalid() == false)\r\n        {\r\n            TimerNodeQueue.pop();\r\n            //delete ptimer_now;\r\n        }\r\n        else\r\n        {\r\n            break;\r\n        }\r\n    }\r\n}"
  },
  {
    "path": "old_version/old_version_0.4/timer.h",
    "content": "#pragma once\r\n#include \"requestData.h\"\r\n#include \"./base/nocopyable.hpp\"\r\n#include \"./base/mutexLock.hpp\"\r\n#include <unistd.h>\r\n#include <memory>\r\n#include <queue>\r\n#include <deque>\r\n\r\nclass RequestData;\r\n\r\nclass TimerNode\r\n{\r\n    typedef std::shared_ptr<RequestData> SP_ReqData;\r\nprivate:\r\n    bool deleted;\r\n    size_t expired_time;\r\n    SP_ReqData request_data;\r\npublic:\r\n    TimerNode(SP_ReqData _request_data, int timeout);\r\n    ~TimerNode();\r\n    void update(int timeout);\r\n    bool isvalid();\r\n    void clearReq();\r\n    void setDeleted();\r\n    bool isDeleted() const;\r\n    size_t getExpTime() const;\r\n};\r\n\r\nstruct timerCmp\r\n{\r\n    bool operator()(std::shared_ptr<TimerNode> &a, std::shared_ptr<TimerNode> &b) const\r\n    {\r\n        return a->getExpTime() > b->getExpTime();\r\n    }\r\n};\r\n\r\nclass TimerManager\r\n{\r\n    typedef std::shared_ptr<RequestData> SP_ReqData;\r\n    typedef std::shared_ptr<TimerNode> SP_TimerNode;\r\nprivate:\r\n    std::priority_queue<SP_TimerNode, std::deque<SP_TimerNode>, timerCmp> TimerNodeQueue;\r\n    MutexLock lock;\r\npublic:\r\n    TimerManager();\r\n    ~TimerManager();\r\n    void addTimer(SP_ReqData request_data, int timeout);\r\n    void addTimer(SP_TimerNode timer_node);\r\n    void handle_expired_event();\r\n};"
  },
  {
    "path": "old_version/old_version_0.4/util.cpp",
    "content": "#include \"util.h\"\r\n#include <unistd.h>\r\n#include <fcntl.h>\r\n#include <signal.h>\r\n#include <errno.h>\r\n#include <string.h>\r\n\r\nssize_t readn(int fd, void *buff, size_t n)\r\n{\r\n    size_t nleft = n;\r\n    ssize_t nread = 0;\r\n    ssize_t readSum = 0;\r\n    char *ptr = (char*)buff;\r\n    while (nleft > 0)\r\n    {\r\n        if ((nread = read(fd, ptr, nleft)) < 0)\r\n        {\r\n            if (errno == EINTR)\r\n                nread = 0;\r\n            else if (errno == EAGAIN)\r\n            {\r\n                return readSum;\r\n            }\r\n            else\r\n            {\r\n                return -1;\r\n            }  \r\n        }\r\n        else if (nread == 0)\r\n            break;\r\n        readSum += nread;\r\n        nleft -= nread;\r\n        ptr += nread;\r\n    }\r\n    return readSum;\r\n}\r\n\r\nssize_t writen(int fd, void *buff, size_t n)\r\n{\r\n    size_t nleft = n;\r\n    ssize_t nwritten = 0;\r\n    ssize_t writeSum = 0;\r\n    char *ptr = (char*)buff;\r\n    while (nleft > 0)\r\n    {\r\n        if ((nwritten = write(fd, ptr, nleft)) <= 0)\r\n        {\r\n            if (nwritten < 0)\r\n            {\r\n                if (errno == EINTR || errno == EAGAIN)\r\n                {\r\n                    nwritten = 0;\r\n                    continue;\r\n                }\r\n                else\r\n                    return -1;\r\n            }\r\n        }\r\n        writeSum += nwritten;\r\n        nleft -= nwritten;\r\n        ptr += nwritten;\r\n    }\r\n    return writeSum;\r\n}\r\n\r\nvoid handle_for_sigpipe()\r\n{\r\n    struct sigaction sa;\r\n    memset(&sa, '\\0', sizeof(sa));\r\n    sa.sa_handler = SIG_IGN;\r\n    sa.sa_flags = 0;\r\n    if(sigaction(SIGPIPE, &sa, NULL))\r\n        return;\r\n}\r\n\r\nint setSocketNonBlocking(int fd)\r\n{\r\n    int flag = fcntl(fd, F_GETFL, 0);\r\n    if(flag == -1)\r\n        return -1;\r\n\r\n    flag |= O_NONBLOCK;\r\n    if(fcntl(fd, F_SETFL, flag) == -1)\r\n        return -1;\r\n    return 0;\r\n}"
  },
  {
    "path": "old_version/old_version_0.4/util.h",
    "content": "#pragma once\r\n#include <cstdlib>\r\n\r\nssize_t readn(int fd, void *buff, size_t n);\r\nssize_t writen(int fd, void *buff, size_t n);\r\nvoid handle_for_sigpipe();\r\nint setSocketNonBlocking(int fd);"
  },
  {
    "path": "old_version/old_version_0.5/Makefile",
    "content": "SOURCE  := $(wildcard *.cpp)\r\nOBJS    := $(patsubst %.c,%.o,$(patsubst %.cpp,%.o,$(SOURCE)))\r\n\r\nTARGET  := myserver\r\nCC      := g++\r\nLIBS    := -lpthread -lopencv_core -lopencv_imgproc -lopencv_highgui\r\nINCLUDE:= -I./usr/local/include/opencv\r\nCFLAGS  := -std=c++11 -g -Wall -O0 $(INCLUDE) -D_PTHREADS\r\nCXXFLAGS:= $(CFLAGS)\r\n\r\n.PHONY : objs clean veryclean rebuild all\r\nall : $(TARGET)\r\nobjs : $(OBJS)\r\nrebuild: veryclean all\r\nclean :\r\n\trm -fr *.o\r\nveryclean : clean\r\n\trm -rf $(TARGET)\r\n\r\n$(TARGET) : $(OBJS)\r\n\t$(CC) $(CXXFLAGS) -o $@ $(OBJS) $(LDFLAGS) $(LIBS)"
  },
  {
    "path": "old_version/old_version_0.5/Makefile1",
    "content": "cc=g++ -std=c++11\r\nmain: main.o RequestData.o epoll.o threadpool.o\r\n\t$(cc) -o main main.o RequestData.o epoll.o threadpool.o -lpthread -lopencv_core -lopencv_imgproc -lopencv_highgui -lopencv_ml \r\nmain.o: main.cpp\r\n\t$(cc) -I/usr/local/include/opencv -c main.cpp\r\nRequestData.o: requestData.h RequestData.cpp\r\n\t$(cc) -I/usr/local/include/opencv -c RequestData.cpp\r\nepoll.o: epoll.h epoll.cpp\r\n\t$(cc) -I/usr/local/include/opencv -c epoll.cpp\r\nthreadpool.o: threadpool.h threadpool.cpp\r\n\t$(cc) -I/usr/local/include/opencv -c threadpool.cpp\r\n.PHONY: clean\r\nclean:\r\n\t-rm main *.o\r\n\r\n\r\n\r\n\r\n.PHONY : everything objs clean veryclean rebuild\r\neverything :\r\n\t$(TARGET)\r\nall :\r\n\t$(TARGET)\r\nobjs :\r\n\t$(OBJS)\r\nrebuild:\r\n\tveryclean everything \r\nclean :\r\n\trm -fr *.o\r\nveryclean :\r\n\tclean rm -fr $(TARGET)"
  },
  {
    "path": "old_version/old_version_0.5/base/condition.hpp",
    "content": "#pragma once\r\n#include \"nocopyable.hpp\"\r\n#include \"mutexLock.hpp\"\r\n#include <pthread.h>\r\n\r\nclass Condition: nocopyable\r\n{\r\npublic:\r\n    explicit Condition(MutexLock &_mutex):\r\n        mutex(_mutex)\r\n    {\r\n        pthread_cond_init(cond, NULL);\r\n    }\r\n    ~Condition()\r\n    {\r\n        pthread_cond_destroy(cond);\r\n    }\r\n    void wait()\r\n    {\r\n        pthread_cond_wait(&cond, mutex.get());\r\n    }\r\n    void notify()\r\n    {\r\n        pthread_cond_signal(&cond);\r\n    }\r\n    void notifyAll()\r\n    {\r\n        pthread_cond_broadcast(&cond);\r\n    }\r\nprivate:\r\n    MutexLock &mutex;\r\n    pthread_cond_t cond;\r\n}"
  },
  {
    "path": "old_version/old_version_0.5/base/mutexLock.hpp",
    "content": "#pragma once\r\n#include \"nocopyable.hpp\"\r\n#include <pthread.h>\r\n#include <cstdio>\r\n\r\nclass MutexLock: noncopyable\r\n{\r\npublic:\r\n    MutexLock()\r\n    {\r\n        pthread_mutex_init(&mutex, NULL);\r\n    }\r\n    ~MutexLock()\r\n    {\r\n        pthread_mutex_lock(&mutex);\r\n        pthread_mutex_destroy(&mutex);\r\n    }\r\n    void lock()\r\n    {\r\n        pthread_mutex_lock(&mutex);\r\n    }\r\n    void unlock()\r\n    {\r\n        pthread_mutex_unlock(&mutex);\r\n    }\r\n    pthread_mutex_t *get()\r\n    {\r\n        return &mutex;\r\n    }\r\nprivate:\r\n    pthread_mutex_t mutex;\r\n\r\n// 友元类不受访问权限影响\r\nprivate:\r\n    friend class Condition;\r\n};\r\n\r\n\r\nclass MutexLockGuard: noncopyable\r\n{\r\npublic:\r\n    explicit MutexLockGuard(MutexLock &_mutex):\r\n    mutex(_mutex)\r\n    {\r\n        mutex.lock();\r\n    }\r\n    ~MutexLockGuard()\r\n    {\r\n        mutex.unlock();\r\n    }\r\nprivate:\r\n    MutexLock &mutex;\r\n};"
  },
  {
    "path": "old_version/old_version_0.5/base/nocopyable.hpp",
    "content": "#pragma once\r\nclass noncopyable\r\n{\r\nprotected:\r\n    noncopyable() {}\r\n    ~noncopyable() {}\r\nprivate:\r\n    noncopyable(const noncopyable&);\r\n    const noncopyable& operator=(const noncopyable&);\r\n};"
  },
  {
    "path": "old_version/old_version_0.5/config.h",
    "content": "#pragma once\r\n\r\n"
  },
  {
    "path": "old_version/old_version_0.5/epoll.cpp",
    "content": "#include \"epoll.h\"\r\n#include \"threadpool.h\"\r\n#include \"util.h\"\r\n#include <sys/epoll.h>\r\n#include <errno.h>\r\n#include <sys/socket.h>\r\n#include <netinet/in.h>\r\n#include <string.h>\r\n#include <queue>\r\n#include <deque>\r\n\r\n#include <iostream>\r\nusing namespace std;\r\n\r\nint TIMER_TIME_OUT = 500;\r\n\r\nepoll_event *Epoll::events;\r\nEpoll::SP_ReqData Epoll::fd2req[MAXFDS];\r\nint Epoll::epoll_fd = 0;\r\nconst std::string Epoll::PATH = \"/\";\r\n\r\nTimerManager Epoll::timer_manager;\r\n\r\n\r\nint Epoll::epoll_init(int maxevents, int listen_num)\r\n{\r\n    epoll_fd = epoll_create(listen_num + 1);\r\n    if(epoll_fd == -1)\r\n        return -1;\r\n    //events.reset(new epoll_event[maxevents], [](epoll_event *data){delete [] data;});\r\n    events = new epoll_event[maxevents];\r\n    return 0;\r\n}\r\n\r\n// 注册新描述符\r\nint Epoll::epoll_add(int fd, SP_ReqData request, __uint32_t events)\r\n{\r\n    struct epoll_event event;\r\n    event.data.fd = fd;\r\n    event.events = events;\r\n    fd2req[fd] = request;\r\n    if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &event) < 0)\r\n    {\r\n        perror(\"epoll_add error\");\r\n        return -1;\r\n    }\r\n    return 0;\r\n}\r\n\r\n// 修改描述符状态\r\nint Epoll::epoll_mod(int fd, SP_ReqData request, __uint32_t events)\r\n{\r\n    \r\n    struct epoll_event event;\r\n    event.data.fd = fd;\r\n    event.events = events;\r\n    fd2req[fd] = request;\r\n    if(epoll_ctl(epoll_fd, EPOLL_CTL_MOD, fd, &event) < 0)\r\n    {\r\n        perror(\"epoll_mod error\");\r\n        fd2req[fd].reset();\r\n        return -1;\r\n    }\r\n    return 0;\r\n}\r\n\r\n// 从epoll中删除描述符\r\nint Epoll::epoll_del(int fd, __uint32_t events)\r\n{\r\n    struct epoll_event event;\r\n    event.data.fd = fd;\r\n    event.events = events;\r\n    if(epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fd, &event) < 0)\r\n    {\r\n        perror(\"epoll_del error\");\r\n        return -1;\r\n    }\r\n    fd2req[fd].reset();\r\n    return 0;\r\n}\r\n\r\n// 返回活跃事件数\r\nvoid Epoll::my_epoll_wait(int listen_fd, int max_events, int timeout)\r\n{\r\n    int event_count = epoll_wait(epoll_fd, events, max_events, timeout);\r\n    if (event_count < 0)\r\n        perror(\"epoll wait error\");\r\n    std::vector<SP_ReqData> req_data = getEventsRequest(listen_fd, event_count, PATH);\r\n    if (req_data.size() > 0)\r\n    {\r\n        for (auto &req: req_data)\r\n        {\r\n            if (ThreadPool::threadpool_add(req) < 0)\r\n            {\r\n                // 线程池满了或者关闭了等原因，抛弃本次监听到的请求。\r\n                break;\r\n            }\r\n        }\r\n    }\r\n    timer_manager.handle_expired_event();\r\n}\r\n#include <iostream>\r\n#include <arpa/inet.h>\r\nusing namespace std;\r\nvoid Epoll::acceptConnection(int listen_fd, int epoll_fd, const std::string path)\r\n{\r\n    struct sockaddr_in client_addr;\r\n    memset(&client_addr, 0, sizeof(struct sockaddr_in));\r\n    //socklen_t client_addr_len = 0;\r\n    socklen_t client_addr_len = sizeof(client_addr);\r\n    int accept_fd = 0;\r\n    while((accept_fd = accept(listen_fd, (struct sockaddr*)&client_addr, &client_addr_len)) > 0)\r\n    {\r\n        cout << inet_ntoa(client_addr.sin_addr) << endl;\r\n        cout << ntohs(client_addr.sin_port) << endl;\r\n        /*\r\n        // TCP的保活机制默认是关闭的\r\n        int optval = 0;\r\n        socklen_t len_optval = 4;\r\n        getsockopt(accept_fd, SOL_SOCKET,  SO_KEEPALIVE, &optval, &len_optval);\r\n        cout << \"optval ==\" << optval << endl;\r\n        */\r\n        // 限制服务器的最大并发连接数\r\n        if (accept_fd >= MAXFDS)\r\n        {\r\n            close(accept_fd);\r\n            continue;\r\n        }\r\n\r\n        // 设为非阻塞模式\r\n        int ret = setSocketNonBlocking(accept_fd);\r\n        if (ret < 0)\r\n        {\r\n            perror(\"Set non block failed!\");\r\n            return;\r\n        }\r\n\r\n        SP_ReqData req_info(new RequestData(epoll_fd, accept_fd, path));\r\n\r\n        // 文件描述符可以读，边缘触发(Edge Triggered)模式，保证一个socket连接在任一时刻只被一个线程处理\r\n        __uint32_t _epo_event = EPOLLIN | EPOLLET | EPOLLONESHOT;\r\n        Epoll::epoll_add(accept_fd, req_info, _epo_event);\r\n        // 新增时间信息\r\n        timer_manager.addTimer(req_info, TIMER_TIME_OUT);\r\n    }\r\n    //if(accept_fd == -1)\r\n     //   perror(\"accept\");\r\n}\r\n\r\n// 分发处理函数\r\nstd::vector<std::shared_ptr<RequestData>> Epoll::getEventsRequest(int listen_fd, int events_num, const std::string path)\r\n{\r\n    std::vector<SP_ReqData> req_data;\r\n    for(int i = 0; i < events_num; ++i)\r\n    {\r\n        // 获取有事件产生的描述符\r\n        int fd = events[i].data.fd;\r\n\r\n        // 有事件发生的描述符为监听描述符\r\n        if(fd == listen_fd)\r\n        {\r\n            //cout << \"This is listen_fd\" << endl;\r\n            acceptConnection(listen_fd, epoll_fd, path);\r\n        }\r\n        else if (fd < 3)\r\n        {\r\n            printf(\"fd < 3\\n\");\r\n            break;\r\n        }\r\n        else\r\n        {\r\n            // 排除错误事件\r\n            if ((events[i].events & EPOLLERR) || (events[i].events & EPOLLHUP))\r\n            {\r\n                printf(\"error event\\n\");\r\n                if (fd2req[fd])\r\n                    fd2req[fd]->seperateTimer();\r\n                fd2req[fd].reset();\r\n                continue;\r\n            }\r\n            // 将请求任务加入到线程池中\r\n            // 加入线程池之前将Timer和request分离\r\n\r\n            SP_ReqData cur_req = fd2req[fd];\r\n            \r\n            if (cur_req)\r\n            {\r\n                if ((events[i].events & EPOLLIN) || (events[i].events & EPOLLPRI))\r\n                    cur_req->enableRead();\r\n                else\r\n                    cur_req->enableWrite();\r\n                //printf(\"cur_req.use_count=%d\\n\", cur_req.use_count());\r\n                \r\n                cur_req->seperateTimer();\r\n                req_data.push_back(cur_req);\r\n                //cout << \"-getEventsRequest fd==\" << fd << endl;\r\n                fd2req[fd].reset();\r\n            }\r\n            else\r\n            {\r\n                cout << \"SP cur_req is invalid\" << endl;\r\n            }\r\n        }\r\n    }\r\n    return req_data;\r\n}\r\n\r\nvoid Epoll::add_timer(shared_ptr<RequestData> request_data, int timeout)\r\n{\r\n    timer_manager.addTimer(request_data, timeout);\r\n}"
  },
  {
    "path": "old_version/old_version_0.5/epoll.h",
    "content": "#pragma once\r\n#include \"requestData.h\"\r\n#include \"timer.h\"\r\n#include <vector>\r\n#include <unordered_map>\r\n#include <sys/epoll.h>\r\n#include <memory>\r\n\r\nclass Epoll\r\n{\r\npublic:\r\n    typedef std::shared_ptr<RequestData> SP_ReqData;\r\nprivate:\r\n    static const int MAXFDS = 1000;\r\n    static epoll_event *events;\r\n    static SP_ReqData fd2req[MAXFDS];\r\n    static int epoll_fd;\r\n    static const std::string PATH;\r\n\r\n    static TimerManager timer_manager;\r\npublic:\r\n    static int epoll_init(int maxevents, int listen_num);\r\n    static int epoll_add(int fd, SP_ReqData request, __uint32_t events);\r\n    static int epoll_mod(int fd, SP_ReqData request, __uint32_t events);\r\n    static int epoll_del(int fd, __uint32_t events = (EPOLLIN | EPOLLET | EPOLLONESHOT));\r\n    static void my_epoll_wait(int listen_fd, int max_events, int timeout);\r\n    static void acceptConnection(int listen_fd, int epoll_fd, const std::string path);\r\n    static std::vector<SP_ReqData> getEventsRequest(int listen_fd, int events_num, const std::string path);\r\n\r\n    static void add_timer(SP_ReqData request_data, int timeout);\r\n};"
  },
  {
    "path": "old_version/old_version_0.5/index.html",
    "content": "Hello World !"
  },
  {
    "path": "old_version/old_version_0.5/main.cpp",
    "content": "#include \"requestData.h\"\r\n#include \"epoll.h\"\r\n#include \"threadpool.h\"\r\n#include \"util.h\"\r\n#include \"config.h\"\r\n#include <sys/epoll.h>\r\n#include <queue>\r\n#include <sys/time.h>\r\n#include <sys/socket.h>\r\n#include <netinet/in.h>\r\n#include <stdio.h>\r\n#include <string.h>\r\n#include <cstdlib>\r\n#include <iostream>\r\n#include <vector>\r\n#include <unistd.h>\r\n#include <memory>\r\n\r\nusing namespace std;\r\n\r\nstatic const int MAXEVENTS = 5000;\r\nstatic const int LISTENQ = 1024;\r\nconst int THREADPOOL_THREAD_NUM = 4;\r\nconst int QUEUE_SIZE = 65535;\r\n\r\nconst int PORT = 8888;\r\nconst int ASK_STATIC_FILE = 1;\r\nconst int ASK_IMAGE_STITCH = 2;\r\n\r\nconst int TIMER_TIME_OUT = 500;\r\n\r\nvoid acceptConnection(int listen_fd, int epoll_fd, const string &path);\r\n\r\nint socket_bind_listen(int port)\r\n{\r\n    // 检查port值，取正确区间范围\r\n    if (port < 1024 || port > 65535)\r\n        return -1;\r\n\r\n    // 创建socket(IPv4 + TCP)，返回监听描述符\r\n    int listen_fd = 0;\r\n    if((listen_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1)\r\n        return -1;\r\n\r\n    // 消除bind时\"Address already in use\"错误\r\n    int optval = 1;\r\n    if(setsockopt(listen_fd, SOL_SOCKET,  SO_REUSEADDR, &optval, sizeof(optval)) == -1)\r\n        return -1;\r\n\r\n    // 设置服务器IP和Port，和监听描述副绑定\r\n    struct sockaddr_in server_addr;\r\n    bzero((char*)&server_addr, sizeof(server_addr));\r\n    server_addr.sin_family = AF_INET;\r\n    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);\r\n    server_addr.sin_port = htons((unsigned short)port);\r\n    if(bind(listen_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1)\r\n        return -1;\r\n\r\n    // 开始监听，最大等待队列长为LISTENQ\r\n    if(listen(listen_fd, LISTENQ) == -1)\r\n        return -1;\r\n\r\n    // 无效监听描述符\r\n    if(listen_fd == -1)\r\n    {\r\n        close(listen_fd);\r\n        return -1;\r\n    }\r\n    return listen_fd;\r\n}\r\n\r\n\r\nint main()\r\n{\r\n    #ifndef _PTHREADS\r\n        printf(\"_PTHREADS is not defined !\\n\");\r\n    #endif\r\n    handle_for_sigpipe();\r\n    if (Epoll::epoll_init(MAXEVENTS, LISTENQ) < 0)\r\n    {\r\n        perror(\"epoll init failed\");\r\n        return 1;\r\n    }\r\n    if (ThreadPool::threadpool_create(THREADPOOL_THREAD_NUM, QUEUE_SIZE) < 0)\r\n    {\r\n        printf(\"Threadpool create failed\\n\");\r\n        return 1;\r\n    }\r\n    int listen_fd = socket_bind_listen(PORT);\r\n    if (listen_fd < 0) \r\n    {\r\n        perror(\"socket bind failed\");\r\n        return 1;\r\n    }\r\n    if (setSocketNonBlocking(listen_fd) < 0)\r\n    {\r\n        perror(\"set socket non block failed\");\r\n        return 1;\r\n    }\r\n    shared_ptr<RequestData> request(new RequestData());\r\n    request->setFd(listen_fd);\r\n    if (Epoll::epoll_add(listen_fd, request, EPOLLIN | EPOLLET) < 0)\r\n    {\r\n        perror(\"epoll add error\");\r\n        return 1;\r\n    }\r\n    \r\n    while (true)\r\n    {\r\n        //sleep(10);\r\n        Epoll::my_epoll_wait(listen_fd, MAXEVENTS, -1);\r\n        \r\n        //ThreadPool::threadpool_destroy();\r\n        //break;\r\n    }\r\n    return 0;\r\n}"
  },
  {
    "path": "old_version/old_version_0.5/requestData.cpp",
    "content": "#include \"requestData.h\"\r\n#include \"util.h\"\r\n#include \"epoll.h\"\r\n#include <unistd.h>\r\n#include <fcntl.h>\r\n#include <sys/stat.h>\r\n#include <sys/mman.h>\r\n#include <queue>\r\n#include <cstdlib>\r\n#include <opencv/cv.h>\r\n#include <opencv2/core/core.hpp>\r\n#include <opencv2/highgui/highgui.hpp>\r\n#include <opencv2/opencv.hpp>\r\nusing namespace cv;\r\n\r\n//test\r\n#include <iostream>\r\nusing namespace std;\r\n\r\npthread_once_t MimeType::once_control = PTHREAD_ONCE_INIT;\r\nstd::unordered_map<std::string, std::string> MimeType::mime;\r\n\r\n\r\nvoid MimeType::init()\r\n{\r\n    mime[\".html\"] = \"text/html\";\r\n    mime[\".avi\"] = \"video/x-msvideo\";\r\n    mime[\".bmp\"] = \"image/bmp\";\r\n    mime[\".c\"] = \"text/plain\";\r\n    mime[\".doc\"] = \"application/msword\";\r\n    mime[\".gif\"] = \"image/gif\";\r\n    mime[\".gz\"] = \"application/x-gzip\";\r\n    mime[\".htm\"] = \"text/html\";\r\n    mime[\".ico\"] = \"application/x-ico\";\r\n    mime[\".jpg\"] = \"image/jpeg\";\r\n    mime[\".png\"] = \"image/png\";\r\n    mime[\".txt\"] = \"text/plain\";\r\n    mime[\".mp3\"] = \"audio/mp3\";\r\n    mime[\"default\"] = \"text/html\";\r\n}\r\n\r\nstd::string MimeType::getMime(const std::string &suffix)\r\n{\r\n    pthread_once(&once_control, MimeType::init);\r\n    if (mime.find(suffix) == mime.end())\r\n        return mime[\"default\"];\r\n    else\r\n        return mime[suffix];\r\n}\r\n\r\nRequestData::RequestData(): \r\n    now_read_pos(0), \r\n    state(STATE_PARSE_URI), \r\n    h_state(h_start), \r\n    keep_alive(false), \r\n    isAbleRead(true),\r\n    isAbleWrite(false),\r\n    events(0),\r\n    error(false)\r\n{\r\n    cout << \"RequestData()\" << endl;\r\n}\r\n\r\nRequestData::RequestData(int _epollfd, int _fd, std::string _path):\r\n    now_read_pos(0), \r\n    state(STATE_PARSE_URI), \r\n    h_state(h_start), \r\n    keep_alive(false), \r\n    path(_path), \r\n    fd(_fd), \r\n    epollfd(_epollfd),\r\n    isAbleRead(true),\r\n    isAbleWrite(false),\r\n    events(0),\r\n    error(false)\r\n{\r\n    cout << \"RequestData()\" << endl;\r\n}\r\n\r\nRequestData::~RequestData()\r\n{\r\n    cout << \"~RequestData()\" << endl;\r\n    close(fd);\r\n}\r\n\r\nvoid RequestData::linkTimer(shared_ptr<TimerNode> mtimer)\r\n{\r\n    // shared_ptr重载了bool, 但weak_ptr没有\r\n    //if (!timer.lock())\r\n    timer = mtimer;\r\n}\r\n\r\nint RequestData::getFd()\r\n{\r\n    return fd;\r\n}\r\nvoid RequestData::setFd(int _fd)\r\n{\r\n    fd = _fd;\r\n}\r\n\r\nvoid RequestData::reset()\r\n{\r\n    inBuffer.clear();\r\n    file_name.clear();\r\n    path.clear();\r\n    now_read_pos = 0;\r\n    state = STATE_PARSE_URI;\r\n    h_state = h_start;\r\n    headers.clear();\r\n    //keep_alive = false;\r\n    if (timer.lock())\r\n    {\r\n        shared_ptr<TimerNode> my_timer(timer.lock());\r\n        my_timer->clearReq();\r\n        timer.reset();\r\n    }\r\n}\r\n\r\nvoid RequestData::seperateTimer()\r\n{\r\n    //cout << \"seperateTimer\" << endl;\r\n    if (timer.lock())\r\n    {\r\n        shared_ptr<TimerNode> my_timer(timer.lock());\r\n        my_timer->clearReq();\r\n        timer.reset();\r\n    }\r\n}\r\n\r\nvoid RequestData::handleRead()\r\n{\r\n    do\r\n    {\r\n        int read_num = readn(fd, inBuffer);\r\n        //printf(\"read_num=%d\\n\", read_num);\r\n        if (read_num < 0)\r\n        {\r\n            perror(\"1\");\r\n            error = true;\r\n            handleError(fd, 400, \"Bad Request\");\r\n            break;\r\n        }\r\n        else if (read_num == 0)\r\n        {\r\n            // 有请求出现但是读不到数据，可能是Request Aborted，或者来自网络的数据没有达到等原因\r\n            // 最可能是对端已经关闭了，统一按照对端已经关闭处理\r\n            error = true;\r\n            break; \r\n        }\r\n\r\n        if (state == STATE_PARSE_URI)\r\n        {\r\n            int flag = this->parse_URI();\r\n            if (flag == PARSE_URI_AGAIN)\r\n                break;\r\n            else if (flag == PARSE_URI_ERROR)\r\n            {\r\n                perror(\"2\");\r\n                error = true;\r\n                handleError(fd, 400, \"Bad Request\");\r\n                break;\r\n            }\r\n            else\r\n                state = STATE_PARSE_HEADERS;\r\n        }\r\n        if (state == STATE_PARSE_HEADERS)\r\n        {\r\n            int flag = this->parse_Headers();\r\n            if (flag == PARSE_HEADER_AGAIN)\r\n                break;\r\n            else if (flag == PARSE_HEADER_ERROR)\r\n            {\r\n                perror(\"3\");\r\n                error = true;\r\n                handleError(fd, 400, \"Bad Request\");\r\n                break;\r\n            }\r\n            if(method == METHOD_POST)\r\n            {\r\n                // POST方法准备\r\n                state = STATE_RECV_BODY;\r\n            }\r\n            else \r\n            {\r\n                state = STATE_ANALYSIS;\r\n            }\r\n        }\r\n        if (state == STATE_RECV_BODY)\r\n        {\r\n            int content_length = -1;\r\n            if (headers.find(\"Content-length\") != headers.end())\r\n            {\r\n                content_length = stoi(headers[\"Content-length\"]);\r\n            }\r\n            else\r\n            {\r\n                error = true;\r\n                handleError(fd, 400, \"Bad Request: Lack of argument (Content-length)\");\r\n                break;\r\n            }\r\n            if (inBuffer.size() < content_length)\r\n                break;\r\n            state = STATE_ANALYSIS;\r\n        }\r\n        if (state == STATE_ANALYSIS)\r\n        {\r\n            int flag = this->analysisRequest();\r\n            if (flag == ANALYSIS_SUCCESS)\r\n            {\r\n                state = STATE_FINISH;\r\n                break;\r\n            }\r\n            else\r\n            {\r\n                error = true;\r\n                break;\r\n            }\r\n        }\r\n    } while (false);\r\n\r\n    if (!error)\r\n    {\r\n        if (outBuffer.size() > 0)\r\n            events |= EPOLLOUT;\r\n        if (state == STATE_FINISH)\r\n        {\r\n            cout << \"keep_alive=\" << keep_alive << endl;\r\n            if (keep_alive)\r\n            {\r\n                this->reset();\r\n                events |= EPOLLIN;\r\n            }\r\n            else\r\n                return;\r\n        }\r\n        else\r\n            events |= EPOLLIN;\r\n    }\r\n}\r\n\r\nvoid RequestData::handleWrite()\r\n{\r\n    if (!error)\r\n    {\r\n        if (writen(fd, outBuffer) < 0)\r\n        {\r\n            perror(\"writen\");\r\n            events = 0;\r\n            error = true;\r\n        }\r\n        else if (outBuffer.size() > 0)\r\n            events |= EPOLLOUT;\r\n    }\r\n}\r\n\r\nvoid RequestData::handleConn()\r\n{\r\n    if (!error)\r\n    {\r\n        if (events != 0)\r\n        {\r\n            // 一定要先加时间信息，否则可能会出现刚加进去，下个in触发来了，然后分离失败后，又加入队列，最后超时被删，然后正在线程中进行的任务出错，double free错误。\r\n            // 新增时间信息\r\n            int timeout = 2000;\r\n            if (keep_alive)\r\n                timeout = 5 * 60 * 1000;\r\n            isAbleRead = false;\r\n            isAbleWrite = false;\r\n            Epoll::add_timer(shared_from_this(), timeout);\r\n            if ((events & EPOLLIN) && (events & EPOLLOUT))\r\n            {\r\n                events = __uint32_t(0);\r\n                events |= EPOLLOUT;\r\n            }\r\n            events |= (EPOLLET | EPOLLONESHOT);\r\n            __uint32_t _events = events;\r\n            events = 0;\r\n            if (Epoll::epoll_mod(fd, shared_from_this(), _events) < 0)\r\n            {\r\n                printf(\"Epoll::epoll_mod error\\n\");\r\n            }\r\n        }\r\n        else if (keep_alive)\r\n        {\r\n            events |= (EPOLLIN | EPOLLET | EPOLLONESHOT);\r\n            int timeout = 5 * 60 * 1000;\r\n            isAbleRead = false;\r\n            isAbleWrite = false;\r\n            Epoll::add_timer(shared_from_this(), timeout);\r\n            __uint32_t _events = events;\r\n            events = 0;\r\n            if (Epoll::epoll_mod(fd, shared_from_this(), _events) < 0)\r\n            {\r\n                printf(\"Epoll::epoll_mod error\\n\");\r\n            }\r\n        }\r\n    }\r\n}\r\n\r\n\r\nint RequestData::parse_URI()\r\n{\r\n    string &str = inBuffer;\r\n    // 读到完整的请求行再开始解析请求\r\n    int pos = str.find('\\r', now_read_pos);\r\n    if (pos < 0)\r\n    {\r\n        return PARSE_URI_AGAIN;\r\n    }\r\n    // 去掉请求行所占的空间，节省空间\r\n    string request_line = str.substr(0, pos);\r\n    if (str.size() > pos + 1)\r\n        str = str.substr(pos + 1);\r\n    else \r\n        str.clear();\r\n    // Method\r\n    pos = request_line.find(\"GET\");\r\n    if (pos < 0)\r\n    {\r\n        pos = request_line.find(\"POST\");\r\n        if (pos < 0)\r\n            return PARSE_URI_ERROR;\r\n        else\r\n            method = METHOD_POST;\r\n    }\r\n    else\r\n        method = METHOD_GET;\r\n    //printf(\"method = %d\\n\", method);\r\n    // filename\r\n    pos = request_line.find(\"/\", pos);\r\n    if (pos < 0)\r\n        return PARSE_URI_ERROR;\r\n    else\r\n    {\r\n        int _pos = request_line.find(' ', pos);\r\n        if (_pos < 0)\r\n            return PARSE_URI_ERROR;\r\n        else\r\n        {\r\n            if (_pos - pos > 1)\r\n            {\r\n                file_name = request_line.substr(pos + 1, _pos - pos - 1);\r\n                int __pos = file_name.find('?');\r\n                if (__pos >= 0)\r\n                {\r\n                    file_name = file_name.substr(0, __pos);\r\n                }\r\n            }\r\n                \r\n            else\r\n                file_name = \"index.html\";\r\n        }\r\n        pos = _pos;\r\n    }\r\n    //cout << \"file_name: \" << file_name << endl;\r\n    // HTTP 版本号\r\n    pos = request_line.find(\"/\", pos);\r\n    if (pos < 0)\r\n        return PARSE_URI_ERROR;\r\n    else\r\n    {\r\n        if (request_line.size() - pos <= 3)\r\n            return PARSE_URI_ERROR;\r\n        else\r\n        {\r\n            string ver = request_line.substr(pos + 1, 3);\r\n            if (ver == \"1.0\")\r\n                HTTPversion = HTTP_10;\r\n            else if (ver == \"1.1\")\r\n                HTTPversion = HTTP_11;\r\n            else\r\n                return PARSE_URI_ERROR;\r\n        }\r\n    }\r\n    return PARSE_URI_SUCCESS;\r\n}\r\n\r\nint RequestData::parse_Headers()\r\n{\r\n    string &str = inBuffer;\r\n    int key_start = -1, key_end = -1, value_start = -1, value_end = -1;\r\n    int now_read_line_begin = 0;\r\n    bool notFinish = true;\r\n    for (int i = 0; i < str.size() && notFinish; ++i)\r\n    {\r\n        switch(h_state)\r\n        {\r\n            case h_start:\r\n            {\r\n                if (str[i] == '\\n' || str[i] == '\\r')\r\n                    break;\r\n                h_state = h_key;\r\n                key_start = i;\r\n                now_read_line_begin = i;\r\n                break;\r\n            }\r\n            case h_key:\r\n            {\r\n                if (str[i] == ':')\r\n                {\r\n                    key_end = i;\r\n                    if (key_end - key_start <= 0)\r\n                        return PARSE_HEADER_ERROR;\r\n                    h_state = h_colon;\r\n                }\r\n                else if (str[i] == '\\n' || str[i] == '\\r')\r\n                    return PARSE_HEADER_ERROR;\r\n                break;  \r\n            }\r\n            case h_colon:\r\n            {\r\n                if (str[i] == ' ')\r\n                {\r\n                    h_state = h_spaces_after_colon;\r\n                }\r\n                else\r\n                    return PARSE_HEADER_ERROR;\r\n                break;  \r\n            }\r\n            case h_spaces_after_colon:\r\n            {\r\n                h_state = h_value;\r\n                value_start = i;\r\n                break;  \r\n            }\r\n            case h_value:\r\n            {\r\n                if (str[i] == '\\r')\r\n                {\r\n                    h_state = h_CR;\r\n                    value_end = i;\r\n                    if (value_end - value_start <= 0)\r\n                        return PARSE_HEADER_ERROR;\r\n                }\r\n                else if (i - value_start > 255)\r\n                    return PARSE_HEADER_ERROR;\r\n                break;  \r\n            }\r\n            case h_CR:\r\n            {\r\n                if (str[i] == '\\n')\r\n                {\r\n                    h_state = h_LF;\r\n                    string key(str.begin() + key_start, str.begin() + key_end);\r\n                    string value(str.begin() + value_start, str.begin() + value_end);\r\n                    headers[key] = value;\r\n                    now_read_line_begin = i;\r\n                }\r\n                else\r\n                    return PARSE_HEADER_ERROR;\r\n                break;  \r\n            }\r\n            case h_LF:\r\n            {\r\n                if (str[i] == '\\r')\r\n                {\r\n                    h_state = h_end_CR;\r\n                }\r\n                else\r\n                {\r\n                    key_start = i;\r\n                    h_state = h_key;\r\n                }\r\n                break;\r\n            }\r\n            case h_end_CR:\r\n            {\r\n                if (str[i] == '\\n')\r\n                {\r\n                    h_state = h_end_LF;\r\n                }\r\n                else\r\n                    return PARSE_HEADER_ERROR;\r\n                break;\r\n            }\r\n            case h_end_LF:\r\n            {\r\n                notFinish = false;\r\n                key_start = i;\r\n                now_read_line_begin = i;\r\n                break;\r\n            }\r\n        }\r\n    }\r\n    if (h_state == h_end_LF)\r\n    {\r\n        str = str.substr(now_read_line_begin);\r\n        return PARSE_HEADER_SUCCESS;\r\n    }\r\n    str = str.substr(now_read_line_begin);\r\n    return PARSE_HEADER_AGAIN;\r\n}\r\n\r\nint RequestData::analysisRequest()\r\n{\r\n    if (method == METHOD_POST)\r\n    {\r\n        //get inBuffer\r\n        string header;\r\n        header += string(\"HTTP/1.1 200 OK\\r\\n\");\r\n        if(headers.find(\"Connection\") != headers.end() && headers[\"Connection\"] == \"keep-alive\")\r\n        {\r\n            //cout << \"headers\" << headers[\"Connection\"] << endl;\r\n            keep_alive = true;\r\n            header += string(\"Connection: keep-alive\\r\\n\") + \"Keep-Alive: timeout=\" + to_string(5 * 60 * 1000) + \"\\r\\n\";\r\n        }\r\n        int length = stoi(headers[\"Content-length\"]);\r\n        vector<char> data(inBuffer.begin(), inBuffer.begin() + length);\r\n        cout << \" data.size()=\" << data.size() << endl;\r\n        Mat src = imdecode(data, CV_LOAD_IMAGE_ANYDEPTH|CV_LOAD_IMAGE_ANYCOLOR);\r\n        imwrite(\"receive.bmp\", src);\r\n        cout << \"1\" << endl;\r\n        Mat res = stitch(src);\r\n        cout << \"2\" << endl;\r\n        vector<uchar> data_encode;\r\n        imencode(\".png\", res, data_encode);\r\n        cout << \"3\" << endl;\r\n        header += string(\"Content-length: \") + to_string(data_encode.size()) + \"\\r\\n\\r\\n\";\r\n        cout << \"4\" << endl;\r\n        outBuffer += header + string(data_encode.begin(), data_encode.end());\r\n        cout << \"5\" << endl;\r\n        inBuffer = inBuffer.substr(length);\r\n        return ANALYSIS_SUCCESS;\r\n    }\r\n    else if (method == METHOD_GET)\r\n    {\r\n        string header;\r\n        header += \"HTTP/1.1 200 OK\\r\\n\";\r\n        if(headers.find(\"Connection\") != headers.end() && headers[\"Connection\"] == \"keep-alive\")\r\n        {\r\n            keep_alive = true;\r\n            header += string(\"Connection: keep-alive\\r\\n\") + \"Keep-Alive: timeout=\" + to_string(5 * 60 * 1000) + \"\\r\\n\";\r\n        }\r\n        int dot_pos = file_name.find('.');\r\n        string filetype;\r\n        if (dot_pos < 0) \r\n            filetype = MimeType::getMime(\"default\");\r\n        else\r\n            filetype = MimeType::getMime(file_name.substr(dot_pos));\r\n        struct stat sbuf;\r\n        if (stat(file_name.c_str(), &sbuf) < 0)\r\n        {\r\n            header.clear();\r\n            handleError(fd, 404, \"Not Found!\");\r\n            return ANALYSIS_ERROR;\r\n        }\r\n        header += \"Content-type: \" + filetype + \"\\r\\n\";\r\n        header += \"Content-length: \" + to_string(sbuf.st_size) + \"\\r\\n\";\r\n        // 头部结束\r\n        header += \"\\r\\n\";\r\n        outBuffer += header;\r\n        // size_t send_len = (size_t)writen(fd, header, strlen(header));\r\n        // if(send_len != strlen(header))\r\n        // {\r\n        //     perror(\"Send header failed\");\r\n        //     return ANALYSIS_ERROR;\r\n        // }\r\n        int src_fd = open(file_name.c_str(), O_RDONLY, 0);\r\n        char *src_addr = static_cast<char*>(mmap(NULL, sbuf.st_size, PROT_READ, MAP_PRIVATE, src_fd, 0));\r\n        close(src_fd);\r\n\r\n        outBuffer += src_addr;\r\n        munmap(src_addr, sbuf.st_size);\r\n        // send_len = writen(fd, src_addr, sbuf.st_size);\r\n        // if(send_len != sbuf.st_size)\r\n        // {\r\n        //     perror(\"Send file failed\");\r\n        //     return ANALYSIS_ERROR;\r\n        // }\r\n        return ANALYSIS_SUCCESS;\r\n    }\r\n    else\r\n        return ANALYSIS_ERROR;\r\n}\r\n\r\nvoid RequestData::handleError(int fd, int err_num, string short_msg)\r\n{\r\n    short_msg = \" \" + short_msg;\r\n    char send_buff[MAX_BUFF];\r\n    string body_buff, header_buff;\r\n    body_buff += \"<html><title>哎~出错了</title>\";\r\n    body_buff += \"<body bgcolor=\\\"ffffff\\\">\";\r\n    body_buff += to_string(err_num) + short_msg;\r\n    body_buff += \"<hr><em> LinYa's Web Server</em>\\n</body></html>\";\r\n\r\n    header_buff += \"HTTP/1.1 \" + to_string(err_num) + short_msg + \"\\r\\n\";\r\n    header_buff += \"Content-type: text/html\\r\\n\";\r\n    header_buff += \"Connection: close\\r\\n\";\r\n    header_buff += \"Content-length: \" + to_string(body_buff.size()) + \"\\r\\n\";\r\n    header_buff += \"\\r\\n\";\r\n    // 错误处理不考虑writen不完的情况\r\n    sprintf(send_buff, \"%s\", header_buff.c_str());\r\n    writen(fd, send_buff, strlen(send_buff));\r\n    sprintf(send_buff, \"%s\", body_buff.c_str());\r\n    writen(fd, send_buff, strlen(send_buff));\r\n}\r\n\r\n\r\nvoid RequestData::disableReadAndWrite()\r\n{\r\n    isAbleRead = false;\r\n    isAbleWrite = false;\r\n}\r\nvoid RequestData::enableRead()\r\n{\r\n    isAbleRead = true;\r\n}\r\nvoid RequestData::enableWrite()\r\n{\r\n    isAbleWrite = true;\r\n}\r\nbool RequestData::canRead()\r\n{\r\n    return isAbleRead;\r\n}\r\nbool RequestData::canWrite()\r\n{\r\n    return isAbleWrite;\r\n}"
  },
  {
    "path": "old_version/old_version_0.5/requestData.h",
    "content": "#pragma once\r\n#include \"timer.h\"\r\n#include <string>\r\n#include <unordered_map>\r\n#include <memory>\r\n#include <sys/epoll.h>\r\n\r\n\r\n#include <opencv/cv.h>\r\n#include <opencv2/core/core.hpp>\r\n#include <opencv2/highgui/highgui.hpp>\r\n#include <opencv2/opencv.hpp>\r\nusing namespace cv;\r\n\r\nconst int STATE_PARSE_URI = 1;\r\nconst int STATE_PARSE_HEADERS = 2;\r\nconst int STATE_RECV_BODY = 3;\r\nconst int STATE_ANALYSIS = 4;\r\nconst int STATE_FINISH = 5;\r\n\r\nconst int MAX_BUFF = 4096;\r\n\r\n// 有请求出现但是读不到数据,可能是Request Aborted,\r\n// 或者来自网络的数据没有达到等原因,\r\n// 对这样的请求尝试超过一定的次数就抛弃\r\nconst int AGAIN_MAX_TIMES = 200;\r\n\r\nconst int PARSE_URI_AGAIN = -1;\r\nconst int PARSE_URI_ERROR = -2;\r\nconst int PARSE_URI_SUCCESS = 0;\r\n\r\nconst int PARSE_HEADER_AGAIN = -1;\r\nconst int PARSE_HEADER_ERROR = -2;\r\nconst int PARSE_HEADER_SUCCESS = 0;\r\n\r\nconst int ANALYSIS_ERROR = -2;\r\nconst int ANALYSIS_SUCCESS = 0;\r\n\r\nconst int METHOD_POST = 1;\r\nconst int METHOD_GET = 2;\r\nconst int HTTP_10 = 1;\r\nconst int HTTP_11 = 2;\r\n\r\nconst int EPOLL_WAIT_TIME = 500;\r\n\r\nclass MimeType\r\n{\r\nprivate:\r\n    static void init();\r\n    static std::unordered_map<std::string, std::string> mime;\r\n    MimeType();\r\n    MimeType(const MimeType &m);\r\n\r\npublic:\r\n    static std::string getMime(const std::string &suffix);\r\n\r\nprivate:\r\n    static pthread_once_t once_control;\r\n};\r\n\r\nenum HeadersState\r\n{\r\n    h_start = 0,\r\n    h_key,\r\n    h_colon,\r\n    h_spaces_after_colon,\r\n    h_value,\r\n    h_CR,\r\n    h_LF,\r\n    h_end_CR,\r\n    h_end_LF\r\n};\r\n\r\nclass TimerNode;\r\n\r\nclass RequestData : public std::enable_shared_from_this<RequestData>\r\n{\r\nprivate:\r\n    std::string path;\r\n    int fd;\r\n    int epollfd;\r\n\r\n    std::string inBuffer;\r\n    std::string outBuffer;\r\n    __uint32_t events;\r\n    bool error;\r\n\r\n    int method;\r\n    int HTTPversion;\r\n    std::string file_name;\r\n    int now_read_pos;\r\n    int state;\r\n    int h_state;\r\n    bool isfinish;\r\n    bool keep_alive;\r\n    std::unordered_map<std::string, std::string> headers;\r\n    std::weak_ptr<TimerNode> timer;\r\n\r\n    bool isAbleRead;\r\n    bool isAbleWrite;\r\n\r\nprivate:\r\n    int parse_URI();\r\n    int parse_Headers();\r\n    int analysisRequest();\r\n\r\n    Mat stitch(Mat &src)\r\n    {\r\n        return src;\r\n    }\r\n\r\npublic:\r\n\r\n    RequestData();\r\n    RequestData(int _epollfd, int _fd, std::string _path);\r\n    ~RequestData();\r\n    void linkTimer(std::shared_ptr<TimerNode> mtimer);\r\n    void reset();\r\n    void seperateTimer();\r\n    int getFd();\r\n    void setFd(int _fd);\r\n    void handleRead();\r\n    void handleWrite();\r\n    void handleError(int fd, int err_num, std::string short_msg);\r\n    void handleConn();\r\n\r\n    void disableReadAndWrite();\r\n\r\n    void enableRead();\r\n\r\n    void enableWrite();\r\n\r\n    bool canRead();\r\n\r\n    bool canWrite();\r\n};\r\n\r\n"
  },
  {
    "path": "old_version/old_version_0.5/threadpool.cpp",
    "content": "#include \"threadpool.h\"\r\n\r\n\r\npthread_mutex_t ThreadPool::lock = PTHREAD_MUTEX_INITIALIZER;\r\npthread_cond_t ThreadPool::notify = PTHREAD_COND_INITIALIZER;\r\nstd::vector<pthread_t> ThreadPool::threads;\r\nstd::vector<ThreadPoolTask> ThreadPool::queue;\r\nint ThreadPool::thread_count = 0;\r\nint ThreadPool::queue_size = 0;\r\nint ThreadPool::head = 0;\r\nint ThreadPool::tail = 0;\r\nint ThreadPool::count = 0;\r\nint ThreadPool::shutdown = 0;\r\nint ThreadPool::started = 0;\r\n\r\nint ThreadPool::threadpool_create(int _thread_count, int _queue_size)\r\n{\r\n    bool err = false;\r\n    do\r\n    {\r\n        if(_thread_count <= 0 || _thread_count > MAX_THREADS || _queue_size <= 0 || _queue_size > MAX_QUEUE) \r\n        {\r\n            _thread_count = 4;\r\n            _queue_size = 1024;\r\n        }\r\n    \r\n        thread_count = 0;\r\n        queue_size = _queue_size;\r\n        head = tail = count = 0;\r\n        shutdown = started = 0;\r\n\r\n        threads.resize(_thread_count);\r\n        queue.resize(_queue_size);\r\n    \r\n        /* Start worker threads */\r\n        for(int i = 0; i < _thread_count; ++i) \r\n        {\r\n            if(pthread_create(&threads[i], NULL, threadpool_thread, (void*)(0)) != 0) \r\n            {\r\n                //threadpool_destroy(pool, 0);\r\n                return -1;\r\n            }\r\n            ++thread_count;\r\n            ++started;\r\n        }\r\n    } while(false);\r\n    \r\n    if (err) \r\n    {\r\n        //threadpool_free(pool);\r\n        return -1;\r\n    }\r\n    return 0;\r\n}\r\n\r\nvoid myHandler(std::shared_ptr<void> req)\r\n{\r\n    std::shared_ptr<RequestData> request = std::static_pointer_cast<RequestData>(req);\r\n    if (request->canWrite())\r\n        request->handleWrite();\r\n    else if (request->canRead())\r\n        request->handleRead();\r\n    request->handleConn();\r\n}\r\n\r\nint ThreadPool::threadpool_add(std::shared_ptr<void> args, std::function<void(std::shared_ptr<void>)> fun)\r\n{\r\n    int next, err = 0;\r\n    if(pthread_mutex_lock(&lock) != 0)\r\n        return THREADPOOL_LOCK_FAILURE;\r\n    do \r\n    {\r\n        next = (tail + 1) % queue_size;\r\n        // 队列满\r\n        if(count == queue_size) \r\n        {\r\n            err = THREADPOOL_QUEUE_FULL;\r\n            break;\r\n        }\r\n        // 已关闭\r\n        if(shutdown)\r\n        {\r\n            err = THREADPOOL_SHUTDOWN;\r\n            break;\r\n        }\r\n        queue[tail].fun = fun;\r\n        queue[tail].args = args;\r\n        tail = next;\r\n        ++count;\r\n        \r\n        /* pthread_cond_broadcast */\r\n        if(pthread_cond_signal(&notify) != 0) \r\n        {\r\n            err = THREADPOOL_LOCK_FAILURE;\r\n            break;\r\n        }\r\n    } while(false);\r\n\r\n    if(pthread_mutex_unlock(&lock) != 0)\r\n        err = THREADPOOL_LOCK_FAILURE;\r\n    return err;\r\n}\r\n\r\n\r\nint ThreadPool::threadpool_destroy(ShutDownOption shutdown_option)\r\n{\r\n    printf(\"Thread pool destroy !\\n\");\r\n    int i, err = 0;\r\n\r\n    if(pthread_mutex_lock(&lock) != 0) \r\n    {\r\n        return THREADPOOL_LOCK_FAILURE;\r\n    }\r\n    do \r\n    {\r\n        if(shutdown) {\r\n            err = THREADPOOL_SHUTDOWN;\r\n            break;\r\n        }\r\n        shutdown = shutdown_option;\r\n\r\n        if((pthread_cond_broadcast(&notify) != 0) ||\r\n           (pthread_mutex_unlock(&lock) != 0)) {\r\n            err = THREADPOOL_LOCK_FAILURE;\r\n            break;\r\n        }\r\n\r\n        for(i = 0; i < thread_count; ++i)\r\n        {\r\n            if(pthread_join(threads[i], NULL) != 0)\r\n            {\r\n                err = THREADPOOL_THREAD_FAILURE;\r\n            }\r\n        }\r\n    } while(false);\r\n\r\n    if(!err) \r\n    {\r\n        threadpool_free();\r\n    }\r\n    return err;\r\n}\r\n\r\nint ThreadPool::threadpool_free()\r\n{\r\n    if(started > 0)\r\n        return -1;\r\n    pthread_mutex_lock(&lock);\r\n    pthread_mutex_destroy(&lock);\r\n    pthread_cond_destroy(&notify);\r\n    return 0;\r\n}\r\n\r\n\r\nvoid *ThreadPool::threadpool_thread(void *args)\r\n{\r\n    while (true)\r\n    {\r\n        ThreadPoolTask task;\r\n        pthread_mutex_lock(&lock);\r\n        while((count == 0) && (!shutdown)) \r\n        {\r\n            pthread_cond_wait(&notify, &lock);\r\n        }\r\n        if((shutdown == immediate_shutdown) ||\r\n           ((shutdown == graceful_shutdown) && (count == 0)))\r\n        {\r\n            break;\r\n        }\r\n        task.fun = queue[head].fun;\r\n        task.args = queue[head].args;\r\n        queue[head].fun = NULL;\r\n        queue[head].args.reset();\r\n        head = (head + 1) % queue_size;\r\n        --count;\r\n        pthread_mutex_unlock(&lock);\r\n        (task.fun)(task.args);\r\n    }\r\n    --started;\r\n    pthread_mutex_unlock(&lock);\r\n    printf(\"This threadpool thread finishs!\\n\");\r\n    pthread_exit(NULL);\r\n    return(NULL);\r\n}"
  },
  {
    "path": "old_version/old_version_0.5/threadpool.h",
    "content": "#pragma once\r\n#include \"requestData.h\"\r\n//#include \"condition.hpp\"\r\n#include <pthread.h>\r\n#include <functional>\r\n#include <memory>\r\n#include <vector>\r\n\r\nconst int THREADPOOL_INVALID = -1;\r\nconst int THREADPOOL_LOCK_FAILURE = -2;\r\nconst int THREADPOOL_QUEUE_FULL = -3;\r\nconst int THREADPOOL_SHUTDOWN = -4;\r\nconst int THREADPOOL_THREAD_FAILURE = -5;\r\nconst int THREADPOOL_GRACEFUL = 1;\r\n\r\nconst int MAX_THREADS = 1024;\r\nconst int MAX_QUEUE = 65535;\r\n\r\ntypedef enum\r\n{\r\n    immediate_shutdown = 1,\r\n    graceful_shutdown  = 2\r\n} ShutDownOption;\r\n\r\nstruct ThreadPoolTask\r\n{\r\n    std::function<void(std::shared_ptr<void>)> fun;\r\n    std::shared_ptr<void> args;\r\n};\r\n\r\nvoid myHandler(std::shared_ptr<void> req);\r\n\r\nclass ThreadPool\r\n{\r\nprivate:\r\n    static pthread_mutex_t lock;\r\n    static pthread_cond_t notify;\r\n\r\n    static std::vector<pthread_t> threads;\r\n    static std::vector<ThreadPoolTask> queue;\r\n    static int thread_count;\r\n    static int queue_size;\r\n    static int head;\r\n    // tail 指向尾节点的下一节点\r\n    static int tail;\r\n    static int count;\r\n    static int shutdown;\r\n    static int started;\r\npublic:\r\n    static int threadpool_create(int _thread_count, int _queue_size);\r\n    static int threadpool_add(std::shared_ptr<void> args, std::function<void(std::shared_ptr<void>)> fun = myHandler);\r\n    static int threadpool_destroy(ShutDownOption shutdown_option = graceful_shutdown);\r\n    static int threadpool_free();\r\n    static void *threadpool_thread(void *args);\r\n};\r\n"
  },
  {
    "path": "old_version/old_version_0.5/timer.cpp",
    "content": "#include \"timer.h\"\r\n#include \"epoll.h\"\r\n#include <unordered_map>\r\n#include <string>\r\n#include <sys/time.h>\r\n#include <unistd.h>\r\n#include <deque>\r\n#include <queue>\r\n\r\n#include <iostream>\r\nusing namespace std;\r\n\r\n\r\nTimerNode::TimerNode(SP_ReqData _request_data, int timeout): \r\n    deleted(false), \r\n    request_data(_request_data)\r\n{\r\n    //cout << \"TimerNode()\" << endl;\r\n    struct timeval now;\r\n    gettimeofday(&now, NULL);\r\n    // 以毫秒计\r\n    expired_time = ((now.tv_sec * 1000) + (now.tv_usec / 1000)) + timeout;\r\n}\r\n\r\nTimerNode::~TimerNode()\r\n{\r\n    //cout << \"~TimerNode()\" << endl;\r\n    if (request_data)\r\n    {\r\n        Epoll::epoll_del(request_data->getFd());\r\n    }\r\n    //request_data.reset();\r\n    // if (request_data)\r\n    // {\r\n    //     cout << \"request_data=\" << request_data << endl;\r\n    //     delete request_data;\r\n    //     request_data = NULL;\r\n    // }\r\n}\r\n\r\nvoid TimerNode::update(int timeout)\r\n{\r\n    struct timeval now;\r\n    gettimeofday(&now, NULL);\r\n    expired_time = ((now.tv_sec * 1000) + (now.tv_usec / 1000)) + timeout;\r\n}\r\n\r\nbool TimerNode::isvalid()\r\n{\r\n    struct timeval now;\r\n    gettimeofday(&now, NULL);\r\n    size_t temp = ((now.tv_sec * 1000) + (now.tv_usec / 1000));\r\n    if (temp < expired_time)\r\n    {\r\n        return true;\r\n    }\r\n    else\r\n    {\r\n        this->setDeleted();\r\n        return false;\r\n    }\r\n}\r\n\r\nvoid TimerNode::clearReq()\r\n{\r\n    request_data.reset();\r\n    this->setDeleted();\r\n}\r\n\r\nvoid TimerNode::setDeleted()\r\n{\r\n    deleted = true;\r\n}\r\n\r\nbool TimerNode::isDeleted() const\r\n{\r\n    return deleted;\r\n}\r\n\r\nsize_t TimerNode::getExpTime() const\r\n{\r\n    return expired_time;\r\n}\r\n\r\nTimerManager::TimerManager()\r\n{\r\n}\r\n\r\nTimerManager::~TimerManager()\r\n{\r\n}\r\n\r\nvoid TimerManager::addTimer(SP_ReqData request_data, int timeout)\r\n{\r\n    SP_TimerNode new_node(new TimerNode(request_data, timeout));\r\n    {\r\n        MutexLockGuard locker(lock);\r\n        TimerNodeQueue.push(new_node);\r\n    }\r\n    request_data->linkTimer(new_node);\r\n}\r\n\r\nvoid TimerManager::addTimer(SP_TimerNode timer_node)\r\n{\r\n\r\n}\r\n\r\n/* 处理逻辑是这样的~\r\n因为(1) 优先队列不支持随机访问\r\n(2) 即使支持，随机删除某节点后破坏了堆的结构，需要重新更新堆结构。\r\n所以对于被置为deleted的时间节点，会延迟到它(1)超时 或 (2)它前面的节点都被删除时，它才会被删除。\r\n一个点被置为deleted,它最迟会在TIMER_TIME_OUT时间后被删除。\r\n这样做有两个好处：\r\n(1) 第一个好处是不需要遍历优先队列，省时。\r\n(2) 第二个好处是给超时时间一个容忍的时间，就是设定的超时时间是删除的下限(并不是一到超时时间就立即删除)，如果监听的请求在超时后的下一次请求中又一次出现了，\r\n就不用再重新申请RequestData节点了，这样可以继续重复利用前面的RequestData，减少了一次delete和一次new的时间。\r\n*/\r\n\r\nvoid TimerManager::handle_expired_event()\r\n{\r\n    MutexLockGuard locker(lock);\r\n    while (!TimerNodeQueue.empty())\r\n    {\r\n        SP_TimerNode ptimer_now = TimerNodeQueue.top();\r\n        if (ptimer_now->isDeleted())\r\n        {\r\n            TimerNodeQueue.pop();\r\n            //delete ptimer_now;\r\n        }\r\n        else if (ptimer_now->isvalid() == false)\r\n        {\r\n            TimerNodeQueue.pop();\r\n            //delete ptimer_now;\r\n        }\r\n        else\r\n        {\r\n            break;\r\n        }\r\n    }\r\n}"
  },
  {
    "path": "old_version/old_version_0.5/timer.h",
    "content": "#pragma once\r\n#include \"requestData.h\"\r\n#include \"./base/nocopyable.hpp\"\r\n#include \"./base/mutexLock.hpp\"\r\n#include <unistd.h>\r\n#include <memory>\r\n#include <queue>\r\n#include <deque>\r\n\r\nclass RequestData;\r\n\r\nclass TimerNode\r\n{\r\n    typedef std::shared_ptr<RequestData> SP_ReqData;\r\nprivate:\r\n    bool deleted;\r\n    size_t expired_time;\r\n    SP_ReqData request_data;\r\npublic:\r\n    TimerNode(SP_ReqData _request_data, int timeout);\r\n    ~TimerNode();\r\n    void update(int timeout);\r\n    bool isvalid();\r\n    void clearReq();\r\n    void setDeleted();\r\n    bool isDeleted() const;\r\n    size_t getExpTime() const;\r\n};\r\n\r\nstruct timerCmp\r\n{\r\n    bool operator()(std::shared_ptr<TimerNode> &a, std::shared_ptr<TimerNode> &b) const\r\n    {\r\n        return a->getExpTime() > b->getExpTime();\r\n    }\r\n};\r\n\r\nclass TimerManager\r\n{\r\n    typedef std::shared_ptr<RequestData> SP_ReqData;\r\n    typedef std::shared_ptr<TimerNode> SP_TimerNode;\r\nprivate:\r\n    std::priority_queue<SP_TimerNode, std::deque<SP_TimerNode>, timerCmp> TimerNodeQueue;\r\n    MutexLock lock;\r\npublic:\r\n    TimerManager();\r\n    ~TimerManager();\r\n    void addTimer(SP_ReqData request_data, int timeout);\r\n    void addTimer(SP_TimerNode timer_node);\r\n    void handle_expired_event();\r\n};"
  },
  {
    "path": "old_version/old_version_0.5/util.cpp",
    "content": "#include \"util.h\"\r\n#include <unistd.h>\r\n#include <fcntl.h>\r\n#include <signal.h>\r\n#include <errno.h>\r\n#include <string.h>\r\n\r\nconst int MAX_BUFF = 4096;\r\nssize_t readn(int fd, void *buff, size_t n)\r\n{\r\n    size_t nleft = n;\r\n    ssize_t nread = 0;\r\n    ssize_t readSum = 0;\r\n    char *ptr = (char*)buff;\r\n    while (nleft > 0)\r\n    {\r\n        if ((nread = read(fd, ptr, nleft)) < 0)\r\n        {\r\n            if (errno == EINTR)\r\n                nread = 0;\r\n            else if (errno == EAGAIN)\r\n            {\r\n                return readSum;\r\n            }\r\n            else\r\n            {\r\n                return -1;\r\n            }  \r\n        }\r\n        else if (nread == 0)\r\n            break;\r\n        readSum += nread;\r\n        nleft -= nread;\r\n        ptr += nread;\r\n    }\r\n    return readSum;\r\n}\r\n\r\nssize_t readn(int fd, std::string &inBuffer)\r\n{\r\n    ssize_t nread = 0;\r\n    ssize_t readSum = 0;\r\n    while (true)\r\n    {\r\n        char buff[MAX_BUFF];\r\n        if ((nread = read(fd, buff, MAX_BUFF)) < 0)\r\n        {\r\n            if (errno == EINTR)\r\n                continue;\r\n            else if (errno == EAGAIN)\r\n            {\r\n                \r\n                return readSum;\r\n            }  \r\n            else\r\n            {\r\n                perror(\"read error\");\r\n                return -1;\r\n            }\r\n        }\r\n        else if (nread == 0)\r\n            break;\r\n        //printf(\"before inBuffer.size() = %d\\n\", inBuffer.size());\r\n        //printf(\"nread = %d\\n\", nread);\r\n        readSum += nread;\r\n        //buff += nread;\r\n        inBuffer += std::string(buff, buff + nread);\r\n        //printf(\"after inBuffer.size() = %d\\n\", inBuffer.size());\r\n    }\r\n    return readSum;\r\n}\r\n\r\nssize_t writen(int fd, void *buff, size_t n)\r\n{\r\n    size_t nleft = n;\r\n    ssize_t nwritten = 0;\r\n    ssize_t writeSum = 0;\r\n    char *ptr = (char*)buff;\r\n    while (nleft > 0)\r\n    {\r\n        if ((nwritten = write(fd, ptr, nleft)) <= 0)\r\n        {\r\n            if (nwritten < 0)\r\n            {\r\n                if (errno == EINTR)\r\n                {\r\n                    nwritten = 0;\r\n                    continue;\r\n                }\r\n                else if (errno == EAGAIN)\r\n                {\r\n                    return writeSum;\r\n                }\r\n                else\r\n                    return -1;\r\n            }\r\n        }\r\n        writeSum += nwritten;\r\n        nleft -= nwritten;\r\n        ptr += nwritten;\r\n    }\r\n    return writeSum;\r\n}\r\n\r\nssize_t writen(int fd, std::string &sbuff)\r\n{\r\n    size_t nleft = sbuff.size();\r\n    ssize_t nwritten = 0;\r\n    ssize_t writeSum = 0;\r\n    const char *ptr = sbuff.c_str();\r\n    while (nleft > 0)\r\n    {\r\n        if ((nwritten = write(fd, ptr, nleft)) <= 0)\r\n        {\r\n            if (nwritten < 0)\r\n            {\r\n                if (errno == EINTR)\r\n                {\r\n                    nwritten = 0;\r\n                    continue;\r\n                }\r\n                else if (errno == EAGAIN)\r\n                    break;\r\n                else\r\n                    return -1;\r\n            }\r\n        }\r\n        writeSum += nwritten;\r\n        nleft -= nwritten;\r\n        ptr += nwritten;\r\n    }\r\n    if (writeSum == sbuff.size())\r\n        sbuff.clear();\r\n    else\r\n        sbuff = sbuff.substr(writeSum);\r\n    return writeSum;\r\n}\r\n\r\nvoid handle_for_sigpipe()\r\n{\r\n    struct sigaction sa;\r\n    memset(&sa, '\\0', sizeof(sa));\r\n    sa.sa_handler = SIG_IGN;\r\n    sa.sa_flags = 0;\r\n    if(sigaction(SIGPIPE, &sa, NULL))\r\n        return;\r\n}\r\n\r\nint setSocketNonBlocking(int fd)\r\n{\r\n    int flag = fcntl(fd, F_GETFL, 0);\r\n    if(flag == -1)\r\n        return -1;\r\n\r\n    flag |= O_NONBLOCK;\r\n    if(fcntl(fd, F_SETFL, flag) == -1)\r\n        return -1;\r\n    return 0;\r\n}"
  },
  {
    "path": "old_version/old_version_0.5/util.h",
    "content": "#pragma once\r\n#include <cstdlib>\r\n#include <string>\r\n\r\nssize_t readn(int fd, void *buff, size_t n);\r\nssize_t readn(int fd, std::string &inBuffer);\r\nssize_t writen(int fd, void *buff, size_t n);\r\nssize_t writen(int fd, std::string &sbuff);\r\nvoid handle_for_sigpipe();\r\nint setSocketNonBlocking(int fd);"
  },
  {
    "path": "old_version/old_version_0.6/AsyncLogging.cpp",
    "content": "#include \"AsyncLogging.h\"\r\n#include \"LogFile.h\"\r\n#include <stdio.h>\r\n#include <assert.h>\r\n\r\nusing namespace std;\r\n\r\nAsyncLogging::AsyncLogging(const string basename,\r\n                           int flushInterval)\r\n  : flushInterval_(flushInterval),\r\n    running_(false),\r\n    basename_(basename),\r\n    thread_(std::bind(&AsyncLogging::threadFunc, this), \"Logging\"),\r\n    mutex_(),\r\n    cond_(mutex_),\r\n    currentBuffer_(new Buffer),\r\n    nextBuffer_(new Buffer),\r\n    buffers_(),\r\n    latch_(1)\r\n{\r\n  currentBuffer_->bzero();\r\n  nextBuffer_->bzero();\r\n  buffers_.reserve(16);\r\n}\r\n\r\nvoid AsyncLogging::append(const char* logline, int len)\r\n{\r\n  MutexLockGuard lock(mutex_);\r\n  if (currentBuffer_->avail() > len)\r\n  {\r\n    currentBuffer_->append(logline, len);\r\n  }\r\n  else\r\n  {\r\n    buffers_.push_back(currentBuffer_);\r\n    currentBuffer_.reset();\r\n\r\n    if (nextBuffer_)\r\n    {\r\n      currentBuffer_ = std::move(nextBuffer_);\r\n    }\r\n    else\r\n    {\r\n      currentBuffer_.reset(new Buffer); // Rarely happens\r\n    }\r\n    currentBuffer_->append(logline, len);\r\n    cond_.notify();\r\n  }\r\n}\r\n\r\n#include <unistd.h>\r\nvoid AsyncLogging::threadFunc()\r\n{\r\n  assert(running_ == true);\r\n  latch_.countDown();\r\n  LogFile output(basename_);\r\n  BufferPtr newBuffer1(new Buffer);\r\n  BufferPtr newBuffer2(new Buffer);\r\n  newBuffer1->bzero();\r\n  newBuffer2->bzero();\r\n  BufferVector buffersToWrite;\r\n  buffersToWrite.reserve(16);\r\n  while (running_)\r\n  {\r\n    assert(newBuffer1 && newBuffer1->length() == 0);\r\n    assert(newBuffer2 && newBuffer2->length() == 0);\r\n    assert(buffersToWrite.empty());\r\n\r\n    {\r\n      MutexLockGuard lock(mutex_);\r\n      if (buffers_.empty())  // unusual usage!\r\n      {\r\n        cond_.waitForSeconds(flushInterval_);\r\n      }\r\n      buffers_.push_back(currentBuffer_);\r\n      currentBuffer_.reset();\r\n\r\n      currentBuffer_ = std::move(newBuffer1);\r\n      buffersToWrite.swap(buffers_);\r\n      if (!nextBuffer_)\r\n      {\r\n        nextBuffer_ = std::move(newBuffer2);\r\n      }\r\n    }\r\n\r\n    assert(!buffersToWrite.empty());\r\n\r\n    if (buffersToWrite.size() > 25)\r\n    {\r\n      //char buf[256];\r\n      // snprintf(buf, sizeof buf, \"Dropped log messages at %s, %zd larger buffers\\n\",\r\n      //          Timestamp::now().toFormattedString().c_str(),\r\n      //          buffersToWrite.size()-2);\r\n      //fputs(buf, stderr);\r\n      //output.append(buf, static_cast<int>(strlen(buf)));\r\n      buffersToWrite.erase(buffersToWrite.begin()+2, buffersToWrite.end());\r\n    }\r\n\r\n    for (size_t i = 0; i < buffersToWrite.size(); ++i)\r\n    {\r\n      // FIXME: use unbuffered stdio FILE ? or use ::writev ?\r\n      output.append(buffersToWrite[i]->data(), buffersToWrite[i]->length());\r\n    }\r\n\r\n    if (buffersToWrite.size() > 2)\r\n    {\r\n      // drop non-bzero-ed buffers, avoid trashing\r\n      buffersToWrite.resize(2);\r\n    }\r\n\r\n    if (!newBuffer1)\r\n    {\r\n      assert(!buffersToWrite.empty());\r\n      newBuffer1 = buffersToWrite.back();\r\n      buffersToWrite.pop_back();\r\n      newBuffer1->reset();\r\n    }\r\n\r\n    if (!newBuffer2)\r\n    {\r\n      assert(!buffersToWrite.empty());\r\n      newBuffer2 = buffersToWrite.back();\r\n      buffersToWrite.pop_back();\r\n      newBuffer2->reset();\r\n    }\r\n\r\n    buffersToWrite.clear();\r\n    output.flush();\r\n  }\r\n  output.flush();\r\n}"
  },
  {
    "path": "old_version/old_version_0.6/AsyncLogging.h",
    "content": "#pragma once\r\n\r\n#include \"CountDownLatch.h\"\r\n#include \"MutexLock.h\"\r\n#include \"Thread.h\"\r\n#include \"LogStream.h\"\r\n#include \"noncopyable.h\"\r\n#include <functional>\r\n#include <string>\r\n#include <vector>\r\n\r\n// extern const int kSmallBuffer;\r\n// extern const int kLargeBuffer;\r\n\r\n// template class FixedBuffer<kLargeBuffer>;\r\n// template class FixedBuffer<kSmallBuffer>;\r\n\r\nclass AsyncLogging : noncopyable\r\n{\r\n public:\r\n\r\n  AsyncLogging(const std::string basename, int flushInterval = 2);\r\n\r\n  ~AsyncLogging()\r\n  {\r\n    if (running_)\r\n    {\r\n      stop();\r\n    }\r\n  }\r\n\r\n  void append(const char* logline, int len);\r\n\r\n  void start()\r\n  {\r\n    running_ = true;\r\n    thread_.start();\r\n    latch_.wait();\r\n  }\r\n\r\n  void stop()\r\n  {\r\n    running_ = false;\r\n    cond_.notify();\r\n    thread_.join();\r\n  }\r\n\r\n private:\r\n\r\n  // declare but not define, prevent compiler-synthesized functions\r\n  AsyncLogging(const AsyncLogging&);  // ptr_container\r\n  void operator=(const AsyncLogging&);  // ptr_container\r\n\r\n  void threadFunc();\r\n\r\n  typedef FixedBuffer<kLargeBuffer> Buffer;\r\n  typedef std::vector<std::shared_ptr<Buffer>> BufferVector;\r\n  typedef std::shared_ptr<Buffer> BufferPtr;\r\n\r\n  const int flushInterval_;\r\n  bool running_;\r\n  std::string basename_;\r\n  Thread thread_;\r\n  MutexLock mutex_;\r\n  Condition cond_;\r\n  BufferPtr currentBuffer_;\r\n  BufferPtr nextBuffer_;\r\n  BufferVector buffers_;\r\n  CountDownLatch latch_;\r\n};"
  },
  {
    "path": "old_version/old_version_0.6/Condition.h",
    "content": "#pragma once\r\n#include \"noncopyable.h\"\r\n#include \"MutexLock.h\"\r\n#include <pthread.h>\r\n#include <pthread.h>\r\n#include <errno.h>\r\n#include <cstdint>\r\n#include <time.h>\r\n\r\nclass Condition: noncopyable\r\n{\r\npublic:\r\n    explicit Condition(MutexLock &_mutex):\r\n        mutex(_mutex)\r\n    {\r\n        pthread_cond_init(&cond, NULL);\r\n    }\r\n    ~Condition()\r\n    {\r\n        pthread_cond_destroy(&cond);\r\n    }\r\n    void wait()\r\n    {\r\n        pthread_cond_wait(&cond, mutex.get());\r\n    }\r\n    void notify()\r\n    {\r\n        pthread_cond_signal(&cond);\r\n    }\r\n    void notifyAll()\r\n    {\r\n        pthread_cond_broadcast(&cond);\r\n    }\r\n    bool waitForSeconds(int seconds)\r\n    {\r\n        struct timespec abstime;\r\n        clock_gettime(CLOCK_REALTIME, &abstime);\r\n        abstime.tv_sec += static_cast<time_t>(seconds);\r\n        return ETIMEDOUT == pthread_cond_timedwait(&cond, mutex.get(), &abstime);\r\n    }\r\nprivate:\r\n    MutexLock &mutex;\r\n    pthread_cond_t cond;\r\n};"
  },
  {
    "path": "old_version/old_version_0.6/CountDownLatch.cpp",
    "content": "#include \"CountDownLatch.h\"\r\n\r\n\r\nCountDownLatch::CountDownLatch(int count)\r\n  : mutex_(),\r\n    condition_(mutex_),\r\n    count_(count)\r\n{\r\n}\r\n\r\nvoid CountDownLatch::wait()\r\n{\r\n    MutexLockGuard lock(mutex_);\r\n    while (count_ > 0)\r\n    {\r\n        condition_.wait();\r\n    }\r\n}\r\n\r\nvoid CountDownLatch::countDown()\r\n{\r\n    MutexLockGuard lock(mutex_);\r\n    --count_;\r\n    if (count_ == 0)\r\n    {\r\n        condition_.notifyAll();\r\n    }\r\n}\r\n\r\nint CountDownLatch::getCount() const\r\n{\r\n    MutexLockGuard lock(mutex_);\r\n    return count_;\r\n}"
  },
  {
    "path": "old_version/old_version_0.6/CountDownLatch.h",
    "content": "#pragma once\r\n\r\n#include \"Condition.h\"\r\n#include \"MutexLock.h\"\r\n\r\n#include \"noncopyable.h\"\r\n\r\nclass CountDownLatch : noncopyable\r\n{\r\npublic:\r\n    explicit CountDownLatch(int count);\r\n    void wait();\r\n    void countDown();\r\n    int getCount() const;\r\n\r\nprivate:\r\n    mutable MutexLock mutex_;\r\n    Condition condition_;\r\n    int count_;\r\n};"
  },
  {
    "path": "old_version/old_version_0.6/CurrentThread.hpp",
    "content": "#pragma once\r\n#include <stdint.h>\r\n\r\n/*\r\nnamespace CurrendThread\r\n{\r\n    // internal\r\n    extern __thread int t_cachedTid;\r\n    extern __thread char t_tidString[32];\r\n    extern __thread int t_tidStringLength;\r\n    extern __thread const char* t_threadName;\r\n    void cacheTid();\r\n    inline int tid()\r\n    {\r\n        if (__builtin_expect(t_cachedTid == 0, 0))\r\n        {\r\n            cacheTid();\r\n        }\r\n        return t_cachedTid;\r\n    }\r\n\r\n    inline const char* tidString() // for logging\r\n    {\r\n        return t_tidString;\r\n    }\r\n\r\n    inline int tidStringLength() // for logging\r\n    {\r\n        return t_tidStringLength;\r\n    }\r\n\r\n    inline const char* name()\r\n    {\r\n        return t_threadName;\r\n    }\r\n}\r\n*/\r\n"
  },
  {
    "path": "old_version/old_version_0.6/FileUtil.cpp",
    "content": "#include \"FileUtil.h\"\r\n#include <assert.h>\r\n#include <errno.h>\r\n#include <fcntl.h>\r\n#include <stdio.h>\r\n#include <sys/stat.h>\r\n#include <unistd.h>\r\n\r\nusing namespace std;\r\n\r\nAppendFile::AppendFile(string filename)\r\n  : fp_(fopen(filename.c_str(), \"ae\"))\r\n{\r\n  setbuffer(fp_, buffer_, sizeof buffer_);\r\n}\r\n\r\nAppendFile::~AppendFile()\r\n{\r\n  fclose(fp_);\r\n}\r\n\r\nvoid AppendFile::append(const char* logline, const size_t len)\r\n{\r\n  size_t n = write(logline, len);\r\n  size_t remain = len - n;\r\n  while (remain > 0)\r\n  {\r\n    size_t x = write(logline + n, remain);\r\n    if (x == 0)\r\n    {\r\n      int err = ferror(fp_);\r\n      if (err)\r\n      {\r\n        fprintf(stderr, \"AppendFile::append() failed !\\n\");\r\n      }\r\n      break;\r\n    }\r\n    n += x;\r\n    remain = len - n;\r\n  }\r\n}\r\n\r\nvoid AppendFile::flush()\r\n{\r\n  fflush(fp_);\r\n}\r\n\r\nsize_t AppendFile::write(const char* logline, size_t len)\r\n{\r\n  // #undef fwrite_unlocked\r\n  return ::fwrite_unlocked(logline, 1, len, fp_);\r\n}"
  },
  {
    "path": "old_version/old_version_0.6/FileUtil.h",
    "content": "#pragma once\r\n\r\n#include \"noncopyable.h\"\r\n#include <string>\r\n\r\nclass AppendFile : noncopyable\r\n{\r\npublic:\r\n    explicit AppendFile(std::string filename);\r\n    ~AppendFile();\r\n    void append(const char* logline, const size_t len);\r\n    void flush();\r\n\r\nprivate:\r\n    size_t write(const char* logline, size_t len);\r\n    FILE* fp_;\r\n    char buffer_[64*1024];\r\n};"
  },
  {
    "path": "old_version/old_version_0.6/LogFile.cpp",
    "content": "#include \"LogFile.h\"\r\n\r\n#include \"FileUtil.h\"\r\n\r\n#include <assert.h>\r\n#include <stdio.h>\r\n#include <time.h>\r\n\r\nusing namespace std;\r\n// \"/logfile.log\"\r\nLogFile::LogFile(const string& basename, int flushEveryN)\r\n  : basename_(basename),\r\n    flushEveryN_(flushEveryN),\r\n    count_(0),\r\n    mutex_(new MutexLock)\r\n{\r\n    //assert(basename.find('/') >= 0);\r\n    file_.reset(new AppendFile(basename));\r\n}\r\n\r\nLogFile::~LogFile()\r\n{\r\n}\r\n\r\nvoid LogFile::append(const char* logline, int len)\r\n{\r\n    MutexLockGuard lock(*mutex_);\r\n    append_unlocked(logline, len);\r\n}\r\n\r\nvoid LogFile::flush()\r\n{\r\n    MutexLockGuard lock(*mutex_);\r\n    file_->flush();\r\n}\r\n\r\nvoid LogFile::append_unlocked(const char* logline, int len)\r\n{\r\n    file_->append(logline, len);\r\n    ++count_;\r\n    if (count_ >= flushEveryN_)\r\n    {\r\n        count_ = 0;\r\n        file_->flush();\r\n    }\r\n}"
  },
  {
    "path": "old_version/old_version_0.6/LogFile.h",
    "content": "#pragma once\r\n#include \"FileUtil.h\"\r\n#include \"MutexLock.h\"\r\n#include \"noncopyable.h\"\r\n#include <memory>\r\n#include <string>\r\n\r\nclass LogFile : noncopyable\r\n{\r\npublic:\r\n    LogFile(const std::string& basename, int flushEveryN = 1024);\r\n    ~LogFile();\r\n\r\n    void append(const char* logline, int len);\r\n    void flush();\r\n    bool rollFile();\r\n\r\n private:\r\n  void append_unlocked(const char* logline, int len);\r\n\r\n  const std::string basename_;\r\n  const int flushEveryN_;\r\n\r\n  int count_;\r\n  std::unique_ptr<MutexLock> mutex_;\r\n  std::unique_ptr<AppendFile> file_;\r\n};"
  },
  {
    "path": "old_version/old_version_0.6/LogStream.cpp",
    "content": "#include \"LogStream.h\"\r\n\r\n#include <algorithm>\r\n#include <limits>\r\n#include <assert.h>\r\n#include <string.h>\r\n#include <stdint.h>\r\n#include <stdio.h>\r\n\r\n\r\nconst char digits[] = \"9876543210123456789\";\r\nconst char* zero = digits + 9;\r\n\r\nconst char digitsHex[] = \"0123456789ABCDEF\";\r\n\r\n// Efficient Integer to String Conversions, by Matthew Wilson.\r\ntemplate<typename T>\r\nsize_t convert(char buf[], T value)\r\n{\r\n  T i = value;\r\n  char* p = buf;\r\n\r\n  do\r\n  {\r\n    int lsd = static_cast<int>(i % 10);\r\n    i /= 10;\r\n    *p++ = zero[lsd];\r\n  } while (i != 0);\r\n\r\n  if (value < 0)\r\n  {\r\n    *p++ = '-';\r\n  }\r\n  *p = '\\0';\r\n  std::reverse(buf, p);\r\n\r\n  return p - buf;\r\n}\r\n\r\n\r\ntemplate class FixedBuffer<kSmallBuffer>;\r\ntemplate class FixedBuffer<kLargeBuffer>;\r\n\r\n\r\ntemplate<typename T>\r\nvoid LogStream::formatInteger(T v)\r\n{\r\n  if (buffer_.avail() >= kMaxNumericSize)\r\n  {\r\n    size_t len = convert(buffer_.current(), v);\r\n    buffer_.add(len);\r\n  }\r\n}\r\n\r\nLogStream& LogStream::operator<<(short v)\r\n{\r\n  *this << static_cast<int>(v);\r\n  return *this;\r\n}\r\n\r\nLogStream& LogStream::operator<<(unsigned short v)\r\n{\r\n  *this << static_cast<unsigned int>(v);\r\n  return *this;\r\n}\r\n\r\nLogStream& LogStream::operator<<(int v)\r\n{\r\n  formatInteger(v);\r\n  return *this;\r\n}\r\n\r\nLogStream& LogStream::operator<<(unsigned int v)\r\n{\r\n  formatInteger(v);\r\n  return *this;\r\n}\r\n\r\nLogStream& LogStream::operator<<(long v)\r\n{\r\n  formatInteger(v);\r\n  return *this;\r\n}\r\n\r\nLogStream& LogStream::operator<<(unsigned long v)\r\n{\r\n  formatInteger(v);\r\n  return *this;\r\n}\r\n\r\nLogStream& LogStream::operator<<(long long v)\r\n{\r\n  formatInteger(v);\r\n  return *this;\r\n}\r\n\r\nLogStream& LogStream::operator<<(unsigned long long v)\r\n{\r\n  formatInteger(v);\r\n  return *this;\r\n}\r\n\r\n// FIXME: replace this with Grisu3 by Florian Loitsch.\r\nLogStream& LogStream::operator<<(double v)\r\n{\r\n  if (buffer_.avail() >= kMaxNumericSize)\r\n  {\r\n    int len = snprintf(buffer_.current(), kMaxNumericSize, \"%.12g\", v);\r\n    buffer_.add(len);\r\n  }\r\n  return *this;\r\n}"
  },
  {
    "path": "old_version/old_version_0.6/LogStream.h",
    "content": "#pragma once\r\n#include <assert.h>\r\n#include <string.h>\r\n#include <string>\r\n#include \"noncopyable.h\"\r\n//#include \"AsyncLogging.h\"\r\n\r\nclass AsyncLogging;\r\nconst int kSmallBuffer = 4000;\r\nconst int kLargeBuffer = 4000*1000;\r\n\r\ntemplate<int SIZE>\r\nclass FixedBuffer :noncopyable\r\n{\r\n public:\r\n  FixedBuffer()\r\n    : cur_(data_)\r\n  {\r\n  }\r\n\r\n  ~FixedBuffer()\r\n  {\r\n  }\r\n\r\n  void append(const char* buf, size_t len)\r\n  {\r\n    if (avail() > len)\r\n    {\r\n      memcpy(cur_, buf, len);\r\n      cur_ += len;\r\n    }\r\n  }\r\n\r\n  const char* data() const { return data_; }\r\n  int length() const { return static_cast<int>(cur_ - data_); }\r\n\r\n  // write to data_ directly\r\n  char* current() { return cur_; }\r\n  int avail() const { return static_cast<int>(end() - cur_); }\r\n  void add(size_t len) { cur_ += len; }\r\n\r\n  void reset() { cur_ = data_; }\r\n  void bzero() { memset(data_, 0, sizeof data_); }\r\n\r\n\r\n private:\r\n  const char* end() const { return data_ + sizeof data_; }\r\n  // Must be outline function for cookies.\r\n\r\n  char data_[SIZE];\r\n  char* cur_;\r\n};\r\n\r\n\r\n\r\nclass LogStream : noncopyable\r\n{\r\n  typedef LogStream self;\r\n public:\r\n  typedef FixedBuffer<kSmallBuffer> Buffer;\r\n\r\n  self& operator<<(bool v)\r\n  {\r\n    buffer_.append(v ? \"1\" : \"0\", 1);\r\n    return *this;\r\n  }\r\n\r\n  self& operator<<(short);\r\n  self& operator<<(unsigned short);\r\n  self& operator<<(int);\r\n  self& operator<<(unsigned int);\r\n  self& operator<<(long);\r\n  self& operator<<(unsigned long);\r\n  self& operator<<(long long);\r\n  self& operator<<(unsigned long long);\r\n\r\n  self& operator<<(const void*);\r\n\r\n  self& operator<<(float v)\r\n  {\r\n    *this << static_cast<double>(v);\r\n    return *this;\r\n  }\r\n  self& operator<<(double);\r\n  // self& operator<<(long double);\r\n\r\n  self& operator<<(char v)\r\n  {\r\n    buffer_.append(&v, 1);\r\n    return *this;\r\n  }\r\n\r\n  self& operator<<(const char* str)\r\n  {\r\n    if (str)\r\n    {\r\n      buffer_.append(str, strlen(str));\r\n    }\r\n    else\r\n    {\r\n      buffer_.append(\"(null)\", 6);\r\n    }\r\n    return *this;\r\n  }\r\n\r\n  self& operator<<(const unsigned char* str)\r\n  {\r\n    return operator<<(reinterpret_cast<const char*>(str));\r\n  }\r\n\r\n  self& operator<<(const std::string& v)\r\n  {\r\n    buffer_.append(v.c_str(), v.size());\r\n    return *this;\r\n  }\r\n\r\n  void append(const char* data, int len) { buffer_.append(data, len); }\r\n  const Buffer& buffer() const { return buffer_; }\r\n  void resetBuffer() { buffer_.reset(); }\r\n\r\n private:\r\n  void staticCheck();\r\n\r\n  template<typename T>\r\n  void formatInteger(T);\r\n\r\n  Buffer buffer_;\r\n\r\n  static const int kMaxNumericSize = 32;\r\n};"
  },
  {
    "path": "old_version/old_version_0.6/Logging.cpp",
    "content": "#include \"Logging.h\"\r\n#include \"CurrentThread.hpp\"\r\n#include \"Thread.h\"\r\n#include <assert.h>\r\n\r\n#include \"AsyncLogging.h\"\r\n\r\n#include <iostream>\r\nusing namespace std;\r\n\r\n\r\nstatic pthread_once_t once_control_ = PTHREAD_ONCE_INIT;\r\nstatic AsyncLogging *AsyncLogger_;\r\n\r\nvoid once_init()\r\n{\r\n    AsyncLogger_ = new AsyncLogging(std::string(\"/linya_web_server.log\"));\r\n    AsyncLogger_->start();\r\n}\r\n\r\nvoid output(const char* msg, int len)\r\n{\r\n    pthread_once(&once_control_, once_init);\r\n    AsyncLogger_->append(msg, len);\r\n}\r\n\r\nLogger::Impl::Impl(const char *fileName, int line)\r\n  : stream_(),\r\n    basename_(fileName),\r\n    line_(line)\r\n{\r\n  //formatTime();\r\n  //CurrentThread::tid();\r\n  //stream_ << T(CurrentThread::tidString(), CurrentThread::tidStringLength());\r\n}\r\n\r\nvoid Logger::Impl::formatTime()\r\n{\r\n  // int64_t microSecondsSinceEpoch = time_.microSecondsSinceEpoch();\r\n  // time_t seconds = static_cast<time_t>(microSecondsSinceEpoch / Timestamp::kMicroSecondsPerSecond);\r\n  // int microseconds = static_cast<int>(microSecondsSinceEpoch % Timestamp::kMicroSecondsPerSecond);\r\n  // if (seconds != t_lastSecond)\r\n  // {\r\n  //   t_lastSecond = seconds;\r\n  //   struct tm tm_time;\r\n  //   if (g_logTimeZone.valid())\r\n  //   {\r\n  //     tm_time = g_logTimeZone.toLocalTime(seconds);\r\n  //   }\r\n  //   else\r\n  //   {\r\n  //     ::gmtime_r(&seconds, &tm_time); // FIXME TimeZone::fromUtcTime\r\n  //   }\r\n\r\n  //   int len = snprintf(t_time, sizeof(t_time), \"%4d%02d%02d %02d:%02d:%02d\",\r\n  //       tm_time.tm_year + 1900, tm_time.tm_mon + 1, tm_time.tm_mday,\r\n  //       tm_time.tm_hour, tm_time.tm_min, tm_time.tm_sec);\r\n  //   assert(len == 17); (void)len;\r\n  // }\r\n\r\n  // if (g_logTimeZone.valid())\r\n  // {\r\n  //   Fmt us(\".%06d \", microseconds);\r\n  //   assert(us.length() == 8);\r\n  //   stream_ << T(t_time, 17) << T(us.data(), 8);\r\n  // }\r\n  // else\r\n  // {\r\n  //   Fmt us(\".%06dZ \", microseconds);\r\n  //   assert(us.length() == 9);\r\n  //   stream_ << T(t_time, 17) << T(us.data(), 9);\r\n  // }\r\n}\r\n\r\nLogger::Logger(const char *fileName, int line)\r\n  : impl_(fileName, line)\r\n{\r\n}\r\n\r\nLogger::~Logger()\r\n{\r\n  impl_.stream_ << \" - \" << impl_.basename_ << ':' << impl_.line_ << '\\n';\r\n  const LogStream::Buffer& buf(stream().buffer());\r\n  output(buf.data(), buf.length());\r\n}"
  },
  {
    "path": "old_version/old_version_0.6/Logging.h",
    "content": "#pragma once\r\n#include \"LogStream.h\"\r\n#include <pthread.h>\r\n#include <string.h>\r\n#include <string>\r\n#include <stdio.h>\r\n\r\nclass AsyncLogging;\r\n\r\nclass Logger\r\n{\r\npublic:\r\n    ~Logger();\r\n    Logger(const char *fileName, int line);\r\n    LogStream& stream() { return impl_.stream_; }\r\n\r\n private:\r\n    class Impl\r\n    {\r\n    public:\r\n        Impl(const char *fileName, int line);\r\n        void formatTime();\r\n\r\n        LogStream stream_;\r\n        int line_;\r\n        std::string basename_;\r\n    };\r\n    Impl impl_;\r\n};\r\n\r\n#define LOG Logger(__FILE__, __LINE__).stream()"
  },
  {
    "path": "old_version/old_version_0.6/Makefile",
    "content": "SOURCE  := $(wildcard *.cpp)\r\nOBJS    := $(patsubst %.c,%.o,$(patsubst %.cpp,%.o,$(SOURCE)))\r\n\r\nTARGET  := myserver\r\nCC      := g++\r\nLIBS    := -lpthread -lopencv_core -lopencv_imgproc -lopencv_highgui\r\nINCLUDE:= -I./usr/local/include/opencv\r\nCFLAGS  := -std=c++11 -g -Wall -O0 $(INCLUDE) -D_PTHREADS\r\nCXXFLAGS:= $(CFLAGS)\r\n\r\n.PHONY : objs clean veryclean rebuild all\r\nall : $(TARGET)\r\nobjs : $(OBJS)\r\nrebuild: veryclean all\r\nclean :\r\n\trm -fr *.o\r\nveryclean : clean\r\n\trm -rf $(TARGET)\r\n\r\n$(TARGET) : $(OBJS)\r\n\t$(CC) $(CXXFLAGS) -o $@ $(OBJS) $(LDFLAGS) $(LIBS)"
  },
  {
    "path": "old_version/old_version_0.6/Makefile1",
    "content": "cc=g++ -std=c++11\r\nmain: main.o RequestData.o epoll.o threadpool.o\r\n\t$(cc) -o main main.o RequestData.o epoll.o threadpool.o -lpthread -lopencv_core -lopencv_imgproc -lopencv_highgui -lopencv_ml \r\nmain.o: main.cpp\r\n\t$(cc) -I/usr/local/include/opencv -c main.cpp\r\nRequestData.o: requestData.h RequestData.cpp\r\n\t$(cc) -I/usr/local/include/opencv -c RequestData.cpp\r\nepoll.o: epoll.h epoll.cpp\r\n\t$(cc) -I/usr/local/include/opencv -c epoll.cpp\r\nthreadpool.o: threadpool.h threadpool.cpp\r\n\t$(cc) -I/usr/local/include/opencv -c threadpool.cpp\r\n.PHONY: clean\r\nclean:\r\n\t-rm main *.o\r\n\r\n\r\n\r\n\r\n.PHONY : everything objs clean veryclean rebuild\r\neverything :\r\n\t$(TARGET)\r\nall :\r\n\t$(TARGET)\r\nobjs :\r\n\t$(OBJS)\r\nrebuild:\r\n\tveryclean everything \r\nclean :\r\n\trm -fr *.o\r\nveryclean :\r\n\tclean rm -fr $(TARGET)"
  },
  {
    "path": "old_version/old_version_0.6/MutexLock.h",
    "content": "#pragma once\r\n#include \"noncopyable.h\"\r\n#include <pthread.h>\r\n#include <cstdio>\r\n\r\nclass MutexLock: noncopyable\r\n{\r\npublic:\r\n    MutexLock()\r\n    {\r\n        pthread_mutex_init(&mutex, NULL);\r\n    }\r\n    ~MutexLock()\r\n    {\r\n        pthread_mutex_lock(&mutex);\r\n        pthread_mutex_destroy(&mutex);\r\n    }\r\n    void lock()\r\n    {\r\n        pthread_mutex_lock(&mutex);\r\n    }\r\n    void unlock()\r\n    {\r\n        pthread_mutex_unlock(&mutex);\r\n    }\r\n    pthread_mutex_t *get()\r\n    {\r\n        return &mutex;\r\n    }\r\nprivate:\r\n    pthread_mutex_t mutex;\r\n\r\n// 友元类不受访问权限影响\r\nprivate:\r\n    friend class Condition;\r\n};\r\n\r\n\r\nclass MutexLockGuard: noncopyable\r\n{\r\npublic:\r\n    explicit MutexLockGuard(MutexLock &_mutex):\r\n    mutex(_mutex)\r\n    {\r\n        mutex.lock();\r\n    }\r\n    ~MutexLockGuard()\r\n    {\r\n        mutex.unlock();\r\n    }\r\nprivate:\r\n    MutexLock &mutex;\r\n};"
  },
  {
    "path": "old_version/old_version_0.6/Thread.cpp",
    "content": "#include \"Thread.h\"\r\n#include \"CurrentThread.hpp\"\r\n//#include \"Logging.h\"\r\n#include <memory>\r\n#include <errno.h>\r\n#include <stdio.h>\r\n#include <unistd.h>\r\n#include <sys/prctl.h>\r\n#include <sys/syscall.h>\r\n#include <sys/types.h>\r\n#include <linux/unistd.h>\r\n#include <stdint.h>\r\n#include <assert.h>\r\n\r\n#include <iostream>\r\nusing namespace std;\r\n\r\nnamespace CurrentThread\r\n{\r\n    // internal\r\n    // extern __thread int t_cachedTid;\r\n    // extern __thread char t_tidString[32];\r\n    // extern __thread int t_tidStringLength;\r\n    // extern __thread const char* t_threadName;\r\n    __thread int t_cachedTid;\r\n    __thread char t_tidString[32];\r\n    __thread int t_tidStringLength;\r\n    __thread const char* t_threadName;\r\n\r\n    void cacheTid();\r\n    inline int tid()\r\n    {\r\n        if (__builtin_expect(t_cachedTid == 0, 0))\r\n        {\r\n            cacheTid();\r\n        }\r\n        return t_cachedTid;\r\n    }\r\n\r\n    inline const char* tidString() // for logging\r\n    {\r\n        return t_tidString;\r\n    }\r\n\r\n    inline int tidStringLength() // for logging\r\n    {\r\n        return t_tidStringLength;\r\n    }\r\n\r\n    inline const char* name()\r\n    {\r\n        return t_threadName;\r\n    }\r\n}\r\n\r\npid_t gettid()\r\n{\r\n    return static_cast<pid_t>(::syscall(SYS_gettid));\r\n}\r\n\r\n// 为了在线程中保留name,tid这些数据\r\nstruct ThreadData\r\n{\r\n  typedef Thread::ThreadFunc ThreadFunc;\r\n  ThreadFunc func_;\r\n  string name_;\r\n  pid_t* tid_;\r\n  CountDownLatch* latch_;\r\n\r\n  ThreadData(const ThreadFunc& func,\r\n             const string& name,\r\n             pid_t* tid,\r\n             CountDownLatch* latch)\r\n    : func_(func),\r\n      name_(name),\r\n      tid_(tid),\r\n      latch_(latch)\r\n  { }\r\n\r\n  void runInThread()\r\n  {\r\n    *tid_ = CurrentThread::tid();\r\n    tid_ = NULL;\r\n    latch_->countDown();\r\n    latch_ = NULL;\r\n\r\n    CurrentThread::t_threadName = name_.empty() ? \"Thread\" : name_.c_str();\r\n    prctl(PR_SET_NAME, CurrentThread::t_threadName);\r\n\r\n    func_();\r\n    CurrentThread::t_threadName = \"finished\";\r\n  }\r\n};\r\n\r\nvoid* startThread(void* obj)\r\n{\r\n  ThreadData* data = static_cast<ThreadData*>(obj);\r\n  data->runInThread();\r\n  delete data;\r\n  return NULL;\r\n}\r\n\r\n\r\nvoid CurrentThread::cacheTid()\r\n{\r\n  if (t_cachedTid == 0)\r\n  {\r\n    t_cachedTid = gettid();\r\n    t_tidStringLength = snprintf(t_tidString, sizeof t_tidString, \"%5d \", t_cachedTid);\r\n  }\r\n}\r\n\r\nThread::Thread(const ThreadFunc& func, const string& n)\r\n  : started_(false),\r\n    joined_(false),\r\n    pthreadId_(0),\r\n    tid_(0),\r\n    func_(func),\r\n    name_(n),\r\n    latch_(1)\r\n{\r\n  setDefaultName();\r\n}\r\n\r\nThread::~Thread()\r\n{\r\n  if (started_ && !joined_)\r\n  {\r\n    pthread_detach(pthreadId_);\r\n  }\r\n}\r\n\r\nvoid Thread::setDefaultName()\r\n{\r\n  if (name_.empty())\r\n  {\r\n    char buf[32];\r\n    snprintf(buf, sizeof buf, \"Thread\");\r\n    name_ = buf;\r\n  }\r\n}\r\n\r\nvoid Thread::start()\r\n{\r\n  assert(!started_);\r\n  started_ = true;\r\n  // FIXME: move(func_)\r\n  ThreadData* data = new ThreadData(func_, name_, &tid_, &latch_);\r\n  if (pthread_create(&pthreadId_, NULL, &startThread, data))\r\n  {\r\n    started_ = false;\r\n    delete data;\r\n  }\r\n  else\r\n  {\r\n    latch_.wait();\r\n    assert(tid_ > 0);\r\n  }\r\n}\r\n\r\nint Thread::join()\r\n{\r\n    assert(started_);\r\n    assert(!joined_);\r\n    joined_ = true;\r\n    return pthread_join(pthreadId_, NULL);\r\n}"
  },
  {
    "path": "old_version/old_version_0.6/Thread.h",
    "content": "#pragma once\r\n\r\n#include \"CountDownLatch.h\"\r\n#include <functional>\r\n#include \"noncopyable.h\"\r\n#include <memory>\r\n#include <pthread.h>\r\n#include <string>\r\n\r\nclass Thread : noncopyable\r\n{\r\n public:\r\n  typedef std::function<void ()> ThreadFunc;\r\n\r\n  explicit Thread(const ThreadFunc&, const std::string& name = std::string());\r\n  ~Thread();\r\n\r\n  void start();\r\n  int join();\r\n\r\n  bool started() const { return started_; }\r\n  pid_t tid() const { return tid_; }\r\n  const std::string& name() const { return name_; }\r\n\r\n\r\n private:\r\n  void setDefaultName();\r\n\r\n  bool       started_;\r\n  bool       joined_;\r\n  pthread_t  pthreadId_;\r\n  pid_t      tid_;\r\n  ThreadFunc func_;\r\n  std::string     name_;\r\n  CountDownLatch latch_;\r\n};"
  },
  {
    "path": "old_version/old_version_0.6/config.h",
    "content": "#pragma once\r\n\r\n"
  },
  {
    "path": "old_version/old_version_0.6/epoll.cpp",
    "content": "#include \"epoll.h\"\r\n#include \"threadpool.h\"\r\n#include \"util.h\"\r\n#include <sys/epoll.h>\r\n#include <errno.h>\r\n#include <sys/socket.h>\r\n#include <netinet/in.h>\r\n#include <string.h>\r\n#include <queue>\r\n#include <deque>\r\n\r\n#include <iostream>\r\nusing namespace std;\r\n\r\nint TIMER_TIME_OUT = 500;\r\n\r\nepoll_event *Epoll::events;\r\nEpoll::SP_ReqData Epoll::fd2req[MAXFDS];\r\nint Epoll::epoll_fd = 0;\r\nconst std::string Epoll::PATH = \"/\";\r\n\r\nTimerManager Epoll::timer_manager;\r\n\r\n\r\nint Epoll::epoll_init(int maxevents, int listen_num)\r\n{\r\n    epoll_fd = epoll_create(listen_num + 1);\r\n    if(epoll_fd == -1)\r\n        return -1;\r\n    //events.reset(new epoll_event[maxevents], [](epoll_event *data){delete [] data;});\r\n    events = new epoll_event[maxevents];\r\n    return 0;\r\n}\r\n\r\n// 注册新描述符\r\nint Epoll::epoll_add(int fd, SP_ReqData request, __uint32_t events)\r\n{\r\n    struct epoll_event event;\r\n    event.data.fd = fd;\r\n    event.events = events;\r\n    fd2req[fd] = request;\r\n    if(epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &event) < 0)\r\n    {\r\n        perror(\"epoll_add error\");\r\n        return -1;\r\n    }\r\n    return 0;\r\n}\r\n\r\n// 修改描述符状态\r\nint Epoll::epoll_mod(int fd, SP_ReqData request, __uint32_t events)\r\n{\r\n    \r\n    struct epoll_event event;\r\n    event.data.fd = fd;\r\n    event.events = events;\r\n    fd2req[fd] = request;\r\n    if(epoll_ctl(epoll_fd, EPOLL_CTL_MOD, fd, &event) < 0)\r\n    {\r\n        perror(\"epoll_mod error\");\r\n        fd2req[fd].reset();\r\n        return -1;\r\n    }\r\n    return 0;\r\n}\r\n\r\n// 从epoll中删除描述符\r\nint Epoll::epoll_del(int fd, __uint32_t events)\r\n{\r\n    struct epoll_event event;\r\n    event.data.fd = fd;\r\n    event.events = events;\r\n    if(epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fd, &event) < 0)\r\n    {\r\n        perror(\"epoll_del error\");\r\n        return -1;\r\n    }\r\n    fd2req[fd].reset();\r\n    return 0;\r\n}\r\n\r\n// 返回活跃事件数\r\nvoid Epoll::my_epoll_wait(int listen_fd, int max_events, int timeout)\r\n{\r\n    int event_count = epoll_wait(epoll_fd, events, max_events, timeout);\r\n    if (event_count < 0)\r\n        perror(\"epoll wait error\");\r\n    std::vector<SP_ReqData> req_data = getEventsRequest(listen_fd, event_count, PATH);\r\n    if (req_data.size() > 0)\r\n    {\r\n        for (auto &req: req_data)\r\n        {\r\n            if (ThreadPool::threadpool_add(req) < 0)\r\n            {\r\n                // 线程池满了或者关闭了等原因，抛弃本次监听到的请求。\r\n                break;\r\n            }\r\n        }\r\n    }\r\n    timer_manager.handle_expired_event();\r\n}\r\n#include <iostream>\r\n#include <arpa/inet.h>\r\nusing namespace std;\r\nvoid Epoll::acceptConnection(int listen_fd, int epoll_fd, const std::string path)\r\n{\r\n    struct sockaddr_in client_addr;\r\n    memset(&client_addr, 0, sizeof(struct sockaddr_in));\r\n    //socklen_t client_addr_len = 0;\r\n    socklen_t client_addr_len = sizeof(client_addr);\r\n    int accept_fd = 0;\r\n    while((accept_fd = accept(listen_fd, (struct sockaddr*)&client_addr, &client_addr_len)) > 0)\r\n    {\r\n        cout << inet_ntoa(client_addr.sin_addr) << endl;\r\n        cout << ntohs(client_addr.sin_port) << endl;\r\n        /*\r\n        // TCP的保活机制默认是关闭的\r\n        int optval = 0;\r\n        socklen_t len_optval = 4;\r\n        getsockopt(accept_fd, SOL_SOCKET,  SO_KEEPALIVE, &optval, &len_optval);\r\n        cout << \"optval ==\" << optval << endl;\r\n        */\r\n        // 限制服务器的最大并发连接数\r\n        if (accept_fd >= MAXFDS)\r\n        {\r\n            close(accept_fd);\r\n            continue;\r\n        }\r\n\r\n        // 设为非阻塞模式\r\n        int ret = setSocketNonBlocking(accept_fd);\r\n        if (ret < 0)\r\n        {\r\n            perror(\"Set non block failed!\");\r\n            return;\r\n        }\r\n\r\n        SP_ReqData req_info(new RequestData(epoll_fd, accept_fd, path));\r\n\r\n        // 文件描述符可以读，边缘触发(Edge Triggered)模式，保证一个socket连接在任一时刻只被一个线程处理\r\n        __uint32_t _epo_event = EPOLLIN | EPOLLET | EPOLLONESHOT;\r\n        Epoll::epoll_add(accept_fd, req_info, _epo_event);\r\n        // 新增时间信息\r\n        timer_manager.addTimer(req_info, TIMER_TIME_OUT);\r\n    }\r\n    //if(accept_fd == -1)\r\n     //   perror(\"accept\");\r\n}\r\n\r\n// 分发处理函数\r\nstd::vector<std::shared_ptr<RequestData>> Epoll::getEventsRequest(int listen_fd, int events_num, const std::string path)\r\n{\r\n    std::vector<SP_ReqData> req_data;\r\n    for(int i = 0; i < events_num; ++i)\r\n    {\r\n        // 获取有事件产生的描述符\r\n        int fd = events[i].data.fd;\r\n\r\n        // 有事件发生的描述符为监听描述符\r\n        if(fd == listen_fd)\r\n        {\r\n            //cout << \"This is listen_fd\" << endl;\r\n            acceptConnection(listen_fd, epoll_fd, path);\r\n        }\r\n        else if (fd < 3)\r\n        {\r\n            printf(\"fd < 3\\n\");\r\n            break;\r\n        }\r\n        else\r\n        {\r\n            // 排除错误事件\r\n            if ((events[i].events & EPOLLERR) || (events[i].events & EPOLLHUP))\r\n            {\r\n                printf(\"error event\\n\");\r\n                if (fd2req[fd])\r\n                    fd2req[fd]->seperateTimer();\r\n                fd2req[fd].reset();\r\n                continue;\r\n            }\r\n            // 将请求任务加入到线程池中\r\n            // 加入线程池之前将Timer和request分离\r\n\r\n            SP_ReqData cur_req = fd2req[fd];\r\n            \r\n            if (cur_req)\r\n            {\r\n                // 重新组织 仿照 void Channel::handleEventWithGuard\r\n                if ((events[i].events & EPOLLIN) || (events[i].events & EPOLLPRI))\r\n                    cur_req->enableRead();\r\n                if (events[i].events & EPOLLOUT)\r\n                    cur_req->enableWrite();\r\n                //printf(\"cur_req.use_count=%d\\n\", cur_req.use_count());\r\n                \r\n                cur_req->seperateTimer();\r\n                req_data.push_back(cur_req);\r\n                //cout << \"-getEventsRequest fd==\" << fd << endl;\r\n                fd2req[fd].reset();\r\n            }\r\n            else\r\n            {\r\n                cout << \"SP cur_req is invalid\" << endl;\r\n            }\r\n        }\r\n    }\r\n    return req_data;\r\n}\r\n\r\nvoid Epoll::add_timer(shared_ptr<RequestData> request_data, int timeout)\r\n{\r\n    timer_manager.addTimer(request_data, timeout);\r\n}"
  },
  {
    "path": "old_version/old_version_0.6/epoll.h",
    "content": "#pragma once\r\n#include \"requestData.h\"\r\n#include \"timer.h\"\r\n#include <vector>\r\n#include <unordered_map>\r\n#include <sys/epoll.h>\r\n#include <memory>\r\n\r\nclass Epoll\r\n{\r\npublic:\r\n    typedef std::shared_ptr<RequestData> SP_ReqData;\r\nprivate:\r\n    static const int MAXFDS = 1000;\r\n    static epoll_event *events;\r\n    static SP_ReqData fd2req[MAXFDS];\r\n    static int epoll_fd;\r\n    static const std::string PATH;\r\n\r\n    static TimerManager timer_manager;\r\npublic:\r\n    static int epoll_init(int maxevents, int listen_num);\r\n    static int epoll_add(int fd, SP_ReqData request, __uint32_t events);\r\n    static int epoll_mod(int fd, SP_ReqData request, __uint32_t events);\r\n    static int epoll_del(int fd, __uint32_t events = (EPOLLIN | EPOLLET | EPOLLONESHOT));\r\n    static void my_epoll_wait(int listen_fd, int max_events, int timeout);\r\n    static void acceptConnection(int listen_fd, int epoll_fd, const std::string path);\r\n    static std::vector<SP_ReqData> getEventsRequest(int listen_fd, int events_num, const std::string path);\r\n\r\n    static void add_timer(SP_ReqData request_data, int timeout);\r\n};"
  },
  {
    "path": "old_version/old_version_0.6/index.html",
    "content": "Hello World !"
  },
  {
    "path": "old_version/old_version_0.6/main.cpp",
    "content": "#include \"requestData.h\"\r\n#include \"epoll.h\"\r\n#include \"threadpool.h\"\r\n#include \"util.h\"\r\n#include \"config.h\"\r\n#include <sys/epoll.h>\r\n#include <queue>\r\n#include <sys/time.h>\r\n#include <sys/socket.h>\r\n#include <netinet/in.h>\r\n#include <stdio.h>\r\n#include <string.h>\r\n#include <cstdlib>\r\n#include <iostream>\r\n#include <vector>\r\n#include <unistd.h>\r\n#include <memory>\r\n#include \"Logging.h\"\r\n\r\nusing namespace std;\r\n\r\nstatic const int MAXEVENTS = 5000;\r\nstatic const int LISTENQ = 1024;\r\nconst int THREADPOOL_THREAD_NUM = 4;\r\nconst int QUEUE_SIZE = 65535;\r\n\r\nconst int PORT = 8888;\r\nconst int ASK_STATIC_FILE = 1;\r\nconst int ASK_IMAGE_STITCH = 2;\r\n\r\nconst int TIMER_TIME_OUT = 500;\r\n\r\nvoid acceptConnection(int listen_fd, int epoll_fd, const string &path);\r\n\r\nint socket_bind_listen(int port)\r\n{\r\n    // 检查port值，取正确区间范围\r\n    if (port < 1024 || port > 65535)\r\n        return -1;\r\n\r\n    // 创建socket(IPv4 + TCP)，返回监听描述符\r\n    int listen_fd = 0;\r\n    if((listen_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1)\r\n        return -1;\r\n\r\n    // 消除bind时\"Address already in use\"错误\r\n    int optval = 1;\r\n    if(setsockopt(listen_fd, SOL_SOCKET,  SO_REUSEADDR, &optval, sizeof(optval)) == -1)\r\n        return -1;\r\n\r\n    // 设置服务器IP和Port，和监听描述副绑定\r\n    struct sockaddr_in server_addr;\r\n    bzero((char*)&server_addr, sizeof(server_addr));\r\n    server_addr.sin_family = AF_INET;\r\n    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);\r\n    server_addr.sin_port = htons((unsigned short)port);\r\n    if(bind(listen_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1)\r\n        return -1;\r\n\r\n    // 开始监听，最大等待队列长为LISTENQ\r\n    if(listen(listen_fd, LISTENQ) == -1)\r\n        return -1;\r\n\r\n    // 无效监听描述符\r\n    if(listen_fd == -1)\r\n    {\r\n        close(listen_fd);\r\n        return -1;\r\n    }\r\n    return listen_fd;\r\n}\r\n\r\n\r\nint main()\r\n{\r\n    LOG << \"yingyingying\";\r\n    LOG << 654 << 3.2 << 0 << string(\"fg\") << true;\r\n    #ifndef _PTHREADS\r\n        LOG << \"_PTHREADS is not defined !\";\r\n    #endif\r\n    handle_for_sigpipe();\r\n    if (Epoll::epoll_init(MAXEVENTS, LISTENQ) < 0)\r\n    {\r\n        perror(\"epoll init failed\");\r\n        return 1;\r\n    }\r\n    if (ThreadPool::threadpool_create(THREADPOOL_THREAD_NUM, QUEUE_SIZE) < 0)\r\n    {\r\n        printf(\"Threadpool create failed\\n\");\r\n        return 1;\r\n    }\r\n    int listen_fd = socket_bind_listen(PORT);\r\n    if (listen_fd < 0) \r\n    {\r\n        perror(\"socket bind failed\");\r\n        return 1;\r\n    }\r\n    if (setSocketNonBlocking(listen_fd) < 0)\r\n    {\r\n        perror(\"set socket non block failed\");\r\n        return 1;\r\n    }\r\n    shared_ptr<RequestData> request(new RequestData());\r\n    request->setFd(listen_fd);\r\n    if (Epoll::epoll_add(listen_fd, request, EPOLLIN | EPOLLET) < 0)\r\n    {\r\n        perror(\"epoll add error\");\r\n        return 1;\r\n    }\r\n    \r\n    while (true)\r\n    {\r\n        //sleep(10);\r\n        Epoll::my_epoll_wait(listen_fd, MAXEVENTS, -1);\r\n        \r\n        //ThreadPool::threadpool_destroy();\r\n        //break;\r\n    }\r\n    return 0;\r\n}"
  },
  {
    "path": "old_version/old_version_0.6/noncopyable.h",
    "content": "#pragma once\r\nclass noncopyable\r\n{\r\nprotected:\r\n    noncopyable() {}\r\n    ~noncopyable() {}\r\nprivate:\r\n    noncopyable(const noncopyable&);\r\n    const noncopyable& operator=(const noncopyable&);\r\n};"
  },
  {
    "path": "old_version/old_version_0.6/requestData.cpp",
    "content": "#include \"requestData.h\"\r\n#include \"util.h\"\r\n#include \"epoll.h\"\r\n#include <unistd.h>\r\n#include <fcntl.h>\r\n#include <sys/stat.h>\r\n#include <sys/mman.h>\r\n#include <queue>\r\n#include <cstdlib>\r\n#include <opencv/cv.h>\r\n#include <opencv2/core/core.hpp>\r\n#include <opencv2/highgui/highgui.hpp>\r\n#include <opencv2/opencv.hpp>\r\nusing namespace cv;\r\n\r\n//test\r\n#include <iostream>\r\nusing namespace std;\r\n\r\npthread_once_t MimeType::once_control = PTHREAD_ONCE_INIT;\r\nstd::unordered_map<std::string, std::string> MimeType::mime;\r\n\r\n\r\nvoid MimeType::init()\r\n{\r\n    mime[\".html\"] = \"text/html\";\r\n    mime[\".avi\"] = \"video/x-msvideo\";\r\n    mime[\".bmp\"] = \"image/bmp\";\r\n    mime[\".c\"] = \"text/plain\";\r\n    mime[\".doc\"] = \"application/msword\";\r\n    mime[\".gif\"] = \"image/gif\";\r\n    mime[\".gz\"] = \"application/x-gzip\";\r\n    mime[\".htm\"] = \"text/html\";\r\n    mime[\".ico\"] = \"application/x-ico\";\r\n    mime[\".jpg\"] = \"image/jpeg\";\r\n    mime[\".png\"] = \"image/png\";\r\n    mime[\".txt\"] = \"text/plain\";\r\n    mime[\".mp3\"] = \"audio/mp3\";\r\n    mime[\"default\"] = \"text/html\";\r\n}\r\n\r\nstd::string MimeType::getMime(const std::string &suffix)\r\n{\r\n    pthread_once(&once_control, MimeType::init);\r\n    if (mime.find(suffix) == mime.end())\r\n        return mime[\"default\"];\r\n    else\r\n        return mime[suffix];\r\n}\r\n\r\nRequestData::RequestData(): \r\n    now_read_pos(0), \r\n    state(STATE_PARSE_URI), \r\n    h_state(h_start), \r\n    keep_alive(false), \r\n    isAbleRead(true),\r\n    isAbleWrite(false),\r\n    events(0),\r\n    error(false)\r\n{\r\n    cout << \"RequestData()\" << endl;\r\n}\r\n\r\nRequestData::RequestData(int _epollfd, int _fd, std::string _path):\r\n    now_read_pos(0), \r\n    state(STATE_PARSE_URI), \r\n    h_state(h_start), \r\n    keep_alive(false), \r\n    path(_path), \r\n    fd(_fd), \r\n    epollfd(_epollfd),\r\n    isAbleRead(true),\r\n    isAbleWrite(false),\r\n    events(0),\r\n    error(false)\r\n{\r\n    cout << \"RequestData()\" << endl;\r\n}\r\n\r\nRequestData::~RequestData()\r\n{\r\n    cout << \"~RequestData()\" << endl;\r\n    close(fd);\r\n}\r\n\r\nvoid RequestData::linkTimer(shared_ptr<TimerNode> mtimer)\r\n{\r\n    // shared_ptr重载了bool, 但weak_ptr没有\r\n    //if (!timer.lock())\r\n    timer = mtimer;\r\n}\r\n\r\nint RequestData::getFd()\r\n{\r\n    return fd;\r\n}\r\nvoid RequestData::setFd(int _fd)\r\n{\r\n    fd = _fd;\r\n}\r\n\r\nvoid RequestData::reset()\r\n{\r\n    inBuffer.clear();\r\n    file_name.clear();\r\n    path.clear();\r\n    now_read_pos = 0;\r\n    state = STATE_PARSE_URI;\r\n    h_state = h_start;\r\n    headers.clear();\r\n    //keep_alive = false;\r\n    if (timer.lock())\r\n    {\r\n        shared_ptr<TimerNode> my_timer(timer.lock());\r\n        my_timer->clearReq();\r\n        timer.reset();\r\n    }\r\n}\r\n\r\nvoid RequestData::seperateTimer()\r\n{\r\n    //cout << \"seperateTimer\" << endl;\r\n    if (timer.lock())\r\n    {\r\n        shared_ptr<TimerNode> my_timer(timer.lock());\r\n        my_timer->clearReq();\r\n        timer.reset();\r\n    }\r\n}\r\n\r\nvoid RequestData::handleRead()\r\n{\r\n    do\r\n    {\r\n        int read_num = readn(fd, inBuffer);\r\n        //printf(\"read_num=%d\\n\", read_num);\r\n        if (read_num < 0)\r\n        {\r\n            perror(\"1\");\r\n            error = true;\r\n            handleError(fd, 400, \"Bad Request\");\r\n            break;\r\n        }\r\n        else if (read_num == 0)\r\n        {\r\n            // 有请求出现但是读不到数据，可能是Request Aborted，或者来自网络的数据没有达到等原因\r\n            // 最可能是对端已经关闭了，统一按照对端已经关闭处理\r\n            error = true;\r\n            break; \r\n        }\r\n\r\n        if (state == STATE_PARSE_URI)\r\n        {\r\n            int flag = this->parse_URI();\r\n            if (flag == PARSE_URI_AGAIN)\r\n                break;\r\n            else if (flag == PARSE_URI_ERROR)\r\n            {\r\n                perror(\"2\");\r\n                error = true;\r\n                handleError(fd, 400, \"Bad Request\");\r\n                break;\r\n            }\r\n            else\r\n                state = STATE_PARSE_HEADERS;\r\n        }\r\n        if (state == STATE_PARSE_HEADERS)\r\n        {\r\n            int flag = this->parse_Headers();\r\n            if (flag == PARSE_HEADER_AGAIN)\r\n                break;\r\n            else if (flag == PARSE_HEADER_ERROR)\r\n            {\r\n                perror(\"3\");\r\n                error = true;\r\n                handleError(fd, 400, \"Bad Request\");\r\n                break;\r\n            }\r\n            if(method == METHOD_POST)\r\n            {\r\n                // POST方法准备\r\n                state = STATE_RECV_BODY;\r\n            }\r\n            else \r\n            {\r\n                state = STATE_ANALYSIS;\r\n            }\r\n        }\r\n        if (state == STATE_RECV_BODY)\r\n        {\r\n            int content_length = -1;\r\n            if (headers.find(\"Content-length\") != headers.end())\r\n            {\r\n                content_length = stoi(headers[\"Content-length\"]);\r\n            }\r\n            else\r\n            {\r\n                error = true;\r\n                handleError(fd, 400, \"Bad Request: Lack of argument (Content-length)\");\r\n                break;\r\n            }\r\n            if (inBuffer.size() < content_length)\r\n                break;\r\n            state = STATE_ANALYSIS;\r\n        }\r\n        if (state == STATE_ANALYSIS)\r\n        {\r\n            int flag = this->analysisRequest();\r\n            if (flag == ANALYSIS_SUCCESS)\r\n            {\r\n                state = STATE_FINISH;\r\n                break;\r\n            }\r\n            else\r\n            {\r\n                error = true;\r\n                break;\r\n            }\r\n        }\r\n    } while (false);\r\n\r\n    if (!error)\r\n    {\r\n        if (outBuffer.size() > 0)\r\n            events |= EPOLLOUT;\r\n        if (state == STATE_FINISH)\r\n        {\r\n            cout << \"keep_alive=\" << keep_alive << endl;\r\n            if (keep_alive)\r\n            {\r\n                this->reset();\r\n                events |= EPOLLIN;\r\n            }\r\n            else\r\n                return;\r\n        }\r\n        else\r\n            events |= EPOLLIN;\r\n    }\r\n}\r\n\r\nvoid RequestData::handleWrite()\r\n{\r\n    if (!error)\r\n    {\r\n        if (writen(fd, outBuffer) < 0)\r\n        {\r\n            perror(\"writen\");\r\n            events = 0;\r\n            error = true;\r\n        }\r\n        else if (outBuffer.size() > 0)\r\n            events |= EPOLLOUT;\r\n    }\r\n}\r\n\r\nvoid RequestData::handleConn()\r\n{\r\n    if (!error)\r\n    {\r\n        if (events != 0)\r\n        {\r\n            // 一定要先加时间信息，否则可能会出现刚加进去，下个in触发来了，然后分离失败后，又加入队列，最后超时被删，然后正在线程中进行的任务出错，double free错误。\r\n            // 新增时间信息\r\n            int timeout = 2000;\r\n            if (keep_alive)\r\n                timeout = 5 * 60 * 1000;\r\n            isAbleRead = false;\r\n            isAbleWrite = false;\r\n            Epoll::add_timer(shared_from_this(), timeout);\r\n            if ((events & EPOLLIN) && (events & EPOLLOUT))\r\n            {\r\n                events = __uint32_t(0);\r\n                events |= EPOLLOUT;\r\n            }\r\n            events |= (EPOLLET | EPOLLONESHOT);\r\n            __uint32_t _events = events;\r\n            events = 0;\r\n            if (Epoll::epoll_mod(fd, shared_from_this(), _events) < 0)\r\n            {\r\n                printf(\"Epoll::epoll_mod error\\n\");\r\n            }\r\n        }\r\n        else if (keep_alive)\r\n        {\r\n            events |= (EPOLLIN | EPOLLET | EPOLLONESHOT);\r\n            int timeout = 5 * 60 * 1000;\r\n            isAbleRead = false;\r\n            isAbleWrite = false;\r\n            Epoll::add_timer(shared_from_this(), timeout);\r\n            __uint32_t _events = events;\r\n            events = 0;\r\n            if (Epoll::epoll_mod(fd, shared_from_this(), _events) < 0)\r\n            {\r\n                printf(\"Epoll::epoll_mod error\\n\");\r\n            }\r\n        }\r\n    }\r\n}\r\n\r\n\r\nint RequestData::parse_URI()\r\n{\r\n    string &str = inBuffer;\r\n    // 读到完整的请求行再开始解析请求\r\n    int pos = str.find('\\r', now_read_pos);\r\n    if (pos < 0)\r\n    {\r\n        return PARSE_URI_AGAIN;\r\n    }\r\n    // 去掉请求行所占的空间，节省空间\r\n    string request_line = str.substr(0, pos);\r\n    if (str.size() > pos + 1)\r\n        str = str.substr(pos + 1);\r\n    else \r\n        str.clear();\r\n    // Method\r\n    pos = request_line.find(\"GET\");\r\n    if (pos < 0)\r\n    {\r\n        pos = request_line.find(\"POST\");\r\n        if (pos < 0)\r\n            return PARSE_URI_ERROR;\r\n        else\r\n            method = METHOD_POST;\r\n    }\r\n    else\r\n        method = METHOD_GET;\r\n    //printf(\"method = %d\\n\", method);\r\n    // filename\r\n    pos = request_line.find(\"/\", pos);\r\n    if (pos < 0)\r\n        return PARSE_URI_ERROR;\r\n    else\r\n    {\r\n        int _pos = request_line.find(' ', pos);\r\n        if (_pos < 0)\r\n            return PARSE_URI_ERROR;\r\n        else\r\n        {\r\n            if (_pos - pos > 1)\r\n            {\r\n                file_name = request_line.substr(pos + 1, _pos - pos - 1);\r\n                int __pos = file_name.find('?');\r\n                if (__pos >= 0)\r\n                {\r\n                    file_name = file_name.substr(0, __pos);\r\n                }\r\n            }\r\n                \r\n            else\r\n                file_name = \"index.html\";\r\n        }\r\n        pos = _pos;\r\n    }\r\n    //cout << \"file_name: \" << file_name << endl;\r\n    // HTTP 版本号\r\n    pos = request_line.find(\"/\", pos);\r\n    if (pos < 0)\r\n        return PARSE_URI_ERROR;\r\n    else\r\n    {\r\n        if (request_line.size() - pos <= 3)\r\n            return PARSE_URI_ERROR;\r\n        else\r\n        {\r\n            string ver = request_line.substr(pos + 1, 3);\r\n            if (ver == \"1.0\")\r\n                HTTPversion = HTTP_10;\r\n            else if (ver == \"1.1\")\r\n                HTTPversion = HTTP_11;\r\n            else\r\n                return PARSE_URI_ERROR;\r\n        }\r\n    }\r\n    return PARSE_URI_SUCCESS;\r\n}\r\n\r\nint RequestData::parse_Headers()\r\n{\r\n    string &str = inBuffer;\r\n    int key_start = -1, key_end = -1, value_start = -1, value_end = -1;\r\n    int now_read_line_begin = 0;\r\n    bool notFinish = true;\r\n    for (int i = 0; i < str.size() && notFinish; ++i)\r\n    {\r\n        switch(h_state)\r\n        {\r\n            case h_start:\r\n            {\r\n                if (str[i] == '\\n' || str[i] == '\\r')\r\n                    break;\r\n                h_state = h_key;\r\n                key_start = i;\r\n                now_read_line_begin = i;\r\n                break;\r\n            }\r\n            case h_key:\r\n            {\r\n                if (str[i] == ':')\r\n                {\r\n                    key_end = i;\r\n                    if (key_end - key_start <= 0)\r\n                        return PARSE_HEADER_ERROR;\r\n                    h_state = h_colon;\r\n                }\r\n                else if (str[i] == '\\n' || str[i] == '\\r')\r\n                    return PARSE_HEADER_ERROR;\r\n                break;  \r\n            }\r\n            case h_colon:\r\n            {\r\n                if (str[i] == ' ')\r\n                {\r\n                    h_state = h_spaces_after_colon;\r\n                }\r\n                else\r\n                    return PARSE_HEADER_ERROR;\r\n                break;  \r\n            }\r\n            case h_spaces_after_colon:\r\n            {\r\n                h_state = h_value;\r\n                value_start = i;\r\n                break;  \r\n            }\r\n            case h_value:\r\n            {\r\n                if (str[i] == '\\r')\r\n                {\r\n                    h_state = h_CR;\r\n                    value_end = i;\r\n                    if (value_end - value_start <= 0)\r\n                        return PARSE_HEADER_ERROR;\r\n                }\r\n                else if (i - value_start > 255)\r\n                    return PARSE_HEADER_ERROR;\r\n                break;  \r\n            }\r\n            case h_CR:\r\n            {\r\n                if (str[i] == '\\n')\r\n                {\r\n                    h_state = h_LF;\r\n                    string key(str.begin() + key_start, str.begin() + key_end);\r\n                    string value(str.begin() + value_start, str.begin() + value_end);\r\n                    headers[key] = value;\r\n                    now_read_line_begin = i;\r\n                }\r\n                else\r\n                    return PARSE_HEADER_ERROR;\r\n                break;  \r\n            }\r\n            case h_LF:\r\n            {\r\n                if (str[i] == '\\r')\r\n                {\r\n                    h_state = h_end_CR;\r\n                }\r\n                else\r\n                {\r\n                    key_start = i;\r\n                    h_state = h_key;\r\n                }\r\n                break;\r\n            }\r\n            case h_end_CR:\r\n            {\r\n                if (str[i] == '\\n')\r\n                {\r\n                    h_state = h_end_LF;\r\n                }\r\n                else\r\n                    return PARSE_HEADER_ERROR;\r\n                break;\r\n            }\r\n            case h_end_LF:\r\n            {\r\n                notFinish = false;\r\n                key_start = i;\r\n                now_read_line_begin = i;\r\n                break;\r\n            }\r\n        }\r\n    }\r\n    if (h_state == h_end_LF)\r\n    {\r\n        str = str.substr(now_read_line_begin);\r\n        return PARSE_HEADER_SUCCESS;\r\n    }\r\n    str = str.substr(now_read_line_begin);\r\n    return PARSE_HEADER_AGAIN;\r\n}\r\n\r\nint RequestData::analysisRequest()\r\n{\r\n    if (method == METHOD_POST)\r\n    {\r\n        //get inBuffer\r\n        string header;\r\n        header += string(\"HTTP/1.1 200 OK\\r\\n\");\r\n        if(headers.find(\"Connection\") != headers.end() && headers[\"Connection\"] == \"keep-alive\")\r\n        {\r\n            //cout << \"headers\" << headers[\"Connection\"] << endl;\r\n            keep_alive = true;\r\n            header += string(\"Connection: keep-alive\\r\\n\") + \"Keep-Alive: timeout=\" + to_string(5 * 60 * 1000) + \"\\r\\n\";\r\n        }\r\n        int length = stoi(headers[\"Content-length\"]);\r\n        vector<char> data(inBuffer.begin(), inBuffer.begin() + length);\r\n        cout << \" data.size()=\" << data.size() << endl;\r\n        Mat src = imdecode(data, CV_LOAD_IMAGE_ANYDEPTH|CV_LOAD_IMAGE_ANYCOLOR);\r\n        imwrite(\"receive.bmp\", src);\r\n        cout << \"1\" << endl;\r\n        Mat res = stitch(src);\r\n        cout << \"2\" << endl;\r\n        vector<uchar> data_encode;\r\n        imencode(\".png\", res, data_encode);\r\n        cout << \"3\" << endl;\r\n        header += string(\"Content-length: \") + to_string(data_encode.size()) + \"\\r\\n\\r\\n\";\r\n        cout << \"4\" << endl;\r\n        outBuffer += header + string(data_encode.begin(), data_encode.end());\r\n        cout << \"5\" << endl;\r\n        inBuffer = inBuffer.substr(length);\r\n        return ANALYSIS_SUCCESS;\r\n    }\r\n    else if (method == METHOD_GET)\r\n    {\r\n        string header;\r\n        header += \"HTTP/1.1 200 OK\\r\\n\";\r\n        if(headers.find(\"Connection\") != headers.end() && headers[\"Connection\"] == \"keep-alive\")\r\n        {\r\n            keep_alive = true;\r\n            header += string(\"Connection: keep-alive\\r\\n\") + \"Keep-Alive: timeout=\" + to_string(5 * 60 * 1000) + \"\\r\\n\";\r\n        }\r\n        int dot_pos = file_name.find('.');\r\n        string filetype;\r\n        if (dot_pos < 0) \r\n            filetype = MimeType::getMime(\"default\");\r\n        else\r\n            filetype = MimeType::getMime(file_name.substr(dot_pos));\r\n        struct stat sbuf;\r\n        if (stat(file_name.c_str(), &sbuf) < 0)\r\n        {\r\n            header.clear();\r\n            handleError(fd, 404, \"Not Found!\");\r\n            return ANALYSIS_ERROR;\r\n        }\r\n        header += \"Content-type: \" + filetype + \"\\r\\n\";\r\n        header += \"Content-length: \" + to_string(sbuf.st_size) + \"\\r\\n\";\r\n        // 头部结束\r\n        header += \"\\r\\n\";\r\n        outBuffer += header;\r\n        // size_t send_len = (size_t)writen(fd, header, strlen(header));\r\n        // if(send_len != strlen(header))\r\n        // {\r\n        //     perror(\"Send header failed\");\r\n        //     return ANALYSIS_ERROR;\r\n        // }\r\n        int src_fd = open(file_name.c_str(), O_RDONLY, 0);\r\n        char *src_addr = static_cast<char*>(mmap(NULL, sbuf.st_size, PROT_READ, MAP_PRIVATE, src_fd, 0));\r\n        close(src_fd);\r\n\r\n        outBuffer += src_addr;\r\n        munmap(src_addr, sbuf.st_size);\r\n        // send_len = writen(fd, src_addr, sbuf.st_size);\r\n        // if(send_len != sbuf.st_size)\r\n        // {\r\n        //     perror(\"Send file failed\");\r\n        //     return ANALYSIS_ERROR;\r\n        // }\r\n        return ANALYSIS_SUCCESS;\r\n    }\r\n    else\r\n        return ANALYSIS_ERROR;\r\n}\r\n\r\nvoid RequestData::handleError(int fd, int err_num, string short_msg)\r\n{\r\n    short_msg = \" \" + short_msg;\r\n    char send_buff[MAX_BUFF];\r\n    string body_buff, header_buff;\r\n    body_buff += \"<html><title>哎~出错了</title>\";\r\n    body_buff += \"<body bgcolor=\\\"ffffff\\\">\";\r\n    body_buff += to_string(err_num) + short_msg;\r\n    body_buff += \"<hr><em> LinYa's Web Server</em>\\n</body></html>\";\r\n\r\n    header_buff += \"HTTP/1.1 \" + to_string(err_num) + short_msg + \"\\r\\n\";\r\n    header_buff += \"Content-type: text/html\\r\\n\";\r\n    header_buff += \"Connection: close\\r\\n\";\r\n    header_buff += \"Content-length: \" + to_string(body_buff.size()) + \"\\r\\n\";\r\n    header_buff += \"\\r\\n\";\r\n    // 错误处理不考虑writen不完的情况\r\n    sprintf(send_buff, \"%s\", header_buff.c_str());\r\n    writen(fd, send_buff, strlen(send_buff));\r\n    sprintf(send_buff, \"%s\", body_buff.c_str());\r\n    writen(fd, send_buff, strlen(send_buff));\r\n}\r\n\r\n\r\nvoid RequestData::disableReadAndWrite()\r\n{\r\n    isAbleRead = false;\r\n    isAbleWrite = false;\r\n}\r\nvoid RequestData::enableRead()\r\n{\r\n    isAbleRead = true;\r\n}\r\nvoid RequestData::enableWrite()\r\n{\r\n    isAbleWrite = true;\r\n}\r\nbool RequestData::canRead()\r\n{\r\n    return isAbleRead;\r\n}\r\nbool RequestData::canWrite()\r\n{\r\n    return isAbleWrite;\r\n}"
  },
  {
    "path": "old_version/old_version_0.6/requestData.h",
    "content": "#pragma once\r\n#include \"timer.h\"\r\n#include <string>\r\n#include <unordered_map>\r\n#include <memory>\r\n#include <sys/epoll.h>\r\n\r\n\r\n#include <opencv/cv.h>\r\n#include <opencv2/core/core.hpp>\r\n#include <opencv2/highgui/highgui.hpp>\r\n#include <opencv2/opencv.hpp>\r\nusing namespace cv;\r\n\r\nconst int STATE_PARSE_URI = 1;\r\nconst int STATE_PARSE_HEADERS = 2;\r\nconst int STATE_RECV_BODY = 3;\r\nconst int STATE_ANALYSIS = 4;\r\nconst int STATE_FINISH = 5;\r\n\r\nconst int MAX_BUFF = 4096;\r\n\r\n// 有请求出现但是读不到数据,可能是Request Aborted,\r\n// 或者来自网络的数据没有达到等原因,\r\n// 对这样的请求尝试超过一定的次数就抛弃\r\nconst int AGAIN_MAX_TIMES = 200;\r\n\r\nconst int PARSE_URI_AGAIN = -1;\r\nconst int PARSE_URI_ERROR = -2;\r\nconst int PARSE_URI_SUCCESS = 0;\r\n\r\nconst int PARSE_HEADER_AGAIN = -1;\r\nconst int PARSE_HEADER_ERROR = -2;\r\nconst int PARSE_HEADER_SUCCESS = 0;\r\n\r\nconst int ANALYSIS_ERROR = -2;\r\nconst int ANALYSIS_SUCCESS = 0;\r\n\r\nconst int METHOD_POST = 1;\r\nconst int METHOD_GET = 2;\r\nconst int HTTP_10 = 1;\r\nconst int HTTP_11 = 2;\r\n\r\nconst int EPOLL_WAIT_TIME = 500;\r\n\r\nclass MimeType\r\n{\r\nprivate:\r\n    static void init();\r\n    static std::unordered_map<std::string, std::string> mime;\r\n    MimeType();\r\n    MimeType(const MimeType &m);\r\n\r\npublic:\r\n    static std::string getMime(const std::string &suffix);\r\n\r\nprivate:\r\n    static pthread_once_t once_control;\r\n};\r\n\r\nenum HeadersState\r\n{\r\n    h_start = 0,\r\n    h_key,\r\n    h_colon,\r\n    h_spaces_after_colon,\r\n    h_value,\r\n    h_CR,\r\n    h_LF,\r\n    h_end_CR,\r\n    h_end_LF\r\n};\r\n\r\nclass TimerNode;\r\n\r\nclass RequestData : public std::enable_shared_from_this<RequestData>\r\n{\r\nprivate:\r\n    std::string path;\r\n    int fd;\r\n    int epollfd;\r\n\r\n    std::string inBuffer;\r\n    std::string outBuffer;\r\n    __uint32_t events;\r\n    bool error;\r\n\r\n    int method;\r\n    int HTTPversion;\r\n    std::string file_name;\r\n    int now_read_pos;\r\n    int state;\r\n    int h_state;\r\n    bool isfinish;\r\n    bool keep_alive;\r\n    std::unordered_map<std::string, std::string> headers;\r\n    std::weak_ptr<TimerNode> timer;\r\n\r\n    bool isAbleRead;\r\n    bool isAbleWrite;\r\n\r\nprivate:\r\n    int parse_URI();\r\n    int parse_Headers();\r\n    int analysisRequest();\r\n\r\n    Mat stitch(Mat &src)\r\n    {\r\n        return src;\r\n    }\r\n\r\npublic:\r\n\r\n    RequestData();\r\n    RequestData(int _epollfd, int _fd, std::string _path);\r\n    ~RequestData();\r\n    void linkTimer(std::shared_ptr<TimerNode> mtimer);\r\n    void reset();\r\n    void seperateTimer();\r\n    int getFd();\r\n    void setFd(int _fd);\r\n    void handleRead();\r\n    void handleWrite();\r\n    void handleError(int fd, int err_num, std::string short_msg);\r\n    void handleConn();\r\n\r\n    void disableReadAndWrite();\r\n\r\n    void enableRead();\r\n\r\n    void enableWrite();\r\n\r\n    bool canRead();\r\n\r\n    bool canWrite();\r\n};\r\n\r\n"
  },
  {
    "path": "old_version/old_version_0.6/threadpool.cpp",
    "content": "#include \"threadpool.h\"\r\n\r\n\r\npthread_mutex_t ThreadPool::lock = PTHREAD_MUTEX_INITIALIZER;\r\npthread_cond_t ThreadPool::notify = PTHREAD_COND_INITIALIZER;\r\nstd::vector<pthread_t> ThreadPool::threads;\r\nstd::vector<ThreadPoolTask> ThreadPool::queue;\r\nint ThreadPool::thread_count = 0;\r\nint ThreadPool::queue_size = 0;\r\nint ThreadPool::head = 0;\r\nint ThreadPool::tail = 0;\r\nint ThreadPool::count = 0;\r\nint ThreadPool::shutdown = 0;\r\nint ThreadPool::started = 0;\r\n\r\nint ThreadPool::threadpool_create(int _thread_count, int _queue_size)\r\n{\r\n    bool err = false;\r\n    do\r\n    {\r\n        if(_thread_count <= 0 || _thread_count > MAX_THREADS || _queue_size <= 0 || _queue_size > MAX_QUEUE) \r\n        {\r\n            _thread_count = 4;\r\n            _queue_size = 1024;\r\n        }\r\n    \r\n        thread_count = 0;\r\n        queue_size = _queue_size;\r\n        head = tail = count = 0;\r\n        shutdown = started = 0;\r\n\r\n        threads.resize(_thread_count);\r\n        queue.resize(_queue_size);\r\n    \r\n        /* Start worker threads */\r\n        for(int i = 0; i < _thread_count; ++i) \r\n        {\r\n            if(pthread_create(&threads[i], NULL, threadpool_thread, (void*)(0)) != 0) \r\n            {\r\n                //threadpool_destroy(pool, 0);\r\n                return -1;\r\n            }\r\n            ++thread_count;\r\n            ++started;\r\n        }\r\n    } while(false);\r\n    \r\n    if (err) \r\n    {\r\n        //threadpool_free(pool);\r\n        return -1;\r\n    }\r\n    return 0;\r\n}\r\n\r\nvoid myHandler(std::shared_ptr<void> req)\r\n{\r\n    std::shared_ptr<RequestData> request = std::static_pointer_cast<RequestData>(req);\r\n    if (request->canWrite())\r\n        request->handleWrite();\r\n    else if (request->canRead())\r\n        request->handleRead();\r\n    request->handleConn();\r\n}\r\n\r\nint ThreadPool::threadpool_add(std::shared_ptr<void> args, std::function<void(std::shared_ptr<void>)> fun)\r\n{\r\n    int next, err = 0;\r\n    if(pthread_mutex_lock(&lock) != 0)\r\n        return THREADPOOL_LOCK_FAILURE;\r\n    do \r\n    {\r\n        next = (tail + 1) % queue_size;\r\n        // 队列满\r\n        if(count == queue_size) \r\n        {\r\n            err = THREADPOOL_QUEUE_FULL;\r\n            break;\r\n        }\r\n        // 已关闭\r\n        if(shutdown)\r\n        {\r\n            err = THREADPOOL_SHUTDOWN;\r\n            break;\r\n        }\r\n        queue[tail].fun = fun;\r\n        queue[tail].args = args;\r\n        tail = next;\r\n        ++count;\r\n        \r\n        /* pthread_cond_broadcast */\r\n        if(pthread_cond_signal(&notify) != 0) \r\n        {\r\n            err = THREADPOOL_LOCK_FAILURE;\r\n            break;\r\n        }\r\n    } while(false);\r\n\r\n    if(pthread_mutex_unlock(&lock) != 0)\r\n        err = THREADPOOL_LOCK_FAILURE;\r\n    return err;\r\n}\r\n\r\n\r\nint ThreadPool::threadpool_destroy(ShutDownOption shutdown_option)\r\n{\r\n    printf(\"Thread pool destroy !\\n\");\r\n    int i, err = 0;\r\n\r\n    if(pthread_mutex_lock(&lock) != 0) \r\n    {\r\n        return THREADPOOL_LOCK_FAILURE;\r\n    }\r\n    do \r\n    {\r\n        if(shutdown) {\r\n            err = THREADPOOL_SHUTDOWN;\r\n            break;\r\n        }\r\n        shutdown = shutdown_option;\r\n\r\n        if((pthread_cond_broadcast(&notify) != 0) ||\r\n           (pthread_mutex_unlock(&lock) != 0)) {\r\n            err = THREADPOOL_LOCK_FAILURE;\r\n            break;\r\n        }\r\n\r\n        for(i = 0; i < thread_count; ++i)\r\n        {\r\n            if(pthread_join(threads[i], NULL) != 0)\r\n            {\r\n                err = THREADPOOL_THREAD_FAILURE;\r\n            }\r\n        }\r\n    } while(false);\r\n\r\n    if(!err) \r\n    {\r\n        threadpool_free();\r\n    }\r\n    return err;\r\n}\r\n\r\nint ThreadPool::threadpool_free()\r\n{\r\n    if(started > 0)\r\n        return -1;\r\n    pthread_mutex_lock(&lock);\r\n    pthread_mutex_destroy(&lock);\r\n    pthread_cond_destroy(&notify);\r\n    return 0;\r\n}\r\n\r\n\r\nvoid *ThreadPool::threadpool_thread(void *args)\r\n{\r\n    while (true)\r\n    {\r\n        ThreadPoolTask task;\r\n        pthread_mutex_lock(&lock);\r\n        while((count == 0) && (!shutdown)) \r\n        {\r\n            pthread_cond_wait(&notify, &lock);\r\n        }\r\n        if((shutdown == immediate_shutdown) ||\r\n           ((shutdown == graceful_shutdown) && (count == 0)))\r\n        {\r\n            break;\r\n        }\r\n        task.fun = queue[head].fun;\r\n        task.args = queue[head].args;\r\n        queue[head].fun = NULL;\r\n        queue[head].args.reset();\r\n        head = (head + 1) % queue_size;\r\n        --count;\r\n        pthread_mutex_unlock(&lock);\r\n        (task.fun)(task.args);\r\n    }\r\n    --started;\r\n    pthread_mutex_unlock(&lock);\r\n    printf(\"This threadpool thread finishs!\\n\");\r\n    pthread_exit(NULL);\r\n    return(NULL);\r\n}"
  },
  {
    "path": "old_version/old_version_0.6/threadpool.h",
    "content": "#pragma once\r\n#include \"requestData.h\"\r\n//#include \"Condition.h\"\r\n#include <pthread.h>\r\n#include <functional>\r\n#include <memory>\r\n#include <vector>\r\n\r\nconst int THREADPOOL_INVALID = -1;\r\nconst int THREADPOOL_LOCK_FAILURE = -2;\r\nconst int THREADPOOL_QUEUE_FULL = -3;\r\nconst int THREADPOOL_SHUTDOWN = -4;\r\nconst int THREADPOOL_THREAD_FAILURE = -5;\r\nconst int THREADPOOL_GRACEFUL = 1;\r\n\r\nconst int MAX_THREADS = 1024;\r\nconst int MAX_QUEUE = 65535;\r\n\r\ntypedef enum\r\n{\r\n    immediate_shutdown = 1,\r\n    graceful_shutdown  = 2\r\n} ShutDownOption;\r\n\r\nstruct ThreadPoolTask\r\n{\r\n    std::function<void(std::shared_ptr<void>)> fun;\r\n    std::shared_ptr<void> args;\r\n};\r\n\r\nvoid myHandler(std::shared_ptr<void> req);\r\n\r\nclass ThreadPool\r\n{\r\nprivate:\r\n    static pthread_mutex_t lock;\r\n    static pthread_cond_t notify;\r\n\r\n    static std::vector<pthread_t> threads;\r\n    static std::vector<ThreadPoolTask> queue;\r\n    static int thread_count;\r\n    static int queue_size;\r\n    static int head;\r\n    // tail 指向尾节点的下一节点\r\n    static int tail;\r\n    static int count;\r\n    static int shutdown;\r\n    static int started;\r\npublic:\r\n    static int threadpool_create(int _thread_count, int _queue_size);\r\n    static int threadpool_add(std::shared_ptr<void> args, std::function<void(std::shared_ptr<void>)> fun = myHandler);\r\n    static int threadpool_destroy(ShutDownOption shutdown_option = graceful_shutdown);\r\n    static int threadpool_free();\r\n    static void *threadpool_thread(void *args);\r\n};\r\n"
  },
  {
    "path": "old_version/old_version_0.6/timer.cpp",
    "content": "#include \"timer.h\"\r\n#include \"epoll.h\"\r\n#include <unordered_map>\r\n#include <string>\r\n#include <sys/time.h>\r\n#include <unistd.h>\r\n#include <deque>\r\n#include <queue>\r\n\r\n#include <iostream>\r\nusing namespace std;\r\n\r\n\r\nTimerNode::TimerNode(SP_ReqData _request_data, int timeout): \r\n    deleted(false), \r\n    request_data(_request_data)\r\n{\r\n    //cout << \"TimerNode()\" << endl;\r\n    struct timeval now;\r\n    gettimeofday(&now, NULL);\r\n    // 以毫秒计\r\n    expired_time = ((now.tv_sec * 1000) + (now.tv_usec / 1000)) + timeout;\r\n}\r\n\r\nTimerNode::~TimerNode()\r\n{\r\n    //cout << \"~TimerNode()\" << endl;\r\n    if (request_data)\r\n    {\r\n        Epoll::epoll_del(request_data->getFd());\r\n    }\r\n    //request_data.reset();\r\n    // if (request_data)\r\n    // {\r\n    //     cout << \"request_data=\" << request_data << endl;\r\n    //     delete request_data;\r\n    //     request_data = NULL;\r\n    // }\r\n}\r\n\r\nvoid TimerNode::update(int timeout)\r\n{\r\n    struct timeval now;\r\n    gettimeofday(&now, NULL);\r\n    expired_time = ((now.tv_sec * 1000) + (now.tv_usec / 1000)) + timeout;\r\n}\r\n\r\nbool TimerNode::isvalid()\r\n{\r\n    struct timeval now;\r\n    gettimeofday(&now, NULL);\r\n    size_t temp = ((now.tv_sec * 1000) + (now.tv_usec / 1000));\r\n    if (temp < expired_time)\r\n    {\r\n        return true;\r\n    }\r\n    else\r\n    {\r\n        this->setDeleted();\r\n        return false;\r\n    }\r\n}\r\n\r\nvoid TimerNode::clearReq()\r\n{\r\n    request_data.reset();\r\n    this->setDeleted();\r\n}\r\n\r\nvoid TimerNode::setDeleted()\r\n{\r\n    deleted = true;\r\n}\r\n\r\nbool TimerNode::isDeleted() const\r\n{\r\n    return deleted;\r\n}\r\n\r\nsize_t TimerNode::getExpTime() const\r\n{\r\n    return expired_time;\r\n}\r\n\r\nTimerManager::TimerManager()\r\n{\r\n}\r\n\r\nTimerManager::~TimerManager()\r\n{\r\n}\r\n\r\nvoid TimerManager::addTimer(SP_ReqData request_data, int timeout)\r\n{\r\n    SP_TimerNode new_node(new TimerNode(request_data, timeout));\r\n    {\r\n        MutexLockGuard locker(lock);\r\n        TimerNodeQueue.push(new_node);\r\n    }\r\n    request_data->linkTimer(new_node);\r\n}\r\n\r\nvoid TimerManager::addTimer(SP_TimerNode timer_node)\r\n{\r\n\r\n}\r\n\r\n/* 处理逻辑是这样的~\r\n因为(1) 优先队列不支持随机访问\r\n(2) 即使支持，随机删除某节点后破坏了堆的结构，需要重新更新堆结构。\r\n所以对于被置为deleted的时间节点，会延迟到它(1)超时 或 (2)它前面的节点都被删除时，它才会被删除。\r\n一个点被置为deleted,它最迟会在TIMER_TIME_OUT时间后被删除。\r\n这样做有两个好处：\r\n(1) 第一个好处是不需要遍历优先队列，省时。\r\n(2) 第二个好处是给超时时间一个容忍的时间，就是设定的超时时间是删除的下限(并不是一到超时时间就立即删除)，如果监听的请求在超时后的下一次请求中又一次出现了，\r\n就不用再重新申请RequestData节点了，这样可以继续重复利用前面的RequestData，减少了一次delete和一次new的时间。\r\n*/\r\n\r\nvoid TimerManager::handle_expired_event()\r\n{\r\n    MutexLockGuard locker(lock);\r\n    while (!TimerNodeQueue.empty())\r\n    {\r\n        SP_TimerNode ptimer_now = TimerNodeQueue.top();\r\n        if (ptimer_now->isDeleted())\r\n        {\r\n            TimerNodeQueue.pop();\r\n            //delete ptimer_now;\r\n        }\r\n        else if (ptimer_now->isvalid() == false)\r\n        {\r\n            TimerNodeQueue.pop();\r\n            //delete ptimer_now;\r\n        }\r\n        else\r\n        {\r\n            break;\r\n        }\r\n    }\r\n}"
  },
  {
    "path": "old_version/old_version_0.6/timer.h",
    "content": "#pragma once\r\n#include \"requestData.h\"\r\n#include \"noncopyable.h\"\r\n#include \"MutexLock.h\"\r\n#include <unistd.h>\r\n#include <memory>\r\n#include <queue>\r\n#include <deque>\r\n\r\nclass RequestData;\r\n\r\nclass TimerNode\r\n{\r\n    typedef std::shared_ptr<RequestData> SP_ReqData;\r\nprivate:\r\n    bool deleted;\r\n    size_t expired_time;\r\n    SP_ReqData request_data;\r\npublic:\r\n    TimerNode(SP_ReqData _request_data, int timeout);\r\n    ~TimerNode();\r\n    void update(int timeout);\r\n    bool isvalid();\r\n    void clearReq();\r\n    void setDeleted();\r\n    bool isDeleted() const;\r\n    size_t getExpTime() const;\r\n};\r\n\r\nstruct timerCmp\r\n{\r\n    bool operator()(std::shared_ptr<TimerNode> &a, std::shared_ptr<TimerNode> &b) const\r\n    {\r\n        return a->getExpTime() > b->getExpTime();\r\n    }\r\n};\r\n\r\nclass TimerManager\r\n{\r\n    typedef std::shared_ptr<RequestData> SP_ReqData;\r\n    typedef std::shared_ptr<TimerNode> SP_TimerNode;\r\nprivate:\r\n    std::priority_queue<SP_TimerNode, std::deque<SP_TimerNode>, timerCmp> TimerNodeQueue;\r\n    MutexLock lock;\r\npublic:\r\n    TimerManager();\r\n    ~TimerManager();\r\n    void addTimer(SP_ReqData request_data, int timeout);\r\n    void addTimer(SP_TimerNode timer_node);\r\n    void handle_expired_event();\r\n};"
  },
  {
    "path": "old_version/old_version_0.6/util.cpp",
    "content": "#include \"util.h\"\r\n#include <unistd.h>\r\n#include <fcntl.h>\r\n#include <signal.h>\r\n#include <errno.h>\r\n#include <string.h>\r\n\r\nconst int MAX_BUFF = 4096;\r\nssize_t readn(int fd, void *buff, size_t n)\r\n{\r\n    size_t nleft = n;\r\n    ssize_t nread = 0;\r\n    ssize_t readSum = 0;\r\n    char *ptr = (char*)buff;\r\n    while (nleft > 0)\r\n    {\r\n        if ((nread = read(fd, ptr, nleft)) < 0)\r\n        {\r\n            if (errno == EINTR)\r\n                nread = 0;\r\n            else if (errno == EAGAIN)\r\n            {\r\n                return readSum;\r\n            }\r\n            else\r\n            {\r\n                return -1;\r\n            }  \r\n        }\r\n        else if (nread == 0)\r\n            break;\r\n        readSum += nread;\r\n        nleft -= nread;\r\n        ptr += nread;\r\n    }\r\n    return readSum;\r\n}\r\n\r\nssize_t readn(int fd, std::string &inBuffer)\r\n{\r\n    ssize_t nread = 0;\r\n    ssize_t readSum = 0;\r\n    while (true)\r\n    {\r\n        char buff[MAX_BUFF];\r\n        if ((nread = read(fd, buff, MAX_BUFF)) < 0)\r\n        {\r\n            if (errno == EINTR)\r\n                continue;\r\n            else if (errno == EAGAIN)\r\n            {\r\n                \r\n                return readSum;\r\n            }  \r\n            else\r\n            {\r\n                perror(\"read error\");\r\n                return -1;\r\n            }\r\n        }\r\n        else if (nread == 0)\r\n            break;\r\n        //printf(\"before inBuffer.size() = %d\\n\", inBuffer.size());\r\n        //printf(\"nread = %d\\n\", nread);\r\n        readSum += nread;\r\n        //buff += nread;\r\n        inBuffer += std::string(buff, buff + nread);\r\n        //printf(\"after inBuffer.size() = %d\\n\", inBuffer.size());\r\n    }\r\n    return readSum;\r\n}\r\n\r\nssize_t writen(int fd, void *buff, size_t n)\r\n{\r\n    size_t nleft = n;\r\n    ssize_t nwritten = 0;\r\n    ssize_t writeSum = 0;\r\n    char *ptr = (char*)buff;\r\n    while (nleft > 0)\r\n    {\r\n        if ((nwritten = write(fd, ptr, nleft)) <= 0)\r\n        {\r\n            if (nwritten < 0)\r\n            {\r\n                if (errno == EINTR)\r\n                {\r\n                    nwritten = 0;\r\n                    continue;\r\n                }\r\n                else if (errno == EAGAIN)\r\n                {\r\n                    return writeSum;\r\n                }\r\n                else\r\n                    return -1;\r\n            }\r\n        }\r\n        writeSum += nwritten;\r\n        nleft -= nwritten;\r\n        ptr += nwritten;\r\n    }\r\n    return writeSum;\r\n}\r\n\r\nssize_t writen(int fd, std::string &sbuff)\r\n{\r\n    size_t nleft = sbuff.size();\r\n    ssize_t nwritten = 0;\r\n    ssize_t writeSum = 0;\r\n    const char *ptr = sbuff.c_str();\r\n    while (nleft > 0)\r\n    {\r\n        if ((nwritten = write(fd, ptr, nleft)) <= 0)\r\n        {\r\n            if (nwritten < 0)\r\n            {\r\n                if (errno == EINTR)\r\n                {\r\n                    nwritten = 0;\r\n                    continue;\r\n                }\r\n                else if (errno == EAGAIN)\r\n                    break;\r\n                else\r\n                    return -1;\r\n            }\r\n        }\r\n        writeSum += nwritten;\r\n        nleft -= nwritten;\r\n        ptr += nwritten;\r\n    }\r\n    if (writeSum == sbuff.size())\r\n        sbuff.clear();\r\n    else\r\n        sbuff = sbuff.substr(writeSum);\r\n    return writeSum;\r\n}\r\n\r\nvoid handle_for_sigpipe()\r\n{\r\n    struct sigaction sa;\r\n    memset(&sa, '\\0', sizeof(sa));\r\n    sa.sa_handler = SIG_IGN;\r\n    sa.sa_flags = 0;\r\n    if(sigaction(SIGPIPE, &sa, NULL))\r\n        return;\r\n}\r\n\r\nint setSocketNonBlocking(int fd)\r\n{\r\n    int flag = fcntl(fd, F_GETFL, 0);\r\n    if(flag == -1)\r\n        return -1;\r\n\r\n    flag |= O_NONBLOCK;\r\n    if(fcntl(fd, F_SETFL, flag) == -1)\r\n        return -1;\r\n    return 0;\r\n}"
  },
  {
    "path": "old_version/old_version_0.6/util.h",
    "content": "#pragma once\r\n#include <cstdlib>\r\n#include <string>\r\n\r\nssize_t readn(int fd, void *buff, size_t n);\r\nssize_t readn(int fd, std::string &inBuffer);\r\nssize_t writen(int fd, void *buff, size_t n);\r\nssize_t writen(int fd, std::string &sbuff);\r\nvoid handle_for_sigpipe();\r\nint setSocketNonBlocking(int fd);"
  },
  {
    "path": "并发模型.md",
    "content": "# 并发模型\r\n\r\n程序使用Reactor模型，并使用多线程提高并发度。为避免线程频繁创建和销毁带来的开销，使用线程池，在程序的开始创建固定数量的线程。使用epoll作为IO多路复用的实现方式。\r\n\r\n## 线程\r\n一般而言，多线程服务器中的线程可分为以下几类：  \r\n\r\n* IO线程(负责网络IO)\r\n* 计算线程(负责复杂计算)\r\n* 第三方库所用线程\r\n\r\n本程序中的Log线程属于第三种，其它线程属于IO线程，因为Web静态服务器计算量较小，所以没有分配计算线程，减少跨线程分配的开销，让IO线程兼顾计算任务。除Log线程外，每个线程一个事件循环，遵循One loop per thread。\r\n\r\n## 并发模型\r\n本程序使用的并发模型如下图所示：\r\n\r\n![并发模型](https://github.com/linyacool/WebServer/blob/master/datum/model.png)\r\n\r\nMainReactor只有一个，负责响应client的连接请求，并建立连接，它使用一个NIO Selector。在建立连接后用Round Robin的方式分配给某个SubReactor,因为涉及到跨线程任务分配，需要加锁，这里的锁由某个特定线程中的loop创建，只会被该线程和主线程竞争。\r\n\r\nSubReactor可以有一个或者多个，每个subReactor都会在一个独立线程中运行，并且维护一个独立的NIO Selector。\r\n\r\n当主线程把新连接分配给了某个SubReactor，该线程此时可能正阻塞在多路选择器(epoll)的等待中，怎么得知新连接的到来呢？这里使用了eventfd进行异步唤醒，线程会从epoll_wait中醒来，得到活跃事件，进行处理。\r\n\r\n我学习了muduo库中的runInLoop和queueInLoop的设计方法，这两个方法主要用来执行用户的某个回调函数，queueInLoop是跨进程调用的精髓所在，具有极大的灵活性，我们只需要绑定好回调函数就可以了，我仿照muduo实现了这一点。\r\n\r\n## epoll工作模式\r\nepoll的触发模式在这里我选择了ET模式，muduo使用的是LT，这两者IO处理上有很大的不同。ET模式要比LE复杂许多，它对用户提出了更高的要求，即每次读，必须读到不能再读(出现EAGAIN)，每次写，写到不能再写(出现EAGAIN)。而LT则简单的多，可以选择也这样做，也可以为编程方便，比如每次只read一次(muduo就是这样做的，这样可以减少系统调用次数)。\r\n\r\n## 定时器\r\n\r\n每个SubReactor持有一个定时器，用于处理超时请求和长时间不活跃的连接。muduo中介绍了时间轮的实现和用stl里set的实现，这里我的实现直接使用了stl里的priority_queue，底层是小根堆，并采用惰性删除的方式，时间的到来不会唤醒线程，而是每次循环的最后进行检查，如果超时了再删，因为这里对超时的要求并不会很高，如果此时线程忙，那么检查时间队列的间隔也会短，如果不忙，也给了超时请求更长的等待时间。\r\n\r\n## 核心结构\r\n\r\n程序中的每一个类和结构体当然都必不可少，其中能体现并发模型和整体架构的，我认为是有两个：\r\n\r\n* Channel类：Channel是Reactor结构中的“事件”，它自始至终都属于一个EventLoop，负责一个文件描述符的IO事件，在Channel类中保存这IO事件的类型以及对应的回调函数，当IO事件发生时，最终会调用到Channel类中的回调函数。因此，程序中所有带有读写时间的对象都会和一个Channel关联，包括loop中的eventfd，listenfd，HttpData等。\r\n* EventLoop：One loop per thread意味着每个线程只能有一个EventLoop对象，EventLoop即是时间循环，每次从poller里拿活跃事件，并给到Channel里分发处理。EventLoop中的loop函数会在最底层(Thread)中被真正调用，开始无限的循环，直到某一轮的检查到退出状态后从底层一层一层的退出。\r\n\r\n## Log\r\nLog的实现了学习了muduo，Log的实现分为前端和后端，前端往后端写，后端往磁盘写。为什么要这样区分前端和后端呢？因为只要涉及到IO，无论是网络IO还是磁盘IO，肯定是慢的，慢就会影响其它操作，必须让它快才行。  \r\n\r\n这里的Log前端是前面所述的IO线程，负责产生log，后端是Log线程，设计了多个缓冲区，负责收集前端产生的log，集中往磁盘写。这样，Log写到后端是没有障碍的，把慢的动作交给后端去做好了。\r\n\r\n后端主要是由多个缓冲区构成的，集满了或者时间到了就向文件写一次。采用了muduo介绍了“双缓冲区”的思想，实际采用4个多的缓冲区(为什么说多呢？为什么4个可能不够用啊，要有备无患)。4个缓冲区分两组，每组的两个一个主要的，另一个防止第一个写满了没地方写，写满或者时间到了就和另外两个交换**指针**，然后把满的往文件里写。\r\n\r\n与Log相关的类包括FileUtil、LogFile、AsyncLogging、LogStream、Logging。\r\n其中前4个类每一个类都含有一个append函数，Log的设计也是主要围绕这个**append**函数展开的。\r\n\r\n* FileUtil是最底层的文件类，封装了Log文件的打开、写入并在类析构的时候关闭文件，底层使用了标准IO，该append函数直接向文件写。\r\n* LogFile进一步封装了FileUtil，并设置了一个循环次数，每过这么多次就flush一次。\r\n* AsyncLogging是核心，它负责启动一个log线程，专门用来将log写入LogFile，应用了“双缓冲技术”，其实有4个以上的缓冲区，但思想是一样的。AsyncLogging负责(定时到或被填满时)将缓冲区中的数据写入LogFile中。\r\n* LogStream主要用来格式化输出，重载了<<运算符，同时也有自己的一块缓冲区，这里缓冲区的存在是为了缓存一行，把多个<<的结果连成一块。\r\n* Logging是对外接口，Logging类内涵一个LogStream对象，主要是为了每次打log的时候在log之前和之后加上固定的格式化的信息，比如打log的行、文件名等信息。"
  },
  {
    "path": "测试及改进.md",
    "content": "# 测试及改进\r\n\r\n## 测试环境\r\n* OS：Ubuntu 14.04\r\n* 内存：8G\r\n* CPU：I7-4720HQ\r\n\r\n## 测试方法\r\n* 理想的测试环境是两台计算机，带宽无限，现在的网卡虽然都是千兆网卡，但是普通家用的网线都是5类双绞线，最高100Mbps,在linux下用ethtool可以看到网卡的速度被限制为100Mbsp，无法更改为更高的，经测试很容易跑满带宽，因此退而选择本地环境。\r\n* 使用工具Webbench，开启1000客户端进程，时间为60s\r\n* 分别测试短连接和长连接的情况\r\n* 关闭所有的输出及Log\r\n* 为避免磁盘IO对测试结果的影响，测试响应为内存中的\"Hello World\"字符加上必要的HTTP头\r\n* 我的最终版本中很多方面借鉴了muduo的思路，muduo中也提供了一个简单的HTTP echo测试，因此我将与muduo进行一个小小的对比，我修改了muduo测试的代码，使其echo相同的内容，关闭muduo的所有输出及Log\r\n* 线程池开启4线程\r\n* 因为发送的内容很少，为避免发送可能的延迟，关闭Nagle算法\r\n\r\n\r\n## 测试结果及分析\r\n测试截图放在最后  \r\n\r\n| 服务器 | 短连接QPS | 长连接QPS | \r\n| - | :-: | -: | \r\n| WebServer | 126798| 335338 | \r\n| Muduo | 88430 | 358302 | \r\n\r\n* 首先很明显的一点是长链接能处理的请求数是短连接的三四倍，因为没有了连接建立和断开的开销，不需要频繁accept和shutdown\\close等系统调用，也不需要频繁建立和销毁对应的结构体。\r\n* 我的服务器在最后的版本中，没有改进输入输出Buffer，用了效率低下的string，muduo用的是设计良好的vector<char>，我将在后续改进这一点。这也造成了在长连接的情况下，我的server逊于muduo。虽说边沿触发效率高一点，但是还是比不过在Buffer上性能的优化的。\r\n* 短链接的情况下，我的服务器要超过Muduo很多。原因在于：Muduo采用水平触发方式(Linux下用epoll)，并且做法是每次Acceptor只accept一次就返回，面对突然的并发量，必然会因为频繁的epoll_wait耽误大量的时间，而我的做法是用while包裹accept，一直accept到不能再accept。当然，如果同时连接的请求很少，陈硕在书中也提到过，假如一次只有一个连接，那么我的方式就会多一次accpet才能跳出循环，但是这样的代价似乎微不足道啊，换来的效率却高了不少。\r\n* 空闲时，Server几乎不占CPU，短连接时，各线程的CPU负载比较均衡，长连接时，主线程负载0，线程池的线程负载接近100%，因为没有新的连接需要处理。各种情况均正常。\r\n* 没有严格的考证，测试时发现，HTTP的header解析的结果用map比用unordered_map快，网上的博客里有很多人做了测试，我在做实验的时候大致也发现了。主要是因为数据量太小，一个HTTP请求头才几个头部字段，建立unordered_map的成本要比map高，数据量小，复杂度根本体现不出来。\r\n\r\n\r\n\r\n## 测试结果截图\r\n* WebServer短连接测试  \r\n![shortWeb](https://github.com/linyacool/WebServer/blob/master/datum/WebServer.png)\r\n* muduo短连接测试  \r\n![shortMuduo](https://github.com/linyacool/WebServer/blob/master/datum/muduo.png)\r\n* WebServer长连接测试  \r\n![keepWeb](https://github.com/linyacool/WebServer/blob/master/datum/WebServerk.png)\r\n* muduo长连接测试  \r\n![keepMuduo](https://github.com/linyacool/WebServer/blob/master/datum/muduok.png)\r\n* WebServer空闲负载  \r\n![idle](https://github.com/linyacool/WebServer/blob/master/datum/idle.png)\r\n* WebServer短连接CPU负载  \r\n![short](https://github.com/linyacool/WebServer/blob/master/datum/close.png)\r\n* WebServer长连接CPU负载  \r\n![keep](https://github.com/linyacool/WebServer/blob/master/datum/keepalive.png)\r\n\r\n"
  },
  {
    "path": "版本历史.md",
    "content": "# 版本历史\n从0.1最初形成到一点一点改进到0.6，到最终看了muduo，痛下决心重写，最终的版本完全从头再来了，但有了前面的经验，写起来顺畅了不少，但花了比之前所有加起来还要长的时间\n## 0.1\n\n第一版是看了很多Github上别人写的服务器，以及博客上的一些总结，结合自己的理解写出来的。模型结构如下：\n\n* 使用了epoll边沿触发+EPOLLONESHOT+非阻塞IO\n* 使用了一个固定线程数的线程池\n* 实现了一个任务队列，由条件变量触发通知新任务的到来\n* 实现了一个小根堆的定时器及时剔除超时请求，使用了STL的优先队列来管理定时器\n* 解析了HTTP的get、post请求，支持长短连接\n* mime设计为单例模式\n* 线程的工作分配为：\n    * 主线程负责等待epoll中的事件，并把到来的事件放进任务队列，在每次循环的结束剔除超时请求和被置为删除的时间结点\n    * 工作线程阻塞在条件变量的等待中，新任务到来后，某一工作线程会被唤醒，执行具体的IO操作和计算任务，如果需要继续监听，会添加到epoll中  \n\n* 锁的使用有两处：\n    * 第一处是任务队列的添加和取操作，都需要加锁，并配合条件变量，跨越了多个线程。\n    * 第二处是定时器结点的添加和删除，需要加锁，主线程和工作线程都要操作定时器队列。\n\n\n\n第一版的服务器已经相对较完整了，该有的功能都已经具备了\n\n## 0.2\n\n在第一版的基础上，优化了代码结构，自己设计了RAII锁机制，使锁能够自动释放，并修复了一些小的bug\n\n## 0.3\n\n* 几乎全部的裸指针被智能指针替代\n* 利用weak_ptr解决时间结点和HTTP类互指的问题\n* 任务队列的任务结构从函数指针+参数指针转换为C++11的function  \n\n这一版还是花了不少时间的，毕竟对象的生命周期不由自己控制了\n\n## 0.4\n\n这个时候买了陈硕的《Linux多线程服务端编程》书，看了一部分，从前面几章获得启发\n* 为不提供拷贝构造和赋值运算符的类添加了noncopyable基类\n* 重写了RAII机制的锁，学习muduo中的做法\n* 重写了单例模式，将双重加锁改为更简单而安全的pthread_once()\n\n## 0.5\n\n* 修复了一些bug，稍微调整了类的结构\n* 封装了条件变量\n\n## 0.6\n\n* 仿照muduo，写了4个缓冲区的异步Log日志，没有区分优先级，其它基本都具备了\n\n## WebServer(重构)\n\n不知道该给自己的服务器取什么名字好，随便叫个吧……最后一版被我改的面目全非，也是下了很大的决心。之前的版本无非修修补补，算是自我检讨的过程，但是闭门造车并不可取，于是我把陈硕的《Linux多线程服务端编程》看完了，书上虽然贴了部分源码，但我看的还是朦朦胧胧，很多地方不明白，花了几天时间把源码看了，不懂的地方再回过来看书，总算是弄明白了。看了大牛的代码，再看自己的……哎我重写总行了吧\n\n顺便吐槽一下自己，之前在知乎上看到陈硕一直推销自己的书，我还觉得这人好功利，后来被师兄推荐，看了一下目录，觉得可以参考一下就买了。没想到书就一点一点这么看完了……还看了好几遍，源码也是看了好几遍……\n\n当然，这不是最终篇，可改进的地方还有很多，绝对不敢说看了几本书就敢说自己写的东西比大牛写的好，我还会继续改进自己的server的\n\n最后版本的东西没有在这里介绍，写在了模型结构里，这里只想写一下自己的心路历程，记录一下小白成长之路～"
  },
  {
    "path": "连接的维护.md",
    "content": "# 连接维护(针对非阻塞IO)\r\n\r\n### 建立连接\r\n\r\n* 建立连接的过程  \r\n连接的建立比较简单，server端通过socket()，bind()，listen()，并使用epoll ET模式监听listenfd的读请求，当TCP连接完成3次握手后，会触发listenfd的读事件，应用程序调用accept()，会检查已完成的连接队列，如果队列里有连接，就返回这个连接，出错或连接为空时返回-1。此时，已经可以进行正常的读写操作了。 当然，因为是ET模式，accept()要一直循环到就绪连接为空。\r\n* 分析  \r\n之所以说建立连接的过程比较简单，是因为数据的通信已经由操作系统帮我们完成了，这里的通信是指3次握手的过程，这个过程不需要应用程序参与，当应用程序感知到连接时，此时该连接已经完成了3次握手的过程，accept就好了。另一个原因是一般情况下，连接的建立都是client发起的，server端被动建立连接就好了，也不会出现同时建立的情况。\r\n* 限制  \r\n假设server只监听一个端口，一个连接就是一个四元组(原ip，原port，对端ip, 对端port)，那么理论上可以建立2^48个连接，可是，fd可没有这么多(操作系统限制、用户进程限制)。当连接满了，如果空等而不连接，那么就绪队列也满了后，会导致新连接无法建立。这里的做法我参考了muduo，准备一个空的文件描述符，accept()后直接close()，这样对端不会收到RST，至少可以知道服务器正在运行。\r\n\r\n### 关闭连接\r\n\r\n相对于连接的建立，关闭连接则复杂的多，远不是一个close()那么简单，关闭连接要优雅。\r\n\r\n##### 什么时候关闭连接？\r\n通常server和client都可以主动发Fin来关闭连接  \r\n\r\n* 对于client(非Keep-Alive)，发送完请求后就可以shutdown()写端，然后收到server发来的应答，最后close掉连接。也可以不shutdown()写，等读完直接close。对于Keep-Alive的情况，就要看client的心情了，收到消息后可以断，也可以不断，server应该保证不主动断开。\r\n\r\n* 对于server端，毫无疑问应该谨慎处理以上所有情况。具体说来:\r\n> * 出现各种关于连接的错误时，可以直接close()掉\r\n> * 短连接超时的请求，可以close()，也可以不关\r\n> * 长连接对方长时间没有请求(如果没有保活机制)，可以close()，也可以不关\r\n> * client发出Fin，server会收到0字节，通常不能判断client是close了还是shutdown，这时server应当把消息发完，然后才可以close()，如果对方调用的是close，会收到RST，server能感知到，就可以立即close了\r\n> * 短连接正常结束，server可以close，也可以不close，大多数的实现是不close的(对HTTP1.1而言)\r\n\r\n\r\n##### EPOLLIN触发但是read()返回0的情况\r\n\r\n这种情况通常有两个原因:\r\n> * 对端已经关闭了连接，这时再写该fd会出错，此时应该关闭连接\r\n> * 对端只是shutdown()了写端，告诉server我已经写完了，但是还可以接收信息。server应该在写完所有的信息后再关闭连接。更优雅的做法是透明的传递这个行为，即server顺着关闭读端，然后发完数据后关闭。\r\n"
  },
  {
    "path": "遇到的困难.md",
    "content": "# 遇到的困难\n\n## 1. 如何设计各个线程个任务\n其实我觉的实现上的困难都不算真正的困难吧，毕竟都能写出来，无非是解决bug花的时间的长短。  \n我遇到的最大的问题是不太理解One loop per thread这句话吧，翻译出来不就是每个线程一个循环，我最开始写的也是一个线程一个循环啊，muduo的实现和我的有什么区别呢？还有怎么设计才能减少竞态？\n\n带着这些问题我看了《Linux多线程服务端编程》，并看完了muduo的源码，这些问题自然而然就解决了\n\n\n## 2. 异步Log几秒钟才写一次磁盘，要是coredump了，这段时间内产生的log我去哪找啊？\n\n其实这个问题非常简单了，也没花多少时间去解决，但我觉的非常好玩。coredump了自然会保存在core文件里了，无非就是把它找出来的问题了，在这里记录一下。\n\n当然这里不管coredump的原因是什么，我只想看丢失的log。所以模拟的话在某个地方abort()就行\n\n多线程调试嘛，先看线程信息，info thread，找到我的异步打印线程，切换进去看bt调用栈，正常是阻塞在条件变量是wait条件中的，frame切换到threadFunc(这个函数是我的异步log里面的循环的函数名)，剩下的就是print啦～不过，我的Buffer是用智能指针shared_ptr包裹的，直接->不行，gdb不识别，优化完.get()不让用，可能被inline掉了，只能直接从shared_ptr源码中找到_M_ptr成员来打印。\n\n![gdb](https://github.com/linyacool/WebServer/blob/master/datum/gdb.png)"
  },
  {
    "path": "项目目的.md",
    "content": "# 项目目的\r\n---\r\n### 最初的想法\r\n本项目是我在三星电子(中国)研发中心实习期间利用晚上和周末的时间完成的，实习期间我负责4K分辨率双鱼眼摄像头视频拼接的算法设计与实现，我希望能把其中的图像拼接部分的成果通过Web的方式展示出来，但因为涉及保密协议，不得不放弃这一想法。\r\n\r\n### Web服务器能够很好的贯穿所学的知识\r\n但是，Web服务器能够很好的贯穿之前所学的知识，之前看过的《C++ Primer》、《Effevtive C++》、《STL源码剖析》、《深度探索C++对象模型》、《TCP\\IP详解卷1》、APUE、UNP，还包括了《后台开发核心技术与应用实践》等书，涵盖了  \r\n  \r\n* TCP、HTTP协议\r\n* 多进程多线程\r\n* IO\r\n* 锁\r\n* 通信\r\n* C++语法\r\n* 编程规范\r\n* Linux环境下各种工具的使用\r\n* 版本控制Git\r\n* Makefile和CMakeLists文件的编写\r\n* 自动化构建工具Travis CI的使用\r\n  \r\n最终的版本在很多方面学习了muduo网络库，在看完陈硕的《Linux多线程服务端编程》后，对照着书把muduo的源码读了几遍，并重构了自己的服务器，最终的很多想法借鉴了muduo的思想。\r\n"
  }
]