Compare commits
28 Commits
f6480ba8ad
...
b6b31818aa
Author | SHA1 | Date | |
---|---|---|---|
b6b31818aa | |||
681f65a6c4 | |||
540aab40d4 | |||
79c20a74ea | |||
1617790e48 | |||
5e422227ab | |||
273982e7d3 | |||
212731101e | |||
b6d77a7910 | |||
c653ed5b4b | |||
6ec43d60ed | |||
e6f2d8041b | |||
34bd74c67f | |||
19ab3637cf | |||
790c165268 | |||
cb097b3ce5 | |||
73c73ef1f5 | |||
29607a6d46 | |||
54a5b66436 | |||
d260646f29 | |||
36e7081920 | |||
63c9757a50 | |||
0f4bbb87f7 | |||
65ff5fb439 | |||
85c8aeaad8 | |||
39660fae32 | |||
18a4b7f72b | |||
2911d1c411 |
373
LICENSE
Normal file
373
LICENSE
Normal file
@ -0,0 +1,373 @@
|
||||
Mozilla Public License Version 2.0
|
||||
==================================
|
||||
|
||||
1. Definitions
|
||||
--------------
|
||||
|
||||
1.1. "Contributor"
|
||||
means each individual or legal entity that creates, contributes to
|
||||
the creation of, or owns Covered Software.
|
||||
|
||||
1.2. "Contributor Version"
|
||||
means the combination of the Contributions of others (if any) used
|
||||
by a Contributor and that particular Contributor's Contribution.
|
||||
|
||||
1.3. "Contribution"
|
||||
means Covered Software of a particular Contributor.
|
||||
|
||||
1.4. "Covered Software"
|
||||
means Source Code Form to which the initial Contributor has attached
|
||||
the notice in Exhibit A, the Executable Form of such Source Code
|
||||
Form, and Modifications of such Source Code Form, in each case
|
||||
including portions thereof.
|
||||
|
||||
1.5. "Incompatible With Secondary Licenses"
|
||||
means
|
||||
|
||||
(a) that the initial Contributor has attached the notice described
|
||||
in Exhibit B to the Covered Software; or
|
||||
|
||||
(b) that the Covered Software was made available under the terms of
|
||||
version 1.1 or earlier of the License, but not also under the
|
||||
terms of a Secondary License.
|
||||
|
||||
1.6. "Executable Form"
|
||||
means any form of the work other than Source Code Form.
|
||||
|
||||
1.7. "Larger Work"
|
||||
means a work that combines Covered Software with other material, in
|
||||
a separate file or files, that is not Covered Software.
|
||||
|
||||
1.8. "License"
|
||||
means this document.
|
||||
|
||||
1.9. "Licensable"
|
||||
means having the right to grant, to the maximum extent possible,
|
||||
whether at the time of the initial grant or subsequently, any and
|
||||
all of the rights conveyed by this License.
|
||||
|
||||
1.10. "Modifications"
|
||||
means any of the following:
|
||||
|
||||
(a) any file in Source Code Form that results from an addition to,
|
||||
deletion from, or modification of the contents of Covered
|
||||
Software; or
|
||||
|
||||
(b) any new file in Source Code Form that contains any Covered
|
||||
Software.
|
||||
|
||||
1.11. "Patent Claims" of a Contributor
|
||||
means any patent claim(s), including without limitation, method,
|
||||
process, and apparatus claims, in any patent Licensable by such
|
||||
Contributor that would be infringed, but for the grant of the
|
||||
License, by the making, using, selling, offering for sale, having
|
||||
made, import, or transfer of either its Contributions or its
|
||||
Contributor Version.
|
||||
|
||||
1.12. "Secondary License"
|
||||
means either the GNU General Public License, Version 2.0, the GNU
|
||||
Lesser General Public License, Version 2.1, the GNU Affero General
|
||||
Public License, Version 3.0, or any later versions of those
|
||||
licenses.
|
||||
|
||||
1.13. "Source Code Form"
|
||||
means the form of the work preferred for making modifications.
|
||||
|
||||
1.14. "You" (or "Your")
|
||||
means an individual or a legal entity exercising rights under this
|
||||
License. For legal entities, "You" includes any entity that
|
||||
controls, is controlled by, or is under common control with You. For
|
||||
purposes of this definition, "control" means (a) the power, direct
|
||||
or indirect, to cause the direction or management of such entity,
|
||||
whether by contract or otherwise, or (b) ownership of more than
|
||||
fifty percent (50%) of the outstanding shares or beneficial
|
||||
ownership of such entity.
|
||||
|
||||
2. License Grants and Conditions
|
||||
--------------------------------
|
||||
|
||||
2.1. Grants
|
||||
|
||||
Each Contributor hereby grants You a world-wide, royalty-free,
|
||||
non-exclusive license:
|
||||
|
||||
(a) under intellectual property rights (other than patent or trademark)
|
||||
Licensable by such Contributor to use, reproduce, make available,
|
||||
modify, display, perform, distribute, and otherwise exploit its
|
||||
Contributions, either on an unmodified basis, with Modifications, or
|
||||
as part of a Larger Work; and
|
||||
|
||||
(b) under Patent Claims of such Contributor to make, use, sell, offer
|
||||
for sale, have made, import, and otherwise transfer either its
|
||||
Contributions or its Contributor Version.
|
||||
|
||||
2.2. Effective Date
|
||||
|
||||
The licenses granted in Section 2.1 with respect to any Contribution
|
||||
become effective for each Contribution on the date the Contributor first
|
||||
distributes such Contribution.
|
||||
|
||||
2.3. Limitations on Grant Scope
|
||||
|
||||
The licenses granted in this Section 2 are the only rights granted under
|
||||
this License. No additional rights or licenses will be implied from the
|
||||
distribution or licensing of Covered Software under this License.
|
||||
Notwithstanding Section 2.1(b) above, no patent license is granted by a
|
||||
Contributor:
|
||||
|
||||
(a) for any code that a Contributor has removed from Covered Software;
|
||||
or
|
||||
|
||||
(b) for infringements caused by: (i) Your and any other third party's
|
||||
modifications of Covered Software, or (ii) the combination of its
|
||||
Contributions with other software (except as part of its Contributor
|
||||
Version); or
|
||||
|
||||
(c) under Patent Claims infringed by Covered Software in the absence of
|
||||
its Contributions.
|
||||
|
||||
This License does not grant any rights in the trademarks, service marks,
|
||||
or logos of any Contributor (except as may be necessary to comply with
|
||||
the notice requirements in Section 3.4).
|
||||
|
||||
2.4. Subsequent Licenses
|
||||
|
||||
No Contributor makes additional grants as a result of Your choice to
|
||||
distribute the Covered Software under a subsequent version of this
|
||||
License (see Section 10.2) or under the terms of a Secondary License (if
|
||||
permitted under the terms of Section 3.3).
|
||||
|
||||
2.5. Representation
|
||||
|
||||
Each Contributor represents that the Contributor believes its
|
||||
Contributions are its original creation(s) or it has sufficient rights
|
||||
to grant the rights to its Contributions conveyed by this License.
|
||||
|
||||
2.6. Fair Use
|
||||
|
||||
This License is not intended to limit any rights You have under
|
||||
applicable copyright doctrines of fair use, fair dealing, or other
|
||||
equivalents.
|
||||
|
||||
2.7. Conditions
|
||||
|
||||
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
|
||||
in Section 2.1.
|
||||
|
||||
3. Responsibilities
|
||||
-------------------
|
||||
|
||||
3.1. Distribution of Source Form
|
||||
|
||||
All distribution of Covered Software in Source Code Form, including any
|
||||
Modifications that You create or to which You contribute, must be under
|
||||
the terms of this License. You must inform recipients that the Source
|
||||
Code Form of the Covered Software is governed by the terms of this
|
||||
License, and how they can obtain a copy of this License. You may not
|
||||
attempt to alter or restrict the recipients' rights in the Source Code
|
||||
Form.
|
||||
|
||||
3.2. Distribution of Executable Form
|
||||
|
||||
If You distribute Covered Software in Executable Form then:
|
||||
|
||||
(a) such Covered Software must also be made available in Source Code
|
||||
Form, as described in Section 3.1, and You must inform recipients of
|
||||
the Executable Form how they can obtain a copy of such Source Code
|
||||
Form by reasonable means in a timely manner, at a charge no more
|
||||
than the cost of distribution to the recipient; and
|
||||
|
||||
(b) You may distribute such Executable Form under the terms of this
|
||||
License, or sublicense it under different terms, provided that the
|
||||
license for the Executable Form does not attempt to limit or alter
|
||||
the recipients' rights in the Source Code Form under this License.
|
||||
|
||||
3.3. Distribution of a Larger Work
|
||||
|
||||
You may create and distribute a Larger Work under terms of Your choice,
|
||||
provided that You also comply with the requirements of this License for
|
||||
the Covered Software. If the Larger Work is a combination of Covered
|
||||
Software with a work governed by one or more Secondary Licenses, and the
|
||||
Covered Software is not Incompatible With Secondary Licenses, this
|
||||
License permits You to additionally distribute such Covered Software
|
||||
under the terms of such Secondary License(s), so that the recipient of
|
||||
the Larger Work may, at their option, further distribute the Covered
|
||||
Software under the terms of either this License or such Secondary
|
||||
License(s).
|
||||
|
||||
3.4. Notices
|
||||
|
||||
You may not remove or alter the substance of any license notices
|
||||
(including copyright notices, patent notices, disclaimers of warranty,
|
||||
or limitations of liability) contained within the Source Code Form of
|
||||
the Covered Software, except that You may alter any license notices to
|
||||
the extent required to remedy known factual inaccuracies.
|
||||
|
||||
3.5. Application of Additional Terms
|
||||
|
||||
You may choose to offer, and to charge a fee for, warranty, support,
|
||||
indemnity or liability obligations to one or more recipients of Covered
|
||||
Software. However, You may do so only on Your own behalf, and not on
|
||||
behalf of any Contributor. You must make it absolutely clear that any
|
||||
such warranty, support, indemnity, or liability obligation is offered by
|
||||
You alone, and You hereby agree to indemnify every Contributor for any
|
||||
liability incurred by such Contributor as a result of warranty, support,
|
||||
indemnity or liability terms You offer. You may include additional
|
||||
disclaimers of warranty and limitations of liability specific to any
|
||||
jurisdiction.
|
||||
|
||||
4. Inability to Comply Due to Statute or Regulation
|
||||
---------------------------------------------------
|
||||
|
||||
If it is impossible for You to comply with any of the terms of this
|
||||
License with respect to some or all of the Covered Software due to
|
||||
statute, judicial order, or regulation then You must: (a) comply with
|
||||
the terms of this License to the maximum extent possible; and (b)
|
||||
describe the limitations and the code they affect. Such description must
|
||||
be placed in a text file included with all distributions of the Covered
|
||||
Software under this License. Except to the extent prohibited by statute
|
||||
or regulation, such description must be sufficiently detailed for a
|
||||
recipient of ordinary skill to be able to understand it.
|
||||
|
||||
5. Termination
|
||||
--------------
|
||||
|
||||
5.1. The rights granted under this License will terminate automatically
|
||||
if You fail to comply with any of its terms. However, if You become
|
||||
compliant, then the rights granted under this License from a particular
|
||||
Contributor are reinstated (a) provisionally, unless and until such
|
||||
Contributor explicitly and finally terminates Your grants, and (b) on an
|
||||
ongoing basis, if such Contributor fails to notify You of the
|
||||
non-compliance by some reasonable means prior to 60 days after You have
|
||||
come back into compliance. Moreover, Your grants from a particular
|
||||
Contributor are reinstated on an ongoing basis if such Contributor
|
||||
notifies You of the non-compliance by some reasonable means, this is the
|
||||
first time You have received notice of non-compliance with this License
|
||||
from such Contributor, and You become compliant prior to 30 days after
|
||||
Your receipt of the notice.
|
||||
|
||||
5.2. If You initiate litigation against any entity by asserting a patent
|
||||
infringement claim (excluding declaratory judgment actions,
|
||||
counter-claims, and cross-claims) alleging that a Contributor Version
|
||||
directly or indirectly infringes any patent, then the rights granted to
|
||||
You by any and all Contributors for the Covered Software under Section
|
||||
2.1 of this License shall terminate.
|
||||
|
||||
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
|
||||
end user license agreements (excluding distributors and resellers) which
|
||||
have been validly granted by You or Your distributors under this License
|
||||
prior to termination shall survive termination.
|
||||
|
||||
************************************************************************
|
||||
* *
|
||||
* 6. Disclaimer of Warranty *
|
||||
* ------------------------- *
|
||||
* *
|
||||
* Covered Software is provided under this License on an "as is" *
|
||||
* basis, without warranty of any kind, either expressed, implied, or *
|
||||
* statutory, including, without limitation, warranties that the *
|
||||
* Covered Software is free of defects, merchantable, fit for a *
|
||||
* particular purpose or non-infringing. The entire risk as to the *
|
||||
* quality and performance of the Covered Software is with You. *
|
||||
* Should any Covered Software prove defective in any respect, You *
|
||||
* (not any Contributor) assume the cost of any necessary servicing, *
|
||||
* repair, or correction. This disclaimer of warranty constitutes an *
|
||||
* essential part of this License. No use of any Covered Software is *
|
||||
* authorized under this License except under this disclaimer. *
|
||||
* *
|
||||
************************************************************************
|
||||
|
||||
************************************************************************
|
||||
* *
|
||||
* 7. Limitation of Liability *
|
||||
* -------------------------- *
|
||||
* *
|
||||
* Under no circumstances and under no legal theory, whether tort *
|
||||
* (including negligence), contract, or otherwise, shall any *
|
||||
* Contributor, or anyone who distributes Covered Software as *
|
||||
* permitted above, be liable to You for any direct, indirect, *
|
||||
* special, incidental, or consequential damages of any character *
|
||||
* including, without limitation, damages for lost profits, loss of *
|
||||
* goodwill, work stoppage, computer failure or malfunction, or any *
|
||||
* and all other commercial damages or losses, even if such party *
|
||||
* shall have been informed of the possibility of such damages. This *
|
||||
* limitation of liability shall not apply to liability for death or *
|
||||
* personal injury resulting from such party's negligence to the *
|
||||
* extent applicable law prohibits such limitation. Some *
|
||||
* jurisdictions do not allow the exclusion or limitation of *
|
||||
* incidental or consequential damages, so this exclusion and *
|
||||
* limitation may not apply to You. *
|
||||
* *
|
||||
************************************************************************
|
||||
|
||||
8. Litigation
|
||||
-------------
|
||||
|
||||
Any litigation relating to this License may be brought only in the
|
||||
courts of a jurisdiction where the defendant maintains its principal
|
||||
place of business and such litigation shall be governed by laws of that
|
||||
jurisdiction, without reference to its conflict-of-law provisions.
|
||||
Nothing in this Section shall prevent a party's ability to bring
|
||||
cross-claims or counter-claims.
|
||||
|
||||
9. Miscellaneous
|
||||
----------------
|
||||
|
||||
This License represents the complete agreement concerning the subject
|
||||
matter hereof. If any provision of this License is held to be
|
||||
unenforceable, such provision shall be reformed only to the extent
|
||||
necessary to make it enforceable. Any law or regulation which provides
|
||||
that the language of a contract shall be construed against the drafter
|
||||
shall not be used to construe this License against a Contributor.
|
||||
|
||||
10. Versions of the License
|
||||
---------------------------
|
||||
|
||||
10.1. New Versions
|
||||
|
||||
Mozilla Foundation is the license steward. Except as provided in Section
|
||||
10.3, no one other than the license steward has the right to modify or
|
||||
publish new versions of this License. Each version will be given a
|
||||
distinguishing version number.
|
||||
|
||||
10.2. Effect of New Versions
|
||||
|
||||
You may distribute the Covered Software under the terms of the version
|
||||
of the License under which You originally received the Covered Software,
|
||||
or under the terms of any subsequent version published by the license
|
||||
steward.
|
||||
|
||||
10.3. Modified Versions
|
||||
|
||||
If you create software not governed by this License, and you want to
|
||||
create a new license for such software, you may create and use a
|
||||
modified version of this License if you rename the license and remove
|
||||
any references to the name of the license steward (except to note that
|
||||
such modified license differs from this License).
|
||||
|
||||
10.4. Distributing Source Code Form that is Incompatible With Secondary
|
||||
Licenses
|
||||
|
||||
If You choose to distribute Source Code Form that is Incompatible With
|
||||
Secondary Licenses under the terms of this version of the License, the
|
||||
notice described in Exhibit B of this License must be attached.
|
||||
|
||||
Exhibit A - Source Code Form License Notice
|
||||
-------------------------------------------
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public
|
||||
License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
If it is not possible or desirable to put the notice in a particular
|
||||
file, then You may include the notice in a location (such as a LICENSE
|
||||
file in a relevant directory) where a recipient would be likely to look
|
||||
for such a notice.
|
||||
|
||||
You may add additional accurate notices of copyright ownership.
|
||||
|
||||
Exhibit B - "Incompatible With Secondary Licenses" Notice
|
||||
---------------------------------------------------------
|
||||
|
||||
This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||
defined by the Mozilla Public License, v. 2.0.
|
@ -65,15 +65,15 @@ Compile software:
|
||||
lutin -C -P enet-test?build?run
|
||||
|
||||
|
||||
License (APACHE v2.0)
|
||||
License (MPL v2.0)
|
||||
=====================
|
||||
Copyright enet Edouard DUPIN
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
Licensed under the Mozilla Public License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
https://www.mozilla.org/MPL/2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
|
@ -38,16 +38,16 @@ Are there any licensing restrictions? {#enet_mainpage_license_restric
|
||||
ENET is **FREE software** and _all sub-library are FREE and staticly linkable !!!_
|
||||
|
||||
|
||||
License (APACHE-2.0) {#enet_mainpage_license}
|
||||
====================
|
||||
License (MPL v2.0) {#enet_mainpage_license}
|
||||
==================
|
||||
|
||||
Copyright ENET Edouard DUPIN
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
Licensed under the Mozilla Public License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
<http://www.apache.org/licenses/LICENSE-2.0>
|
||||
<https://www.mozilla.org/MPL/2.0>
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
|
@ -1,9 +1,8 @@
|
||||
/** @file
|
||||
* @author Edouard DUPIN
|
||||
* @copyright 2014, Edouard DUPIN, all right reserved
|
||||
* @license APACHE v2.0 (see license file)
|
||||
* @license MPL v2.0 (see license file)
|
||||
*/
|
||||
|
||||
#include <enet/debug.hpp>
|
||||
#include <enet/Ftp.hpp>
|
||||
#include <cstring>
|
||||
|
@ -1,7 +1,7 @@
|
||||
/** @file
|
||||
* @author Edouard DUPIN
|
||||
* @copyright 2014, Edouard DUPIN, all right reserved
|
||||
* @license APACHE v2.0 (see license file)
|
||||
* @license MPL v2.0 (see license file)
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
|
363
enet/Http.cpp
363
enet/Http.cpp
@ -1,24 +1,29 @@
|
||||
/** @file
|
||||
* @author Edouard DUPIN
|
||||
* @copyright 2014, Edouard DUPIN, all right reserved
|
||||
* @license APACHE v2.0 (see license file)
|
||||
* @license MPL v2.0 (see license file)
|
||||
*/
|
||||
|
||||
#include <enet/debug.hpp>
|
||||
#include <enet/Http.hpp>
|
||||
#include <map>
|
||||
#include <etk/Map.hpp>
|
||||
#include <etk/Stream.hpp>
|
||||
#include <etk/stdTools.hpp>
|
||||
#include <cstring>
|
||||
#include <enet/TcpClient.hpp>
|
||||
#include <enet/pourcentEncoding.hpp>
|
||||
extern "C" {
|
||||
#include <string.h>
|
||||
}
|
||||
|
||||
|
||||
static std::string escapeChar(const std::string& _value) {
|
||||
static etk::String escapeChar(const etk::String& _value) {
|
||||
return _value;
|
||||
}
|
||||
static std::string unEscapeChar(const std::string& _value) {
|
||||
static etk::String unEscapeChar(const etk::String& _value) {
|
||||
return _value;
|
||||
}
|
||||
static std::string removeStartAndStopSpace(const std::string& _value) {
|
||||
std::string out;
|
||||
static etk::String removeStartAndStopSpace(const etk::String& _value) {
|
||||
etk::String out;
|
||||
out.reserve(_value.size());
|
||||
bool findSpace = false;
|
||||
for (auto &it : _value) {
|
||||
@ -36,64 +41,71 @@ static std::string removeStartAndStopSpace(const std::string& _value) {
|
||||
return out;
|
||||
}
|
||||
|
||||
static std::map<enet::HTTPAnswerCode, std::string> protocolName = {
|
||||
{enet::HTTPAnswerCode::c100_continue, "Continue"},
|
||||
{enet::HTTPAnswerCode::c101_switchingProtocols, "Switching Protocols"},
|
||||
{enet::HTTPAnswerCode::c103_checkpoint, "Checkpoint"},
|
||||
{enet::HTTPAnswerCode::c200_ok, "OK"},
|
||||
{enet::HTTPAnswerCode::c201_created, "Created"},
|
||||
{enet::HTTPAnswerCode::c202_accepted, "Accepted"},
|
||||
{enet::HTTPAnswerCode::c203_nonAuthoritativeInformation, "Non-Authoritative Information"},
|
||||
{enet::HTTPAnswerCode::c204_noContent, "No Content"},
|
||||
{enet::HTTPAnswerCode::c205_resetContent, "Reset Content"},
|
||||
{enet::HTTPAnswerCode::c206_partialContent, "Partial Content"},
|
||||
{enet::HTTPAnswerCode::c300_multipleChoices, "Multiple Choices"},
|
||||
{enet::HTTPAnswerCode::c301_movedPermanently, "Moved Permanently"},
|
||||
{enet::HTTPAnswerCode::c302_found, "Found"},
|
||||
{enet::HTTPAnswerCode::c303_seeOther, "See Other"},
|
||||
{enet::HTTPAnswerCode::c304_notModified, "Not Modified"},
|
||||
{enet::HTTPAnswerCode::c306_switchProxy, "Switch Proxy"},
|
||||
{enet::HTTPAnswerCode::c307_temporaryRedirect, "Temporary Redirect"},
|
||||
{enet::HTTPAnswerCode::c308_resumeIncomplete, "Resume Incomplete"},
|
||||
{enet::HTTPAnswerCode::c400_badRequest, "Bad Request"},
|
||||
{enet::HTTPAnswerCode::c401_unauthorized, "Unauthorized"},
|
||||
{enet::HTTPAnswerCode::c402_paymentRequired, "Payment Required"},
|
||||
{enet::HTTPAnswerCode::c403_forbidden, "Forbidden"},
|
||||
{enet::HTTPAnswerCode::c404_notFound, "Not Found"},
|
||||
{enet::HTTPAnswerCode::c405_methodNotAllowed, "Method Not Allowed"},
|
||||
{enet::HTTPAnswerCode::c406_notAcceptable, "Not Acceptable"},
|
||||
{enet::HTTPAnswerCode::c407_proxyAuthenticationRequired, "Proxy Authentication Required"},
|
||||
{enet::HTTPAnswerCode::c408_requestTimeout, "Request Timeout"},
|
||||
{enet::HTTPAnswerCode::c409_conflict, "Conflict"},
|
||||
{enet::HTTPAnswerCode::c410_gone, "Gone"},
|
||||
{enet::HTTPAnswerCode::c411_lengthRequired, "Length Required"},
|
||||
{enet::HTTPAnswerCode::c412_preconditionFailed, "Precondition Failed"},
|
||||
{enet::HTTPAnswerCode::c413_requestEntityTooLarge, "Request Entity Too Large"},
|
||||
{enet::HTTPAnswerCode::c414_requestURITooLong, "Request-URI Too Long"},
|
||||
{enet::HTTPAnswerCode::c415_unsupportedMediaType, "Unsupported Media Type"},
|
||||
{enet::HTTPAnswerCode::c416_requestedRangeNotSatisfiable, "Requested Range Not Satisfiable"},
|
||||
{enet::HTTPAnswerCode::c417_expectationFailed, "Expectation Failed"},
|
||||
{enet::HTTPAnswerCode::c500_internalServerError, "Internal Server Error"},
|
||||
{enet::HTTPAnswerCode::c501_notImplemented, "Not Implemented"},
|
||||
{enet::HTTPAnswerCode::c502_badGateway, "Bad Gateway"},
|
||||
{enet::HTTPAnswerCode::c503_serviceUnavailable, "Service Unavailable"},
|
||||
{enet::HTTPAnswerCode::c504_gatewayTimeout, "Gateway Timeout"},
|
||||
{enet::HTTPAnswerCode::c505_httpVersionNotSupported, "HTTP Version Not Supported"},
|
||||
{enet::HTTPAnswerCode::c511_networkAuthenticationRequired, "Network Authentication Required"}
|
||||
static const etk::Map<enet::HTTPAnswerCode, etk::String>& getProtocolName() {
|
||||
static etk::Map<enet::HTTPAnswerCode, etk::String> protocolName;
|
||||
static bool isInit = false;
|
||||
if (isInit == true) {
|
||||
return protocolName;
|
||||
}
|
||||
isInit = true;
|
||||
protocolName.add(enet::HTTPAnswerCode::c100_continue, "Continue");
|
||||
protocolName.add(enet::HTTPAnswerCode::c101_switchingProtocols, "Switching Protocols");
|
||||
protocolName.add(enet::HTTPAnswerCode::c103_checkpoint, "Checkpoint");
|
||||
protocolName.add(enet::HTTPAnswerCode::c200_ok, "OK");
|
||||
protocolName.add(enet::HTTPAnswerCode::c201_created, "Created");
|
||||
protocolName.add(enet::HTTPAnswerCode::c202_accepted, "Accepted");
|
||||
protocolName.add(enet::HTTPAnswerCode::c203_nonAuthoritativeInformation, "Non-Authoritative Information");
|
||||
protocolName.add(enet::HTTPAnswerCode::c204_noContent, "No Content");
|
||||
protocolName.add(enet::HTTPAnswerCode::c205_resetContent, "Reset Content");
|
||||
protocolName.add(enet::HTTPAnswerCode::c206_partialContent, "Partial Content");
|
||||
protocolName.add(enet::HTTPAnswerCode::c300_multipleChoices, "Multiple Choices");
|
||||
protocolName.add(enet::HTTPAnswerCode::c301_movedPermanently, "Moved Permanently");
|
||||
protocolName.add(enet::HTTPAnswerCode::c302_found, "Found");
|
||||
protocolName.add(enet::HTTPAnswerCode::c303_seeOther, "See Other");
|
||||
protocolName.add(enet::HTTPAnswerCode::c304_notModified, "Not Modified");
|
||||
protocolName.add(enet::HTTPAnswerCode::c306_switchProxy, "Switch Proxy");
|
||||
protocolName.add(enet::HTTPAnswerCode::c307_temporaryRedirect, "Temporary Redirect");
|
||||
protocolName.add(enet::HTTPAnswerCode::c308_resumeIncomplete, "Resume Incomplete");
|
||||
protocolName.add(enet::HTTPAnswerCode::c400_badRequest, "Bad Request");
|
||||
protocolName.add(enet::HTTPAnswerCode::c401_unauthorized, "Unauthorized");
|
||||
protocolName.add(enet::HTTPAnswerCode::c402_paymentRequired, "Payment Required");
|
||||
protocolName.add(enet::HTTPAnswerCode::c403_forbidden, "Forbidden");
|
||||
protocolName.add(enet::HTTPAnswerCode::c404_notFound, "Not Found");
|
||||
protocolName.add(enet::HTTPAnswerCode::c405_methodNotAllowed, "Method Not Allowed");
|
||||
protocolName.add(enet::HTTPAnswerCode::c406_notAcceptable, "Not Acceptable");
|
||||
protocolName.add(enet::HTTPAnswerCode::c407_proxyAuthenticationRequired, "Proxy Authentication Required");
|
||||
protocolName.add(enet::HTTPAnswerCode::c408_requestTimeout, "Request Timeout");
|
||||
protocolName.add(enet::HTTPAnswerCode::c409_conflict, "Conflict");
|
||||
protocolName.add(enet::HTTPAnswerCode::c410_gone, "Gone");
|
||||
protocolName.add(enet::HTTPAnswerCode::c411_lengthRequired, "Length Required");
|
||||
protocolName.add(enet::HTTPAnswerCode::c412_preconditionFailed, "Precondition Failed");
|
||||
protocolName.add(enet::HTTPAnswerCode::c413_requestEntityTooLarge, "Request Entity Too Large");
|
||||
protocolName.add(enet::HTTPAnswerCode::c414_requestURITooLong, "Request-URI Too Long");
|
||||
protocolName.add(enet::HTTPAnswerCode::c415_unsupportedMediaType, "Unsupported Media Type");
|
||||
protocolName.add(enet::HTTPAnswerCode::c416_requestedRangeNotSatisfiable, "Requested Range Not Satisfiable");
|
||||
protocolName.add(enet::HTTPAnswerCode::c417_expectationFailed, "Expectation Failed");
|
||||
protocolName.add(enet::HTTPAnswerCode::c500_internalServerError, "Internal Server Error");
|
||||
protocolName.add(enet::HTTPAnswerCode::c501_notImplemented, "Not Implemented");
|
||||
protocolName.add(enet::HTTPAnswerCode::c502_badGateway, "Bad Gateway");
|
||||
protocolName.add(enet::HTTPAnswerCode::c503_serviceUnavailable, "Service Unavailable");
|
||||
protocolName.add(enet::HTTPAnswerCode::c504_gatewayTimeout, "Gateway Timeout");
|
||||
protocolName.add(enet::HTTPAnswerCode::c505_httpVersionNotSupported, "HTTP Version Not Supported");
|
||||
protocolName.add(enet::HTTPAnswerCode::c511_networkAuthenticationRequired, "Network Authentication Required");
|
||||
return protocolName;
|
||||
};
|
||||
|
||||
|
||||
|
||||
static std::map<int32_t, std::string> getErrorList() {
|
||||
static std::map<int32_t, std::string> g_list;
|
||||
static etk::Map<int32_t, etk::String> getErrorList() {
|
||||
static etk::Map<int32_t, etk::String> g_list;
|
||||
return g_list;
|
||||
}
|
||||
|
||||
enet::Http::Http(enet::Tcp _connection, bool _isServer) :
|
||||
m_isServer(_isServer),
|
||||
m_connection(std::move(_connection)),
|
||||
m_connection(etk::move(_connection)),
|
||||
m_headerIsSend(false),
|
||||
m_thread(nullptr),
|
||||
m_thread(null),
|
||||
m_threadRunning(false) {
|
||||
//setSendHeaderProperties("User-Agent", "e-net (ewol network interface)");
|
||||
/*
|
||||
@ -116,16 +128,18 @@ void enet::Http::threadCallback() {
|
||||
// READ section data:
|
||||
if (m_headerIsSend == false) {
|
||||
getHeader();
|
||||
m_headerIsSend = true;
|
||||
if (m_headerIsSend == false) {
|
||||
continue;
|
||||
}
|
||||
if (m_observerRaw != nullptr) {
|
||||
}
|
||||
if (m_observerRaw != null) {
|
||||
m_observerRaw(m_connection);
|
||||
} else {
|
||||
m_temporaryBuffer.resize(67000);
|
||||
int32_t len = m_connection.read(&m_temporaryBuffer[0], m_temporaryBuffer.size());
|
||||
if (len > 0) {
|
||||
ENET_INFO("Call client with datas ...");
|
||||
if (m_observer != nullptr) {
|
||||
if (m_observer != null) {
|
||||
m_observer(m_temporaryBuffer);
|
||||
}
|
||||
}
|
||||
@ -135,19 +149,28 @@ void enet::Http::threadCallback() {
|
||||
ENET_DEBUG("End of thread HTTP");
|
||||
}
|
||||
|
||||
void enet::Http::redirectTo(const etk::String& _addressRedirect, bool _inThreadStop) {
|
||||
if (m_isServer == true) {
|
||||
ENET_ERROR("Request a redirect in Server mode ==> not authorised");
|
||||
return;
|
||||
}
|
||||
stop(_inThreadStop);
|
||||
m_headerIsSend = false;
|
||||
m_connection = etk::move(connectTcpClient(_addressRedirect, 5, echrono::seconds(1)));
|
||||
}
|
||||
|
||||
void enet::Http::start() {
|
||||
ENET_DEBUG("connect [START]");
|
||||
m_threadRunning = true;
|
||||
m_thread = new std::thread([&](void *){ this->threadCallback();}, nullptr);
|
||||
if (m_thread == nullptr) {
|
||||
m_thread = ETK_NEW(ethread::Thread, [&](){ threadCallback();});
|
||||
if (m_thread == null) {
|
||||
m_threadRunning = false;
|
||||
ENET_ERROR("creating callback thread!");
|
||||
return;
|
||||
}
|
||||
while ( m_threadRunning == true
|
||||
&& m_connection.getConnectionStatus() != enet::Tcp::status::link) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
||||
ethread::sleepMilliSeconds((50));
|
||||
}
|
||||
//ethread::setPriority(*m_receiveThread, -6);
|
||||
ENET_DEBUG("connect [STOP]");
|
||||
@ -166,21 +189,23 @@ void enet::Http::stop(bool _inThreadStop){
|
||||
m_connection.unlink();
|
||||
}
|
||||
if (_inThreadStop == false) {
|
||||
if (m_thread != nullptr) {
|
||||
if (m_thread != null) {
|
||||
ENET_DEBUG("wait join Thread ...");
|
||||
m_thread->join();
|
||||
delete m_thread;
|
||||
m_thread = nullptr;
|
||||
ENET_DEBUG("wait join Thread (done)");
|
||||
ETK_DELETE(ethread::Thread, m_thread);
|
||||
m_thread = null;
|
||||
}
|
||||
}
|
||||
ENET_DEBUG("disconnect [STOP]");
|
||||
}
|
||||
/*
|
||||
void enet::Http::writeAnswerHeader(enum enet::HTTPAnswerCode _value) {
|
||||
std::string out;
|
||||
etk::String out;
|
||||
out = "HTTP/1.1 ";
|
||||
out += etk::to_string(int32_t(_value));
|
||||
auto it = protocolName.find(_value);
|
||||
if (it == protocolName.end() ) {
|
||||
out += etk::toString(int32_t(_value));
|
||||
auto it = getProtocolName().find(_value);
|
||||
if (it == getProtocolName().end() ) {
|
||||
out += " ???";
|
||||
} else {
|
||||
out += " " + it->second;
|
||||
@ -192,10 +217,10 @@ void enet::Http::writeAnswerHeader(enum enet::HTTPAnswerCode _value) {
|
||||
*/
|
||||
namespace etk {
|
||||
template <>
|
||||
bool from_string<enum enet::HTTPAnswerCode>(enum enet::HTTPAnswerCode& _variableRet, const std::string& _value) {
|
||||
bool from_string<enum enet::HTTPAnswerCode>(enum enet::HTTPAnswerCode& _variableRet, const etk::String& _value) {
|
||||
_variableRet = enet::HTTPAnswerCode::c000_unknow;
|
||||
for (auto &it : protocolName) {
|
||||
if (etk::to_string(int32_t(it.first)) == _value) {
|
||||
for (auto &it : getProtocolName()) {
|
||||
if (etk::toString(int32_t(it.first)) == _value) {
|
||||
_variableRet = it.first;
|
||||
return true;
|
||||
}
|
||||
@ -203,11 +228,11 @@ namespace etk {
|
||||
return false;
|
||||
}
|
||||
template <>
|
||||
std::string to_string<enum enet::HTTPAnswerCode>(const enum enet::HTTPAnswerCode& _value) {
|
||||
return etk::to_string(int32_t(_value));
|
||||
etk::String toString<enum enet::HTTPAnswerCode>(const enum enet::HTTPAnswerCode& _value) {
|
||||
return etk::toString(int32_t(_value));
|
||||
}
|
||||
template <>
|
||||
bool from_string<enum enet::HTTPReqType>(enum enet::HTTPReqType& _variableRet, const std::string& _value) {
|
||||
bool from_string<enum enet::HTTPReqType>(enum enet::HTTPReqType& _variableRet, const etk::String& _value) {
|
||||
_variableRet = enet::HTTPReqType::HTTP_GET;
|
||||
if (_value == "GET") {
|
||||
_variableRet = enet::HTTPReqType::HTTP_GET;
|
||||
@ -224,22 +249,26 @@ namespace etk {
|
||||
} else if (_value == "DELETE") {
|
||||
_variableRet = enet::HTTPReqType::HTTP_DELETE;
|
||||
return true;
|
||||
} else if (_value == "OPTIONS") {
|
||||
_variableRet = enet::HTTPReqType::HTTP_OPTIONS;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
template <>
|
||||
std::string to_string<enum enet::HTTPReqType>(const enum enet::HTTPReqType& _value) {
|
||||
etk::String toString<enum enet::HTTPReqType>(const enum enet::HTTPReqType& _value) {
|
||||
switch (_value) {
|
||||
case enet::HTTPReqType::HTTP_GET: return "GET";
|
||||
case enet::HTTPReqType::HTTP_HEAD: return "HEAD";
|
||||
case enet::HTTPReqType::HTTP_POST: return "POST";
|
||||
case enet::HTTPReqType::HTTP_PUT: return "PUT";
|
||||
case enet::HTTPReqType::HTTP_DELETE: return "DELETE";
|
||||
case enet::HTTPReqType::HTTP_OPTIONS: return "OPTIONS";
|
||||
}
|
||||
return "UNKNOW";
|
||||
}
|
||||
template <>
|
||||
bool from_string<enum enet::HTTPProtocol>(enum enet::HTTPProtocol& _variableRet, const std::string& _value) {
|
||||
bool from_string<enum enet::HTTPProtocol>(enum enet::HTTPProtocol& _variableRet, const etk::String& _value) {
|
||||
_variableRet = enet::HTTPProtocol::http_0_1;
|
||||
if (_value == "HTTP/0.1") { _variableRet = enet::HTTPProtocol::http_0_1; return true; }
|
||||
if (_value == "HTTP/0.2") { _variableRet = enet::HTTPProtocol::http_0_2; return true; }
|
||||
@ -287,7 +316,7 @@ namespace etk {
|
||||
return false;
|
||||
}
|
||||
template <>
|
||||
std::string to_string<enum enet::HTTPProtocol>(const enum enet::HTTPProtocol& _value) {
|
||||
etk::String toString<enum enet::HTTPProtocol>(const enum enet::HTTPProtocol& _value) {
|
||||
switch (_value) {
|
||||
case enet::HTTPProtocol::http_0_1: return "HTTP/0.1";
|
||||
case enet::HTTPProtocol::http_0_2: return "HTTP/0.2";
|
||||
@ -340,31 +369,43 @@ namespace etk {
|
||||
|
||||
void enet::Http::setRequestHeader(const enet::HttpRequest& _req) {
|
||||
m_requestHeader = _req;
|
||||
if (m_isServer == true) {
|
||||
if (m_requestHeader.getKey("Server") == "") {
|
||||
m_requestHeader.setKey("Server", "e-net (ewol network interface)");
|
||||
}
|
||||
} else {
|
||||
if (m_requestHeader.getKey("User-Agent") == "") {
|
||||
m_requestHeader.setKey("User-Agent", "e-net (ewol network interface)");
|
||||
}
|
||||
std::string value = m_requestHeader.generate();
|
||||
}
|
||||
etk::String value = m_requestHeader.generate();
|
||||
write(value, false);
|
||||
}
|
||||
|
||||
void enet::Http::setAnswerHeader(const enet::HttpAnswer& _req) {
|
||||
m_answerHeader = _req;
|
||||
if (m_isServer == true) {
|
||||
if (m_requestHeader.getKey("Server") == "") {
|
||||
m_requestHeader.setKey("Server", "e-net (ewol network interface)");
|
||||
}
|
||||
} else {
|
||||
if (m_requestHeader.getKey("User-Agent") == "") {
|
||||
m_requestHeader.setKey("User-Agent", "e-net (ewol network interface)");
|
||||
}
|
||||
std::string value = m_answerHeader.generate();
|
||||
}
|
||||
etk::String value = m_answerHeader.generate();
|
||||
write(value, false);
|
||||
}
|
||||
|
||||
void enet::Http::getHeader() {
|
||||
ENET_VERBOSE("Read HTTP Header [START]");
|
||||
bool headerEnded = false;
|
||||
std::string header;
|
||||
etk::String header;
|
||||
while (m_connection.getConnectionStatus() == enet::Tcp::status::link) {
|
||||
char type;
|
||||
char type = '?';
|
||||
int32_t len = m_connection.read(&type, 1);
|
||||
if (len == 0) {
|
||||
std::this_thread::sleep_for(std::chrono::microseconds(1));
|
||||
ethread::sleepMilliSeconds(1);
|
||||
continue;
|
||||
}
|
||||
header += type;
|
||||
@ -394,7 +435,7 @@ void enet::Http::getHeader() {
|
||||
ENET_VERBOSE("Read HTTP Header [STOP] : '" << header << "'");
|
||||
m_headerIsSend = true;
|
||||
// parse header :
|
||||
std::vector<std::string> list = etk::split(header, '\n');
|
||||
etk::Vector<etk::String> list = etk::split(header, '\n');
|
||||
for (auto &it : list) {
|
||||
if ( it.size()>0
|
||||
&& it[it.size()-1] == '\r') {
|
||||
@ -402,7 +443,7 @@ void enet::Http::getHeader() {
|
||||
}
|
||||
}
|
||||
//parse first element:
|
||||
std::vector<std::string> listLineOne = etk::split(list[0], ' ');
|
||||
etk::Vector<etk::String> listLineOne = etk::split(list[0], ' ');
|
||||
if (listLineOne.size() < 2) {
|
||||
ENET_ERROR("can not parse answear : " << listLineOne);
|
||||
// answer bad request and close connection ...
|
||||
@ -414,7 +455,8 @@ void enet::Http::getHeader() {
|
||||
|| listLineOne[0] == "POST"
|
||||
|| listLineOne[0] == "HEAD"
|
||||
|| listLineOne[0] == "DELETE"
|
||||
|| listLineOne[0] == "PUT" ) ) {
|
||||
|| listLineOne[0] == "PUT"
|
||||
|| listLineOne[0] == "OPTIONS" ) ) {
|
||||
// HTTP CALL
|
||||
if (m_isServer == false) {
|
||||
// can not have call in client mode
|
||||
@ -430,7 +472,14 @@ void enet::Http::getHeader() {
|
||||
etk::from_string(valueType, listLineOne[0]);
|
||||
m_requestHeader.setType(valueType);
|
||||
// get URI:
|
||||
etk::String basicUri = listLineOne[1];
|
||||
size_t pos = basicUri.find('?');
|
||||
if (pos == etk::String::npos) {
|
||||
m_requestHeader.setUri(listLineOne[1]);
|
||||
} else {
|
||||
m_requestHeader.setUri(basicUri.extract(0, pos));
|
||||
m_requestHeader.setQuery(pourcentUriDecode(basicUri.extract(pos+1)));
|
||||
}
|
||||
// Get http version:
|
||||
enum enet::HTTPProtocol valueProtocol;
|
||||
etk::from_string(valueProtocol, listLineOne[2]);
|
||||
@ -457,7 +506,7 @@ void enet::Http::getHeader() {
|
||||
m_answerHeader.setErrorCode(valueErrorCode);
|
||||
|
||||
// get comment:
|
||||
std::string comment;
|
||||
etk::String comment;
|
||||
for (size_t iii=2; iii<listLineOne.size(); ++iii) {
|
||||
if (comment.size() != 0) {
|
||||
comment += " ";
|
||||
@ -476,13 +525,13 @@ void enet::Http::getHeader() {
|
||||
}
|
||||
for (size_t iii=1; iii<list.size(); ++iii) {
|
||||
size_t found = list[iii].find(":");
|
||||
if (found == std::string::npos) {
|
||||
if (found == etk::String::npos) {
|
||||
// nothing
|
||||
continue;
|
||||
}
|
||||
std::string key = unEscapeChar(std::string(list[iii], 0, found));
|
||||
etk::String key = unEscapeChar(etk::String(list[iii], 0, found));
|
||||
key = removeStartAndStopSpace(key);
|
||||
std::string value = unEscapeChar(std::string(list[iii], found+2));
|
||||
etk::String value = unEscapeChar(etk::String(list[iii], found+2));
|
||||
value = removeStartAndStopSpace(value);
|
||||
ENET_VERBOSE("header : key='" << key << "' value='" << value << "'");
|
||||
if (m_isServer == false) {
|
||||
@ -496,12 +545,13 @@ void enet::Http::getHeader() {
|
||||
m_connection.unlink();
|
||||
}
|
||||
}
|
||||
m_headerIsSend = true;
|
||||
if (m_isServer == false) {
|
||||
if (m_observerAnswer != nullptr) {
|
||||
if (m_observerAnswer != null) {
|
||||
m_observerAnswer(m_answerHeader);
|
||||
}
|
||||
} else {
|
||||
if (m_observerRequest != nullptr) {
|
||||
if (m_observerRequest != null) {
|
||||
m_observerRequest(m_requestHeader);
|
||||
}
|
||||
}
|
||||
@ -509,9 +559,9 @@ void enet::Http::getHeader() {
|
||||
|
||||
|
||||
/*
|
||||
bool enet::Http::get(const std::string& _address) {
|
||||
bool enet::Http::get(const etk::String& _address) {
|
||||
m_header.m_map.clear();
|
||||
std::string req = "GET http://" + m_connection.getName();
|
||||
etk::String req = "GET http://" + m_connection.getName();
|
||||
if (_address != "") {
|
||||
req += "/";
|
||||
req += _address;
|
||||
@ -536,10 +586,10 @@ bool enet::Http::get(const std::string& _address) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool enet::Http::post(const std::string& _address, const std::map<std::string, std::string>& _values) {
|
||||
bool enet::Http::post(const etk::String& _address, const etk::Map<etk::String, etk::String>& _values) {
|
||||
m_header.m_map.clear();
|
||||
// First create body :
|
||||
std::string body;
|
||||
etk::String body;
|
||||
for (auto &it : _values) {
|
||||
if (body.size() > 0) {
|
||||
body += "&";
|
||||
@ -549,16 +599,16 @@ bool enet::Http::post(const std::string& _address, const std::map<std::string, s
|
||||
return post(_address, "application/x-www-form-urlencoded", body);
|
||||
}
|
||||
|
||||
bool enet::Http::post(const std::string& _address, const std::string& _contentType, const std::string& _data) {
|
||||
bool enet::Http::post(const etk::String& _address, const etk::String& _contentType, const etk::String& _data) {
|
||||
m_header.m_map.clear();
|
||||
std::string req = "POST http://" + m_connection.getName();
|
||||
etk::String req = "POST http://" + m_connection.getName();
|
||||
if (_address != "") {
|
||||
req += "/";
|
||||
req += _address;
|
||||
}
|
||||
req += " HTTP/1.0\n";
|
||||
setSendHeaderProperties("Content-Type", _contentType);
|
||||
setSendHeaderProperties("Content-Length", etk::to_string(_data.size()));
|
||||
setSendHeaderProperties("Content-Length", etk::toString(_data.size()));
|
||||
// add header properties :
|
||||
for (auto &it : m_header.m_map) {
|
||||
req += escapeChar(it.first) + ": " + escapeChar(it.second) + "\r\n";
|
||||
@ -583,23 +633,23 @@ int32_t enet::Http::write(const void* _data, int32_t _len) {
|
||||
}
|
||||
|
||||
|
||||
void enet::HttpHeader::setKey(const std::string& _key, const std::string& _value) {
|
||||
void enet::HttpHeader::setKey(const etk::String& _key, const etk::String& _value) {
|
||||
auto it = m_map.find(_key);
|
||||
if (it == m_map.end()) {
|
||||
m_map.insert(make_pair(_key, _value));
|
||||
m_map.add(_key, _value);
|
||||
} else {
|
||||
it->second = _value;
|
||||
}
|
||||
}
|
||||
|
||||
void enet::HttpHeader::rmKey(const std::string& _key) {
|
||||
void enet::HttpHeader::rmKey(const etk::String& _key) {
|
||||
auto it = m_map.find(_key);
|
||||
if (it != m_map.end()) {
|
||||
m_map.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
std::string enet::HttpHeader::getKey(const std::string& _key) const {
|
||||
etk::String enet::HttpHeader::getKey(const etk::String& _key) const {
|
||||
auto it = m_map.find(_key);
|
||||
if (it != m_map.end()) {
|
||||
return it->second;
|
||||
@ -607,17 +657,64 @@ std::string enet::HttpHeader::getKey(const std::string& _key) const {
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string enet::HttpHeader::generateKeys() const {
|
||||
std::string out;
|
||||
bool enet::HttpHeader::existKey(const etk::String& _key) const {
|
||||
auto it = m_map.find(_key);
|
||||
if (it != m_map.end()) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
etk::String enet::HttpHeader::generateKeys() const {
|
||||
etk::String out;
|
||||
for (auto &it : m_map) {
|
||||
if ( it.first != ""
|
||||
&& it.second != "") {
|
||||
out += escapeChar(it.first) + " : " + escapeChar(it.second) + "\r\n";
|
||||
out += escapeChar(it.first) + ": " + escapeChar(it.second) + "\r\n";
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
void enet::HttpHeader::setQuery(const etk::Map<etk::String, etk::String>& _value) {
|
||||
m_query = _value;
|
||||
}
|
||||
void enet::HttpHeader::setQueryKey(const etk::String& _key, const etk::String& _value) {
|
||||
auto it = m_query.find(_key);
|
||||
if (it == m_query.end()) {
|
||||
m_query.add(_key, _value);
|
||||
} else {
|
||||
it->second = _value;
|
||||
}
|
||||
}
|
||||
|
||||
void enet::HttpHeader::rmQueryKey(const etk::String& _key) {
|
||||
auto it = m_query.find(_key);
|
||||
if (it != m_query.end()) {
|
||||
m_query.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
etk::String enet::HttpHeader::getQueryKey(const etk::String& _key) const {
|
||||
auto it = m_query.find(_key);
|
||||
if (it != m_query.end()) {
|
||||
return it->second;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
bool enet::HttpHeader::existQueryKey(const etk::String& _key) const {
|
||||
auto it = m_query.find(_key);
|
||||
if (it != m_query.end()) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
etk::String enet::HttpHeader::generateQueryKeys() const {
|
||||
return enet::pourcentUriEncode(m_query);
|
||||
}
|
||||
|
||||
enet::HttpHeader::HttpHeader():
|
||||
m_protocol(enet::HTTPProtocol::http_1_0) {
|
||||
|
||||
@ -627,7 +724,7 @@ enet::HttpHeader::HttpHeader():
|
||||
// -----------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
enet::HttpAnswer::HttpAnswer(enum HTTPAnswerCode _code, const std::string& _help):
|
||||
enet::HttpAnswer::HttpAnswer(enum HTTPAnswerCode _code, const etk::String& _help):
|
||||
m_what(_code),
|
||||
m_helpMessage(_help) {
|
||||
|
||||
@ -645,19 +742,26 @@ void enet::HttpAnswer::display() const {
|
||||
ENET_PRINT(" '" + it.first + "' = '" + it.second + "'");
|
||||
}
|
||||
}
|
||||
ENET_PRINT(" query:");
|
||||
for (auto &it : m_query) {
|
||||
if ( it.first != ""
|
||||
&& it.second != "") {
|
||||
ENET_PRINT(" '" + it.first + "' = '" + it.second + "'");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string enet::HttpAnswer::generate() const {
|
||||
std::string out;
|
||||
out += etk::to_string(m_protocol);
|
||||
etk::String enet::HttpAnswer::generate() const {
|
||||
etk::String out;
|
||||
out += etk::toString(m_protocol);
|
||||
out += " ";
|
||||
out += etk::to_string(int32_t(m_what));
|
||||
out += etk::toString(int32_t(m_what));
|
||||
out += " ";
|
||||
if (m_helpMessage != "") {
|
||||
out += escapeChar(m_helpMessage);
|
||||
} else {
|
||||
auto it = protocolName.find(m_what);
|
||||
if (it != protocolName.end()) {
|
||||
auto it = getProtocolName().find(m_what);
|
||||
if (it != getProtocolName().end()) {
|
||||
out += escapeChar(it->second);
|
||||
} else {
|
||||
out += "???";
|
||||
@ -669,12 +773,12 @@ std::string enet::HttpAnswer::generate() const {
|
||||
return out;
|
||||
}
|
||||
enet::HttpServer::HttpServer(enet::Tcp _connection) :
|
||||
enet::Http(std::move(_connection), true) {
|
||||
enet::Http(etk::move(_connection), true) {
|
||||
|
||||
}
|
||||
|
||||
enet::HttpClient::HttpClient(enet::Tcp _connection) :
|
||||
enet::Http(std::move(_connection), false) {
|
||||
enet::Http(etk::move(_connection), false) {
|
||||
|
||||
}
|
||||
// -----------------------------------------------------------------------------------------
|
||||
@ -697,15 +801,26 @@ void enet::HttpRequest::display() const {
|
||||
ENET_PRINT(" '" + it.first + "' = '" + it.second + "'");
|
||||
}
|
||||
}
|
||||
ENET_PRINT(" query:");
|
||||
for (auto &it : m_query) {
|
||||
if ( it.first != ""
|
||||
&& it.second != "") {
|
||||
ENET_PRINT(" '" + it.first + "' = '" + it.second + "'");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string enet::HttpRequest::generate() const {
|
||||
std::string out;
|
||||
out += etk::to_string(m_req);
|
||||
etk::String enet::HttpRequest::generate() const {
|
||||
etk::String out;
|
||||
out += etk::toString(m_req);
|
||||
out += " ";
|
||||
out += m_uri;
|
||||
etk::String querryData = generateQueryKeys();
|
||||
if (querryData.empty() != 0) {
|
||||
out += "?" + querryData;
|
||||
}
|
||||
out += " ";
|
||||
out += etk::to_string(m_protocol);
|
||||
out += etk::toString(m_protocol);
|
||||
out += "\r\n";
|
||||
out += generateKeys();
|
||||
out += "\r\n";
|
||||
@ -713,17 +828,17 @@ std::string enet::HttpRequest::generate() const {
|
||||
}
|
||||
|
||||
|
||||
std::ostream& enet::operator <<(std::ostream& _os, enum enet::HTTPProtocol _obj) {
|
||||
_os << "enet::HTTPProtocol::" <<etk::to_string(_obj);
|
||||
etk::Stream& enet::operator <<(etk::Stream& _os, enum enet::HTTPProtocol _obj) {
|
||||
_os << "enet::HTTPProtocol::" <<etk::toString(_obj);
|
||||
return _os;
|
||||
}
|
||||
|
||||
std::ostream& enet::operator <<(std::ostream& _os, enum enet::HTTPAnswerCode _obj) {
|
||||
_os << "enet::HTTPAnswerCode::" << etk::to_string(_obj);
|
||||
etk::Stream& enet::operator <<(etk::Stream& _os, enum enet::HTTPAnswerCode _obj) {
|
||||
_os << "enet::HTTPAnswerCode::" << etk::toString(_obj);
|
||||
return _os;
|
||||
}
|
||||
|
||||
std::ostream& enet::operator <<(std::ostream& _os, enum enet::HTTPReqType _obj) {
|
||||
_os << "enet::HTTPReqType::" << etk::to_string(_obj);
|
||||
etk::Stream& enet::operator <<(etk::Stream& _os, enum enet::HTTPReqType _obj) {
|
||||
_os << "enet::HTTPReqType::" << etk::toString(_obj);
|
||||
return _os;
|
||||
}
|
@ -1,15 +1,16 @@
|
||||
/** @file
|
||||
* @author Edouard DUPIN
|
||||
* @copyright 2014, Edouard DUPIN, all right reserved
|
||||
* @license APACHE v2.0 (see license file)
|
||||
* @license MPL v2.0 (see license file)
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <enet/Tcp.hpp>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <thread>
|
||||
#include <etk/Vector.hpp>
|
||||
#include <etk/Map.hpp>
|
||||
#include <ethread/Thread.hpp>
|
||||
#include <ethread/tools.hpp>
|
||||
#include <etk/Function.hpp>
|
||||
|
||||
namespace enet {
|
||||
enum class HTTPAnswerCode {
|
||||
@ -63,7 +64,7 @@ namespace enet {
|
||||
c505_httpVersionNotSupported, //!< The server does not support the HTTP protocol version used in the request
|
||||
c511_networkAuthenticationRequired, //!< The client needs to authenticate to gain network access
|
||||
};
|
||||
std::ostream& operator <<(std::ostream& _os, enum enet::HTTPAnswerCode _obj);
|
||||
etk::Stream& operator <<(etk::Stream& _os, enum enet::HTTPAnswerCode _obj);
|
||||
|
||||
enum class HTTPProtocol {
|
||||
http_0_1,
|
||||
@ -110,18 +111,28 @@ namespace enet {
|
||||
http_3_9,
|
||||
http_3_10,
|
||||
};
|
||||
std::ostream& operator <<(std::ostream& _os, enum enet::HTTPProtocol _obj);
|
||||
etk::Stream& operator <<(etk::Stream& _os, enum enet::HTTPProtocol _obj);
|
||||
class HttpHeader {
|
||||
protected:
|
||||
// key, val
|
||||
std::map<std::string, std::string> m_map;
|
||||
etk::Map<etk::String, etk::String> m_map;
|
||||
etk::Map<etk::String, etk::String> m_query;
|
||||
enum HTTPProtocol m_protocol;
|
||||
public:
|
||||
void setKey(const std::string& _key, const std::string& _value);
|
||||
void rmKey(const std::string& _key);
|
||||
std::string getKey(const std::string& _key) const;
|
||||
void setKey(const etk::String& _key, const etk::String& _value);
|
||||
void rmKey(const etk::String& _key);
|
||||
etk::String getKey(const etk::String& _key) const;
|
||||
bool existKey(const etk::String& _key) const;
|
||||
protected:
|
||||
std::string generateKeys() const;
|
||||
etk::String generateKeys() const;
|
||||
public:
|
||||
void setQuery(const etk::Map<etk::String, etk::String>& _value);
|
||||
void setQueryKey(const etk::String& _key, const etk::String& _value);
|
||||
void rmQueryKey(const etk::String& _key);
|
||||
etk::String getQueryKey(const etk::String& _key) const;
|
||||
bool existQueryKey(const etk::String& _key) const;
|
||||
protected:
|
||||
etk::String generateQueryKeys() const;
|
||||
public:
|
||||
enum HTTPProtocol getProtocol() const {
|
||||
return m_protocol;
|
||||
@ -131,27 +142,27 @@ namespace enet {
|
||||
}
|
||||
HttpHeader();
|
||||
virtual ~HttpHeader() = default;
|
||||
virtual std::string generate() const = 0;
|
||||
virtual etk::String generate() const = 0;
|
||||
};
|
||||
|
||||
class HttpAnswer : public HttpHeader {
|
||||
private:
|
||||
enet::HTTPAnswerCode m_what;
|
||||
std::string m_helpMessage;
|
||||
etk::String m_helpMessage;
|
||||
public:
|
||||
HttpAnswer(enum HTTPAnswerCode _code = enet::HTTPAnswerCode::c400_badRequest, const std::string& _help="");
|
||||
HttpAnswer(enum HTTPAnswerCode _code = enet::HTTPAnswerCode::c400_badRequest, const etk::String& _help="");
|
||||
void display() const;
|
||||
std::string generate() const;
|
||||
etk::String generate() const;
|
||||
void setErrorCode(enum HTTPAnswerCode _value) {
|
||||
m_what = _value;
|
||||
}
|
||||
enum HTTPAnswerCode getErrorCode() const {
|
||||
return m_what;
|
||||
}
|
||||
void setHelp(const std::string& _value) {
|
||||
void setHelp(const etk::String& _value) {
|
||||
m_helpMessage = _value;
|
||||
}
|
||||
const std::string& getHelp() const {
|
||||
const etk::String& getHelp() const {
|
||||
return m_helpMessage;
|
||||
}
|
||||
};
|
||||
@ -161,27 +172,28 @@ namespace enet {
|
||||
HTTP_POST,
|
||||
HTTP_PUT,
|
||||
HTTP_DELETE,
|
||||
HTTP_OPTIONS,
|
||||
};
|
||||
std::ostream& operator <<(std::ostream& _os, enum enet::HTTPReqType _obj);
|
||||
etk::Stream& operator <<(etk::Stream& _os, enum enet::HTTPReqType _obj);
|
||||
class HttpRequest : public HttpHeader {
|
||||
private:
|
||||
// key, val
|
||||
enum HTTPReqType m_req;
|
||||
std::string m_uri;
|
||||
etk::String m_uri;
|
||||
public:
|
||||
HttpRequest(enum enet::HTTPReqType _type=enet::HTTPReqType::HTTP_GET);
|
||||
void display() const;
|
||||
std::string generate() const;
|
||||
etk::String generate() const;
|
||||
void setType(enum enet::HTTPReqType _value) {
|
||||
m_req = _value;
|
||||
}
|
||||
enum enet::HTTPReqType getType() const{
|
||||
return m_req;
|
||||
}
|
||||
void setUri(const std::string& _value) {
|
||||
void setUri(const etk::String& _value) {
|
||||
m_uri = _value;
|
||||
}
|
||||
const std::string& getUri() const {
|
||||
const etk::String& getUri() const {
|
||||
return m_uri;
|
||||
}
|
||||
};
|
||||
@ -215,9 +227,17 @@ namespace enet {
|
||||
protected:
|
||||
enet::Tcp m_connection;
|
||||
bool m_headerIsSend;
|
||||
std::thread* m_thread;
|
||||
ethread::Thread* m_thread;
|
||||
bool m_threadRunning;
|
||||
std::vector<uint8_t> m_temporaryBuffer;
|
||||
etk::Vector<uint8_t> m_temporaryBuffer;
|
||||
public:
|
||||
/**
|
||||
* @brief Get the adress of the connection source IP:port
|
||||
* @return string with the remote address name.
|
||||
*/
|
||||
const etk::String& getRemoteAddress() const {
|
||||
return m_connection.getRemoteName();
|
||||
}
|
||||
private:
|
||||
void threadCallback();
|
||||
private:
|
||||
@ -229,7 +249,14 @@ namespace enet {
|
||||
return m_connection.getConnectionStatus() == enet::Tcp::status::link;
|
||||
}
|
||||
public:
|
||||
using Observer = std::function<void(std::vector<uint8_t>&)>; //!< Define an Observer: function pointer
|
||||
/**
|
||||
* @brief Redirect the current HTTP request to an other network address.
|
||||
* @param[in] _addressRedirect new redirection address:port
|
||||
* @param[in] _inThreadStop the http thread request an auto-stop.
|
||||
*/
|
||||
void redirectTo(const etk::String& _addressRedirect, bool _inThreadStop=false);
|
||||
public:
|
||||
using Observer = etk::Function<void(etk::Vector<uint8_t>&)>; //!< Define an Observer: function pointer
|
||||
Observer m_observer;
|
||||
/**
|
||||
* @brief Connect an function member on the signal with the shared_ptr object.
|
||||
@ -238,8 +265,8 @@ namespace enet {
|
||||
* @param[in] _args Argument optinnal the user want to add.
|
||||
*/
|
||||
template<class CLASS_TYPE>
|
||||
void connect(CLASS_TYPE* _class, void (CLASS_TYPE::*_func)(std::vector<uint8_t>&)) {
|
||||
m_observer = [=](std::vector<uint8_t>& _value){
|
||||
void connect(CLASS_TYPE* _class, void (CLASS_TYPE::*_func)(etk::Vector<uint8_t>&)) {
|
||||
m_observer = [=](etk::Vector<uint8_t>& _value){
|
||||
(*_class.*_func)(_value);
|
||||
};
|
||||
}
|
||||
@ -247,7 +274,7 @@ namespace enet {
|
||||
m_observer = _func;
|
||||
}
|
||||
public:
|
||||
using ObserverRaw = std::function<void(enet::Tcp&)>; //!< Define an Observer: function pointer
|
||||
using ObserverRaw = etk::Function<void(enet::Tcp&)>; //!< Define an Observer: function pointer
|
||||
ObserverRaw m_observerRaw;
|
||||
/**
|
||||
* @brief Connect an function member on the signal with the shared_ptr object.
|
||||
@ -265,11 +292,11 @@ namespace enet {
|
||||
m_observerRaw = _func;
|
||||
}
|
||||
public:
|
||||
using ObserverRequest = std::function<void(const enet::HttpRequest&)>; //!< Define an Observer: function pointer
|
||||
using ObserverRequest = etk::Function<void(const enet::HttpRequest&)>; //!< Define an Observer: function pointer
|
||||
protected:
|
||||
ObserverRequest m_observerRequest;
|
||||
public:
|
||||
using ObserverAnswer = std::function<void(const enet::HttpAnswer&)>; //!< Define an Observer: function pointer
|
||||
using ObserverAnswer = etk::Function<void(const enet::HttpAnswer&)>; //!< Define an Observer: function pointer
|
||||
protected:
|
||||
ObserverAnswer m_observerAnswer;
|
||||
public:
|
||||
@ -288,7 +315,7 @@ namespace enet {
|
||||
* @return >0 byte size on the socket write
|
||||
* @return -1 an error occured.
|
||||
*/
|
||||
int32_t write(const std::string& _data, bool _writeBackSlashZero = true) {
|
||||
int32_t write(const etk::String& _data, bool _writeBackSlashZero = true) {
|
||||
if (_data.size() == 0) {
|
||||
return 0;
|
||||
}
|
||||
@ -305,7 +332,7 @@ namespace enet {
|
||||
* @return -1 an error occured.
|
||||
*/
|
||||
template <class T>
|
||||
int32_t write(const std::vector<T>& _data) {
|
||||
int32_t write(const etk::Vector<T>& _data) {
|
||||
if (_data.size() == 0) {
|
||||
return 0;
|
||||
}
|
||||
@ -322,12 +349,13 @@ namespace enet {
|
||||
HttpClient(enet::Tcp _connection);
|
||||
public:
|
||||
void setHeader(const enet::HttpRequest& _header) {
|
||||
_header.display();
|
||||
setRequestHeader(_header);
|
||||
}
|
||||
public:
|
||||
//bool get(const std::string& _address);
|
||||
//bool post(const std::string& _address, const std::map<std::string, std::string>& _values);
|
||||
//bool post(const std::string& _address, const std::string& _contentType, const std::string& _data);
|
||||
//bool get(const etk::String& _address);
|
||||
//bool post(const etk::String& _address, const etk::Map<etk::String, etk::String>& _values);
|
||||
//bool post(const etk::String& _address, const etk::String& _contentType, const etk::String& _data);
|
||||
public:
|
||||
/**
|
||||
* @brief Connect an function member on the signal with the shared_ptr object.
|
||||
@ -351,6 +379,7 @@ namespace enet {
|
||||
HttpServer(enet::Tcp _connection);
|
||||
public:
|
||||
void setHeader(const enet::HttpAnswer& _header) {
|
||||
_header.display();
|
||||
setAnswerHeader(_header);
|
||||
}
|
||||
public:
|
||||
|
65
enet/Tcp.cpp
65
enet/Tcp.cpp
@ -1,16 +1,19 @@
|
||||
/** @file
|
||||
* @author Edouard DUPIN
|
||||
* @copyright 2014, Edouard DUPIN, all right reserved
|
||||
* @license APACHE v2.0 (see license file)
|
||||
* @license MPL v2.0 (see license file)
|
||||
*/
|
||||
|
||||
#include <enet/debug.hpp>
|
||||
#include <enet/Tcp.hpp>
|
||||
#include <sys/types.h>
|
||||
#include <cerrno>
|
||||
#include <unistd.h>
|
||||
#include <cstring>
|
||||
extern "C" {
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
}
|
||||
#include <etk/stdTools.hpp>
|
||||
#include <ethread/tools.hpp>
|
||||
|
||||
#ifdef __TARGET_OS__Windows
|
||||
#include <winsock2.h>
|
||||
@ -22,6 +25,10 @@
|
||||
#include <netdb.h>
|
||||
#endif
|
||||
|
||||
#ifdef ENET_STORE_INPUT
|
||||
static uint32_t baseID = 0;
|
||||
#endif
|
||||
|
||||
bool enet::Tcp::setTCPNoDelay(bool _enabled) {
|
||||
if (m_socketId >= 0) {
|
||||
int flag = _enabled==true?1:0;
|
||||
@ -46,20 +53,29 @@ enet::Tcp::Tcp() :
|
||||
}
|
||||
|
||||
#ifdef __TARGET_OS__Windows
|
||||
enet::Tcp::Tcp(SOCKET _idSocket, const std::string& _name) :
|
||||
enet::Tcp::Tcp(SOCKET _idSocket, const etk::String& _name, const etk::String& _remoteName) :
|
||||
#else
|
||||
enet::Tcp::Tcp(int32_t _idSocket, const std::string& _name) :
|
||||
enet::Tcp::Tcp(int32_t _idSocket, const etk::String& _name, const etk::String& _remoteName) :
|
||||
#endif
|
||||
m_socketId(_idSocket),
|
||||
m_name(_name),
|
||||
m_remoteName(_remoteName),
|
||||
m_status(status::link) {
|
||||
|
||||
#ifdef ENET_STORE_INPUT
|
||||
m_nodeStoreInput = etk::FSNode("CACHE:StoreTCPdata_" + etk::toString(baseID++) + ".tcp");
|
||||
m_nodeStoreInput.fileOpenWrite();
|
||||
#endif
|
||||
}
|
||||
|
||||
enet::Tcp::Tcp(Tcp&& _obj) :
|
||||
m_socketId(_obj.m_socketId),
|
||||
m_name(_obj.m_name),
|
||||
m_remoteName(_obj.m_remoteName),
|
||||
m_status(_obj.m_status) {
|
||||
#ifdef ENET_STORE_INPUT
|
||||
m_nodeStoreInput = etk::FSNode("CACHE:StoreTCPdata_" + etk::toString(baseID++) + ".tcp");
|
||||
m_nodeStoreInput.fileOpenWrite();
|
||||
#endif
|
||||
#ifdef __TARGET_OS__Windows
|
||||
_obj.m_socketId = INVALID_SOCKET;
|
||||
#else
|
||||
@ -75,6 +91,10 @@ enet::Tcp::~Tcp() {
|
||||
|
||||
enet::Tcp& enet::Tcp::operator = (enet::Tcp&& _obj) {
|
||||
unlink();
|
||||
#ifdef ENET_STORE_INPUT
|
||||
m_nodeStoreInput = etk::FSNode("CACHE:StoreTCPdata_" + etk::toString(baseID++) + ".tcp");
|
||||
m_nodeStoreInput.fileOpenWrite();
|
||||
#endif
|
||||
m_socketId = _obj.m_socketId;
|
||||
#ifdef __TARGET_OS__Windows
|
||||
_obj.m_socketId = INVALID_SOCKET;
|
||||
@ -89,20 +109,25 @@ enet::Tcp& enet::Tcp::operator = (enet::Tcp&& _obj) {
|
||||
}
|
||||
|
||||
bool enet::Tcp::unlink() {
|
||||
// prevent call while stoping ...
|
||||
m_status = status::unlink;
|
||||
if (m_socketId >= 0) {
|
||||
ENET_INFO("Close socket (start)");
|
||||
#ifdef __TARGET_OS__Windows
|
||||
shutdown(m_socketId, SD_BOTH);
|
||||
// Release hand of the socket to permit the Select to exit ... ==> otherwise it lock ...
|
||||
ethread::sleepMilliSeconds((20));
|
||||
closesocket(m_socketId);
|
||||
m_socketId = INVALID_SOCKET;
|
||||
#else
|
||||
shutdown(m_socketId, SHUT_RDWR);
|
||||
// Release hand of the socket to permit the Select to exit ... ==> otherwise it lock ...
|
||||
ethread::sleepMilliSeconds((20));
|
||||
close(m_socketId);
|
||||
m_socketId = -1;
|
||||
#endif
|
||||
ENET_INFO("Close socket (done)");
|
||||
}
|
||||
m_status = status::unlink;
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -121,7 +146,9 @@ int32_t enet::Tcp::read(void* _data, int32_t _maxLen) {
|
||||
timeOutStruct.tv_usec = 0;
|
||||
FD_ZERO(&sock);
|
||||
FD_SET(m_socketId,&sock);
|
||||
ENET_VERBOSE(" select ...");
|
||||
int rc = select(m_socketId+1, &sock, NULL, NULL, &timeOutStruct);
|
||||
ENET_VERBOSE(" select (done)");
|
||||
// Check to see if the poll call failed.
|
||||
if (rc < 0) {
|
||||
ENET_ERROR(" select() failed");
|
||||
@ -141,7 +168,7 @@ int32_t enet::Tcp::read(void* _data, int32_t _maxLen) {
|
||||
// Receive data on this connection until the recv fails with EWOULDBLOCK.
|
||||
// If any other failure occurs, we will close the connection.
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(m_mutex);
|
||||
ethread::UniqueLock lock(m_mutex);
|
||||
rc = recv(m_socketId, (char *)_data, _maxLen, 0);
|
||||
}
|
||||
if (rc < 0) {
|
||||
@ -154,6 +181,7 @@ int32_t enet::Tcp::read(void* _data, int32_t _maxLen) {
|
||||
if (rc == 0) {
|
||||
ENET_INFO("Connection closed");
|
||||
closeConn = true;
|
||||
size = 0;
|
||||
}
|
||||
if (closeConn == false) {
|
||||
// Data was received
|
||||
@ -164,7 +192,9 @@ int32_t enet::Tcp::read(void* _data, int32_t _maxLen) {
|
||||
ENET_DEBUG(" Set status at remote close ...");
|
||||
m_status = status::linkRemoteClose;
|
||||
}
|
||||
//#endif
|
||||
#ifdef ENET_STORE_INPUT
|
||||
m_nodeStoreInput.fileWrite(_data, 1, size);
|
||||
#endif
|
||||
return size;
|
||||
}
|
||||
|
||||
@ -174,10 +204,23 @@ int32_t enet::Tcp::write(const void* _data, int32_t _len) {
|
||||
ENET_ERROR("Can not write on unlink connection");
|
||||
return -1;
|
||||
}
|
||||
if (_data == null) {
|
||||
ENET_ERROR("try write null data on TCP socket");
|
||||
return -1;
|
||||
}
|
||||
if (_len <= 0) {
|
||||
if (_len == 0) {
|
||||
ENET_WARNING("try write data with lenght=" << _len << " ==> bad case");
|
||||
return 0;
|
||||
}
|
||||
ENET_ERROR("try write data with lenght=" << _len << " ==> bad case");
|
||||
elog::displayBacktrace();
|
||||
return -1;
|
||||
}
|
||||
//ENET_DEBUG("write on socketid = " << m_socketId << " data@=" << int64_t(_data) << " size=" << _len );
|
||||
int32_t size;
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(m_mutex);
|
||||
ethread::UniqueLock lock(m_mutex);
|
||||
size = ::send(m_socketId, (const char *)_data, _len, 0);
|
||||
}
|
||||
if ( size != _len
|
||||
|
38
enet/Tcp.hpp
38
enet/Tcp.hpp
@ -1,17 +1,24 @@
|
||||
/** @file
|
||||
* @author Edouard DUPIN
|
||||
* @copyright 2014, Edouard DUPIN, all right reserved
|
||||
* @license APACHE v2.0 (see license file)
|
||||
* @license MPL v2.0 (see license file)
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <etk/types.hpp>
|
||||
#include <mutex>
|
||||
#include <ethread/Mutex.hpp>
|
||||
#include <etk/Function.hpp>
|
||||
#ifdef __TARGET_OS__Windows
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#endif
|
||||
|
||||
//#define ENET_STORE_INPUT
|
||||
|
||||
#ifdef ENET_STORE_INPUT
|
||||
#include <etk/os/FSNode.hpp>
|
||||
#endif
|
||||
|
||||
namespace enet {
|
||||
class Tcp {
|
||||
private:
|
||||
@ -20,13 +27,16 @@ namespace enet {
|
||||
#else
|
||||
int32_t m_socketId; //!< socket linux interface generic
|
||||
#endif
|
||||
std::mutex m_mutex;
|
||||
#ifdef ENET_STORE_INPUT
|
||||
etk::FSNode m_nodeStoreInput;
|
||||
#endif
|
||||
ethread::Mutex m_mutex;
|
||||
public:
|
||||
Tcp();
|
||||
#ifdef __TARGET_OS__Windows
|
||||
Tcp(SOCKET _idSocket, const std::string& _name);
|
||||
Tcp(SOCKET _idSocket, const etk::String& _name, const etk::String& _remoteName="");
|
||||
#else
|
||||
Tcp(int32_t _idSocket, const std::string& _name);
|
||||
Tcp(int32_t _idSocket, const etk::String& _name, const etk::String& _remoteName="");
|
||||
#endif
|
||||
// move constructor
|
||||
Tcp(Tcp&& _obj);
|
||||
@ -36,15 +46,25 @@ namespace enet {
|
||||
Tcp& operator= (Tcp& _obj) = delete;
|
||||
virtual ~Tcp();
|
||||
private:
|
||||
std::string m_name; //!< hostname/IP:port.
|
||||
etk::String m_name; //!< hostname/IP:port.
|
||||
public:
|
||||
/**
|
||||
* @brief Get the decriptive name hot the host:port
|
||||
* @return the string requested
|
||||
*/
|
||||
const std::string& getName() {
|
||||
const etk::String& getName() const {
|
||||
return m_name;
|
||||
}
|
||||
private:
|
||||
etk::String m_remoteName; //!< remote IP:port.
|
||||
public:
|
||||
/**
|
||||
* @brief Get the decriptive name hot the host:port
|
||||
* @return the string requested
|
||||
*/
|
||||
const etk::String& getRemoteName() const {
|
||||
return m_remoteName;
|
||||
}
|
||||
public:
|
||||
enum class status {
|
||||
unlink,
|
||||
@ -92,7 +112,7 @@ namespace enet {
|
||||
* @return >0 byte size on the socket write
|
||||
* @return -1 an error occured.
|
||||
*/
|
||||
int32_t write(const std::string& _data, bool _writeBackSlashZero = true) {
|
||||
int32_t write(const etk::String& _data, bool _writeBackSlashZero = true) {
|
||||
if (_data.size() == 0) {
|
||||
return 0;
|
||||
}
|
||||
@ -109,7 +129,7 @@ namespace enet {
|
||||
* @return -1 an error occured.
|
||||
*/
|
||||
template <class T>
|
||||
int32_t write(const std::vector<T>& _data) {
|
||||
int32_t write(const etk::Vector<T>& _data) {
|
||||
if (_data.size() == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
@ -1,17 +1,19 @@
|
||||
/** @file
|
||||
* @author Edouard DUPIN
|
||||
* @copyright 2014, Edouard DUPIN, all right reserved
|
||||
* @license APACHE v2.0 (see license file)
|
||||
* @license MPL v2.0 (see license file)
|
||||
*/
|
||||
|
||||
#include <enet/debug.hpp>
|
||||
#include <enet/Tcp.hpp>
|
||||
#include <enet/TcpClient.hpp>
|
||||
#include <enet/enet.hpp>
|
||||
#include <sys/types.h>
|
||||
#include <cerrno>
|
||||
#include <unistd.h>
|
||||
#include <cstring>
|
||||
extern "C" {
|
||||
#include <sys/types.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
}
|
||||
#include <etk/stdTools.hpp>
|
||||
|
||||
#ifdef __TARGET_OS__Windows
|
||||
@ -19,34 +21,53 @@
|
||||
#else
|
||||
#include <netinet/in.h>
|
||||
#include <netdb.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#endif
|
||||
|
||||
enet::Tcp enet::connectTcpClient(uint8_t _ip1, uint8_t _ip2, uint8_t _ip3, uint8_t _ip4, uint16_t _port, uint32_t _numberRetry) {
|
||||
std::string tmpname;
|
||||
tmpname = etk::to_string(_ip1);
|
||||
enet::Tcp enet::connectTcpClient(const etk::String& _config, uint32_t _numberRetry, echrono::Duration _timeOut) {
|
||||
size_t pos = _config.find(':');
|
||||
if (pos == etk::String::npos) {
|
||||
return etk::move(enet::connectTcpClient(_config, 0, _numberRetry, _timeOut));
|
||||
}
|
||||
return etk::move(enet::connectTcpClient(_config.extract(0, pos),
|
||||
etk::string_to_uint16_t(_config.extract(pos+1)),
|
||||
_numberRetry,
|
||||
_timeOut));
|
||||
}
|
||||
|
||||
enet::Tcp enet::connectTcpClient(uint8_t _ip1, uint8_t _ip2, uint8_t _ip3, uint8_t _ip4, uint16_t _port, uint32_t _numberRetry, echrono::Duration _timeOut) {
|
||||
etk::String tmpname;
|
||||
tmpname = etk::toString(_ip1);
|
||||
tmpname += ".";
|
||||
tmpname += etk::to_string(_ip2);
|
||||
tmpname += etk::toString(_ip2);
|
||||
tmpname += ".";
|
||||
tmpname += etk::to_string(_ip3);
|
||||
tmpname += etk::toString(_ip3);
|
||||
tmpname += ".";
|
||||
tmpname += etk::to_string(_ip4);
|
||||
return std::move(enet::connectTcpClient(tmpname, _port, _numberRetry));
|
||||
tmpname += etk::toString(_ip4);
|
||||
return etk::move(enet::connectTcpClient(tmpname, _port, _numberRetry, _timeOut));
|
||||
}
|
||||
|
||||
#ifdef __TARGET_OS__Windows
|
||||
enet::Tcp enet::connectTcpClient(const std::string& _hostname, uint16_t _port, uint32_t _numberRetry) {
|
||||
enet::Tcp enet::connectTcpClient(const etk::String& _hostname, uint16_t _port, uint32_t _numberRetry, echrono::Duration _timeOut) {
|
||||
if (enet::isInit() == false) {
|
||||
ENET_ERROR("Need call enet::init(...) before accessing to the socket");
|
||||
return std::move(enet::Tcp());
|
||||
return etk::move(enet::Tcp());
|
||||
}
|
||||
if (_hostname == "") {
|
||||
ENET_ERROR("get connection wihtout hostname");
|
||||
return etk::move(enet::Tcp());
|
||||
}
|
||||
SOCKET socketId = INVALID_SOCKET;
|
||||
ENET_INFO("Start connection on " << _hostname << ":" << _port);
|
||||
for(int32_t iii=0; iii<_numberRetry ;iii++) {
|
||||
if (iii > 0) {
|
||||
ethread::sleepMilliSeconds((200));
|
||||
}
|
||||
// open in Socket normal mode
|
||||
socketId = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (socketId < 0) {
|
||||
ENET_ERROR("ERROR while opening socket : errno=" << errno << "," << strerror(errno));
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
||||
continue;
|
||||
}
|
||||
ENET_INFO("Try connect on socket ... (" << iii+1 << "/" << _numberRetry << ")");
|
||||
@ -58,18 +79,17 @@ enet::Tcp enet::connectTcpClient(uint8_t _ip1, uint8_t _ip2, uint8_t _ip3, uint8
|
||||
hints.ai_protocol = IPPROTO_TCP;
|
||||
|
||||
// Resolve the server address and port
|
||||
struct addrinfo* result = nullptr;
|
||||
std::string portValue = etk::to_string(_port);
|
||||
struct addrinfo* result = null;
|
||||
etk::String portValue = etk::toString(_port);
|
||||
int iResult = getaddrinfo(_hostname.c_str(), portValue.c_str(), &hints, &result);
|
||||
if (iResult != 0) {
|
||||
ENET_ERROR("getaddrinfo failed with error: " << iResult);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
||||
continue;
|
||||
}
|
||||
|
||||
// Attempt to connect to an address until one succeeds
|
||||
for(struct addrinfo* ptr=result;
|
||||
ptr != nullptr;
|
||||
ptr != null;
|
||||
ptr=ptr->ai_next) {
|
||||
ENET_DEBUG(" find one ...");
|
||||
// Create a SOCKET for connecting to server
|
||||
@ -92,7 +112,6 @@ enet::Tcp enet::connectTcpClient(uint8_t _ip1, uint8_t _ip2, uint8_t _ip3, uint8
|
||||
|
||||
if (socketId == INVALID_SOCKET) {
|
||||
ENET_ERROR("Unable to connect to server!");
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
||||
continue;
|
||||
} else {
|
||||
break;
|
||||
@ -100,34 +119,56 @@ enet::Tcp enet::connectTcpClient(uint8_t _ip1, uint8_t _ip2, uint8_t _ip3, uint8
|
||||
}
|
||||
if (socketId == INVALID_SOCKET) {
|
||||
ENET_ERROR("ERROR connecting ... (after all try)");
|
||||
return std::move(enet::Tcp());
|
||||
return etk::move(enet::Tcp());
|
||||
}
|
||||
ENET_DEBUG("Connection done");
|
||||
return std::move(enet::Tcp(socketId, _hostname + ":" + etk::to_string(_port)));
|
||||
return etk::move(enet::Tcp(socketId, _hostname + ":" + etk::toString(_port)));
|
||||
}
|
||||
#else
|
||||
#include <sys/socket.h>
|
||||
enet::Tcp enet::connectTcpClient(const std::string& _hostname, uint16_t _port, uint32_t _numberRetry) {
|
||||
enet::Tcp enet::connectTcpClient(const etk::String& _hostname, uint16_t _port, uint32_t _numberRetry, echrono::Duration _timeOut) {
|
||||
if (enet::isInit() == false) {
|
||||
ENET_ERROR("Need call enet::init(...) before accessing to the socket");
|
||||
return std::move(enet::Tcp());
|
||||
return etk::move(enet::Tcp());
|
||||
}
|
||||
if (_hostname == "") {
|
||||
ENET_ERROR("get connection wihtout hostname");
|
||||
return etk::move(enet::Tcp());
|
||||
}
|
||||
int32_t socketId = -1;
|
||||
ENET_INFO("Start connection on " << _hostname << ":" << _port);
|
||||
for(int32_t iii=0; iii<_numberRetry ;iii++) {
|
||||
if (iii > 0) {
|
||||
ethread::sleepMilliSeconds((200));
|
||||
}
|
||||
// open in Socket normal mode
|
||||
socketId = socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (socketId < 0) {
|
||||
ENET_ERROR("ERROR while opening socket : errno=" << errno << "," << strerror(errno));
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
||||
continue;
|
||||
}
|
||||
ENET_INFO("Try connect on socket ... (" << iii+1 << "/" << _numberRetry << ")");
|
||||
struct sockaddr_in servAddr;
|
||||
struct hostent* server = gethostbyname(_hostname.c_str());
|
||||
if (server == nullptr) {
|
||||
struct hostent* server = null;
|
||||
if ( _hostname.c_str()[0] >= '0'
|
||||
&& _hostname.c_str()[0] <= '9') {
|
||||
ENET_INFO("Calling gethostbyaddr with " << _hostname);
|
||||
struct in_addr addr;
|
||||
addr.s_addr = inet_addr(_hostname.c_str());
|
||||
if (addr.s_addr == INADDR_NONE) {
|
||||
ENET_ERROR("The IPv4 address entered must be a legal address" << _hostname.c_str());
|
||||
return etk::move(enet::Tcp());
|
||||
} else {
|
||||
// TODO : This is deprecated use getaddrinfo like windows ...
|
||||
server = gethostbyaddr((char *) &addr, 4, AF_INET);
|
||||
}
|
||||
} else {
|
||||
ENET_INFO("Calling gethostbyname with " << _hostname);
|
||||
// TODO : This is deprecated use getaddrinfo like windows ...
|
||||
server = gethostbyname(_hostname.c_str());
|
||||
}
|
||||
if (server == null) {
|
||||
ENET_ERROR("ERROR, no such host : " << _hostname);
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
||||
continue;
|
||||
}
|
||||
bzero((char *) &servAddr, sizeof(servAddr));
|
||||
@ -135,7 +176,7 @@ enet::Tcp enet::connectTcpClient(uint8_t _ip1, uint8_t _ip2, uint8_t _ip3, uint8
|
||||
bcopy((char *)server->h_addr, (char *)&servAddr.sin_addr.s_addr, server->h_length);
|
||||
servAddr.sin_port = htons(_port);
|
||||
ENET_INFO("Start connexion ...");
|
||||
if (connect(socketId, (struct sockaddr *)&servAddr,sizeof(servAddr)) != 0) {
|
||||
if (connect(socketId, (struct sockaddr *)&servAddr, sizeof(servAddr)) != 0) {
|
||||
if(errno != EINPROGRESS) {
|
||||
if( errno != ENOENT
|
||||
&& errno != EAGAIN
|
||||
@ -150,18 +191,16 @@ enet::Tcp enet::connectTcpClient(uint8_t _ip1, uint8_t _ip2, uint8_t _ip3, uint8
|
||||
socketId = -1;
|
||||
}
|
||||
ENET_ERROR("ERROR connecting, maybe retry ... errno=" << errno << "," << strerror(errno));
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
||||
continue;
|
||||
}
|
||||
// if we are here ==> then the connextion is done corectly ...
|
||||
break;
|
||||
}
|
||||
if (socketId<0) {
|
||||
ENET_ERROR("ERROR connecting ... (after all try)");
|
||||
return std::move(enet::Tcp());
|
||||
return etk::move(enet::Tcp());
|
||||
}
|
||||
ENET_DEBUG("Connection done");
|
||||
return std::move(enet::Tcp(socketId, _hostname + ":" + etk::to_string(_port)));
|
||||
ENET_INFO("Connection done");
|
||||
return etk::move(enet::Tcp(socketId, _hostname + ":" + etk::toString(_port)));
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -1,13 +1,15 @@
|
||||
/** @file
|
||||
* @author Edouard DUPIN
|
||||
* @copyright 2014, Edouard DUPIN, all right reserved
|
||||
* @license APACHE v2.0 (see license file)
|
||||
* @license MPL v2.0 (see license file)
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <enet/Tcp.hpp>
|
||||
#include <echrono/Duration.hpp>
|
||||
|
||||
namespace enet {
|
||||
enet::Tcp connectTcpClient(uint8_t _ip1, uint8_t _ip2, uint8_t _ip3, uint8_t _ip4, uint16_t _port, uint32_t _numberRetry=5);
|
||||
enet::Tcp connectTcpClient(const std::string& _hostname, uint16_t _port, uint32_t _numberRetry=5);
|
||||
enet::Tcp connectTcpClient(uint8_t _ip1, uint8_t _ip2, uint8_t _ip3, uint8_t _ip4, uint16_t _port, uint32_t _numberRetry=5, echrono::Duration _timeOut = echrono::seconds(1));
|
||||
enet::Tcp connectTcpClient(const etk::String& _hostname, uint16_t _port, uint32_t _numberRetry=5, echrono::Duration _timeOut = echrono::seconds(1));
|
||||
enet::Tcp connectTcpClient(const etk::String& _config, uint32_t _numberRetry, echrono::Duration _timeOut);
|
||||
}
|
||||
|
@ -1,17 +1,19 @@
|
||||
/** @file
|
||||
* @author Edouard DUPIN
|
||||
* @copyright 2014, Edouard DUPIN, all right reserved
|
||||
* @license APACHE v2.0 (see license file)
|
||||
* @license MPL v2.0 (see license file)
|
||||
*/
|
||||
|
||||
#include <enet/debug.hpp>
|
||||
#include <enet/Tcp.hpp>
|
||||
#include <enet/TcpServer.hpp>
|
||||
#include <enet/enet.hpp>
|
||||
#include <sys/types.h>
|
||||
#include <cerrno>
|
||||
#include <unistd.h>
|
||||
#include <cstring>
|
||||
extern "C" {
|
||||
#include <sys/types.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
}
|
||||
#include <etk/stdTools.hpp>
|
||||
|
||||
#ifdef __TARGET_OS__Windows
|
||||
@ -37,18 +39,18 @@ enet::TcpServer::~TcpServer() {
|
||||
}
|
||||
|
||||
void enet::TcpServer::setIpV4(uint8_t _fist, uint8_t _second, uint8_t _third, uint8_t _quatro) {
|
||||
std::string tmpname;
|
||||
tmpname = etk::to_string(_fist);
|
||||
etk::String tmpname;
|
||||
tmpname = etk::toString(_fist);
|
||||
tmpname += ".";
|
||||
tmpname += etk::to_string(_second);
|
||||
tmpname += etk::toString(_second);
|
||||
tmpname += ".";
|
||||
tmpname += etk::to_string(_third);
|
||||
tmpname += etk::toString(_third);
|
||||
tmpname += ".";
|
||||
tmpname += etk::to_string(_quatro);
|
||||
tmpname += etk::toString(_quatro);
|
||||
setHostNane(tmpname);
|
||||
}
|
||||
|
||||
void enet::TcpServer::setHostNane(const std::string& _name) {
|
||||
void enet::TcpServer::setHostNane(const etk::String& _name) {
|
||||
if (_name == m_host) {
|
||||
return;
|
||||
}
|
||||
@ -70,7 +72,7 @@ void enet::TcpServer::setPort(uint16_t _port) {
|
||||
}
|
||||
ENET_INFO("Start connection on " << m_host << ":" << m_port);
|
||||
|
||||
struct addrinfo *result = nullptr;
|
||||
struct addrinfo *result = null;
|
||||
struct addrinfo hints;
|
||||
ZeroMemory(&hints, sizeof(hints));
|
||||
hints.ai_family = AF_INET;
|
||||
@ -79,8 +81,8 @@ void enet::TcpServer::setPort(uint16_t _port) {
|
||||
hints.ai_flags = AI_PASSIVE;
|
||||
|
||||
// Resolve the server address and port
|
||||
std::string portValue = etk::to_string(m_port);
|
||||
int iResult = getaddrinfo(nullptr, portValue.c_str(), &hints, &result);
|
||||
etk::String portValue = etk::toString(m_port);
|
||||
int iResult = getaddrinfo(null, portValue.c_str(), &hints, &result);
|
||||
if (iResult != 0) {
|
||||
ENET_ERROR("getaddrinfo failed with error: " << iResult);
|
||||
return 1;
|
||||
@ -153,7 +155,7 @@ void enet::TcpServer::setPort(uint16_t _port) {
|
||||
enet::Tcp enet::TcpServer::waitNext() {
|
||||
if (enet::isInit() == false) {
|
||||
ENET_ERROR("Need call enet::init(...) before accessing to the socket");
|
||||
return std::move(enet::Tcp());
|
||||
return etk::move(enet::Tcp());
|
||||
}
|
||||
ENET_INFO("End binding Socket ... (start listen)");
|
||||
#ifdef __TARGET_OS__Windows
|
||||
@ -181,8 +183,57 @@ enet::Tcp enet::TcpServer::waitNext() {
|
||||
|
||||
return enet::Tcp();
|
||||
}
|
||||
ENET_INFO("End configuring Socket ... Find New one");
|
||||
return enet::Tcp(socketIdClient, m_host + ":" + etk::to_string(m_port));
|
||||
etk::String remoteAddress;
|
||||
{
|
||||
struct sockaddr_storage addr;
|
||||
char ipstr[INET6_ADDRSTRLEN];
|
||||
socklen_t len = sizeof(addr);
|
||||
getpeername(socketIdClient, (struct sockaddr*)&addr, &len);
|
||||
// deal with both IPv4 and IPv6:
|
||||
if (addr.ss_family == AF_INET) {
|
||||
struct sockaddr_in *s = (struct sockaddr_in *)&addr;
|
||||
int port = ntohs(s->sin_port);
|
||||
remoteAddress = etk::toString(s->sin_addr.s_addr&0xFF);
|
||||
remoteAddress += ".";
|
||||
remoteAddress += etk::toString((s->sin_addr.s_addr>>8)&0xFF);
|
||||
remoteAddress += ".";
|
||||
remoteAddress += etk::toString((s->sin_addr.s_addr>>16)&0xFF);
|
||||
remoteAddress += ".";
|
||||
remoteAddress += etk::toString((s->sin_addr.s_addr>>24)&0xFF);
|
||||
remoteAddress += ":";
|
||||
remoteAddress += etk::toString(port);
|
||||
} else { // AF_INET6
|
||||
struct sockaddr_in6 *s = (struct sockaddr_in6 *)&addr;
|
||||
int port = ntohs(s->sin6_port);
|
||||
remoteAddress = etk::toHex(s->sin6_addr.s6_addr[0], 2);
|
||||
remoteAddress += etk::toHex(s->sin6_addr.s6_addr[1], 2);
|
||||
remoteAddress += ".";
|
||||
remoteAddress += etk::toHex(s->sin6_addr.s6_addr[2], 2);
|
||||
remoteAddress += etk::toHex(s->sin6_addr.s6_addr[3], 2);
|
||||
remoteAddress += ".";
|
||||
remoteAddress += etk::toHex(s->sin6_addr.s6_addr[4], 2);
|
||||
remoteAddress += etk::toHex(s->sin6_addr.s6_addr[5], 2);
|
||||
remoteAddress += ".";
|
||||
remoteAddress += etk::toHex(s->sin6_addr.s6_addr[6], 2);
|
||||
remoteAddress += etk::toHex(s->sin6_addr.s6_addr[7], 2);
|
||||
remoteAddress += ".";
|
||||
remoteAddress += etk::toHex(s->sin6_addr.s6_addr[8], 2);
|
||||
remoteAddress += etk::toHex(s->sin6_addr.s6_addr[9], 2);
|
||||
remoteAddress += ".";
|
||||
remoteAddress += etk::toHex(s->sin6_addr.s6_addr[10], 2);
|
||||
remoteAddress += etk::toHex(s->sin6_addr.s6_addr[11], 2);
|
||||
remoteAddress += ".";
|
||||
remoteAddress += etk::toHex(s->sin6_addr.s6_addr[12], 2);
|
||||
remoteAddress += etk::toHex(s->sin6_addr.s6_addr[13], 2);
|
||||
remoteAddress += ".";
|
||||
remoteAddress += etk::toHex(s->sin6_addr.s6_addr[14], 2);
|
||||
remoteAddress += etk::toHex(s->sin6_addr.s6_addr[15], 2);
|
||||
remoteAddress += ":";
|
||||
remoteAddress += etk::toString(port);
|
||||
}
|
||||
}
|
||||
ENET_ERROR("End configuring Socket ... Find New one FROM " << remoteAddress);
|
||||
return enet::Tcp(socketIdClient, m_host + ":" + etk::toString(m_port), remoteAddress);
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
/** @file
|
||||
* @author Edouard DUPIN
|
||||
* @copyright 2014, Edouard DUPIN, all right reserved
|
||||
* @license APACHE v2.0 (see license file)
|
||||
* @license MPL v2.0 (see license file)
|
||||
*/
|
||||
#pragma once
|
||||
#include <enet/Tcp.hpp>
|
||||
@ -26,7 +26,7 @@ namespace enet {
|
||||
TcpServer();
|
||||
virtual ~TcpServer();
|
||||
private:
|
||||
std::string m_host; //!< hostname/IP to connect with.
|
||||
etk::String m_host; //!< hostname/IP to connect with.
|
||||
public:
|
||||
/**
|
||||
* @brief Set the connection IP id.
|
||||
@ -40,12 +40,12 @@ namespace enet {
|
||||
* @brief set the Host name is the same things as set an Ip adress, but in test mode "127.0.0.1" or "localhost".
|
||||
* @param[in] _name Host name to connect.
|
||||
*/
|
||||
void setHostNane(const std::string& _name);
|
||||
void setHostNane(const etk::String& _name);
|
||||
/**
|
||||
* @brief Get the decriptive name hot the host
|
||||
* @return the string requested
|
||||
*/
|
||||
const std::string& getHostName() {
|
||||
const etk::String& getHostName() {
|
||||
return m_host;
|
||||
}
|
||||
private:
|
||||
|
@ -1,7 +1,7 @@
|
||||
/** @file
|
||||
* @author Edouard DUPIN
|
||||
* @copyright 2014, Edouard DUPIN, all right reserved
|
||||
* @license APACHE v2.0 (see license file)
|
||||
* @license MPL v2.0 (see license file)
|
||||
*/
|
||||
|
||||
#include <enet/debug.hpp>
|
||||
|
@ -1,7 +1,7 @@
|
||||
/** @file
|
||||
* @author Edouard DUPIN
|
||||
* @copyright 2014, Edouard DUPIN, all right reserved
|
||||
* @license APACHE v2.0 (see license file)
|
||||
* @license MPL v2.0 (see license file)
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
|
@ -1,15 +1,15 @@
|
||||
/** @file
|
||||
* @author Edouard DUPIN
|
||||
* @copyright 2014, Edouard DUPIN, all right reserved
|
||||
* @license APACHE v2.0 (see license file)
|
||||
* @license MPL v2.0 (see license file)
|
||||
*/
|
||||
|
||||
#include <enet/debug.hpp>
|
||||
#include <enet/WebSocket.hpp>
|
||||
#include <map>
|
||||
#include <etk/Map.hpp>
|
||||
#include <etk/stdTools.hpp>
|
||||
#include <cstring>
|
||||
#include <random>
|
||||
#include <etk/String.hpp>
|
||||
#include <etk/tool.hpp>
|
||||
#include <algue/base64.hpp>
|
||||
#include <algue/sha1.hpp>
|
||||
|
||||
@ -29,54 +29,44 @@ namespace enet {
|
||||
|
||||
enet::WebSocket::WebSocket() :
|
||||
m_connectionValidate(false),
|
||||
m_interface(nullptr),
|
||||
m_observer(nullptr),
|
||||
m_observerUriCheck(nullptr) {
|
||||
m_interface(null),
|
||||
m_observer(null),
|
||||
m_observerUriCheck(null) {
|
||||
|
||||
}
|
||||
|
||||
enet::WebSocket::WebSocket(enet::Tcp _connection, bool _isServer) :
|
||||
m_connectionValidate(false),
|
||||
m_interface(nullptr),
|
||||
m_observer(nullptr),
|
||||
m_observerUriCheck(nullptr) {
|
||||
_connection.setTCPNoDelay(true);
|
||||
if (_isServer == true) {
|
||||
ememory::SharedPtr<enet::HttpServer> interface = ememory::makeShared<enet::HttpServer>(std::move(_connection));
|
||||
m_interface = interface;
|
||||
if (interface != nullptr) {
|
||||
interface->connectHeader(this, &enet::WebSocket::onReceiveRequest);
|
||||
m_interface(null),
|
||||
m_observer(null),
|
||||
m_observerUriCheck(null) {
|
||||
setInterface(etk::move(_connection), _isServer);
|
||||
}
|
||||
|
||||
const etk::String& enet::WebSocket::getRemoteAddress() const {
|
||||
if (m_interface == null) {
|
||||
static const etk::String tmpOut;
|
||||
return tmpOut;
|
||||
}
|
||||
} else {
|
||||
ememory::SharedPtr<enet::HttpClient> interface = ememory::makeShared<enet::HttpClient>(std::move(_connection));
|
||||
m_interface = interface;
|
||||
if (interface != nullptr) {
|
||||
interface->connectHeader(this, &enet::WebSocket::onReceiveAnswer);
|
||||
}
|
||||
}
|
||||
if (m_interface == nullptr) {
|
||||
ENET_ERROR("can not create interface for the websocket");
|
||||
return;
|
||||
}
|
||||
m_interface->connectRaw(this, &enet::WebSocket::onReceiveData);
|
||||
return m_interface->getRemoteAddress();
|
||||
}
|
||||
|
||||
void enet::WebSocket::setInterface(enet::Tcp _connection, bool _isServer) {
|
||||
_connection.setTCPNoDelay(true);
|
||||
if (_isServer == true) {
|
||||
ememory::SharedPtr<enet::HttpServer> interface = ememory::makeShared<enet::HttpServer>(std::move(_connection));
|
||||
ememory::SharedPtr<enet::HttpServer> interface = ememory::makeShared<enet::HttpServer>(etk::move(_connection));
|
||||
m_interface = interface;
|
||||
if (interface != nullptr) {
|
||||
if (interface != null) {
|
||||
interface->connectHeader(this, &enet::WebSocket::onReceiveRequest);
|
||||
}
|
||||
} else {
|
||||
ememory::SharedPtr<enet::HttpClient> interface = ememory::makeShared<enet::HttpClient>(std::move(_connection));
|
||||
ememory::SharedPtr<enet::HttpClient> interface = ememory::makeShared<enet::HttpClient>(etk::move(_connection));
|
||||
m_interface = interface;
|
||||
if (interface != nullptr) {
|
||||
if (interface != null) {
|
||||
interface->connectHeader(this, &enet::WebSocket::onReceiveAnswer);
|
||||
}
|
||||
}
|
||||
if (m_interface == nullptr) {
|
||||
if (m_interface == null) {
|
||||
ENET_ERROR("can not create interface for the websocket");
|
||||
return;
|
||||
}
|
||||
@ -84,37 +74,38 @@ void enet::WebSocket::setInterface(enet::Tcp _connection, bool _isServer) {
|
||||
}
|
||||
|
||||
enet::WebSocket::~WebSocket() {
|
||||
if (m_interface == nullptr) {
|
||||
if (m_interface == null) {
|
||||
return;
|
||||
}
|
||||
stop(true);
|
||||
}
|
||||
|
||||
static std::string generateKey() {
|
||||
// create dynamic key:
|
||||
std::random_device rd;
|
||||
std::mt19937 e2(rd());
|
||||
std::uniform_real_distribution<> dist(0, 0xFF);
|
||||
static etk::String generateKey() {
|
||||
uint8_t dataKey[16];
|
||||
// create dynamic key:
|
||||
for (size_t iii=0; iii<16; ++iii) {
|
||||
dataKey[iii] = uint8_t(dist(e2));
|
||||
dataKey[iii] = uint8_t(etk::tool::urand(0,255));
|
||||
}
|
||||
return algue::base64::encode(dataKey, 16);
|
||||
}
|
||||
|
||||
static std::string generateCheckKey(const std::string& _key) {
|
||||
std::string out = _key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
|
||||
std::vector<uint8_t> keyData = algue::sha1::encode(out);
|
||||
static etk::String generateCheckKey(const etk::String& _key) {
|
||||
etk::String out = _key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
|
||||
etk::Vector<uint8_t> keyData = algue::sha1::encode(out);
|
||||
return algue::base64::encode(keyData);
|
||||
}
|
||||
|
||||
void enet::WebSocket::start(const std::string& _uri, const std::vector<std::string>& _listProtocols) {
|
||||
if (m_interface == nullptr) {
|
||||
void enet::WebSocket::start(const etk::String& _uri, const etk::Vector<etk::String>& _listProtocols) {
|
||||
if (m_interface == null) {
|
||||
ENET_ERROR("Nullptr interface ...");
|
||||
return;
|
||||
}
|
||||
if (m_interface->isServer() == true) {
|
||||
m_interface->start();
|
||||
} else {
|
||||
do {
|
||||
m_redirectInProgress = false;
|
||||
m_interface->start();
|
||||
if (m_interface->isServer() == false) {
|
||||
enet::HttpRequest req(enet::HTTPReqType::HTTP_GET);
|
||||
req.setProtocol(enet::HTTPProtocol::http_1_1);
|
||||
req.setUri(_uri);
|
||||
@ -126,7 +117,7 @@ void enet::WebSocket::start(const std::string& _uri, const std::vector<std::stri
|
||||
req.setKey("Sec-WebSocket-Version", "13");
|
||||
req.setKey("Pragma", "no-cache");
|
||||
req.setKey("Cache-Control", "no-cache");
|
||||
std::string protocolList;
|
||||
etk::String protocolList;
|
||||
for (auto &it : _listProtocols) {
|
||||
if (it == "") {
|
||||
continue;
|
||||
@ -140,28 +131,36 @@ void enet::WebSocket::start(const std::string& _uri, const std::vector<std::stri
|
||||
req.setKey("Sec-WebSocket-Protocol", protocolList);
|
||||
}
|
||||
ememory::SharedPtr<enet::HttpClient> interface = ememory::dynamicPointerCast<enet::HttpClient>(m_interface);
|
||||
if (interface != nullptr) {
|
||||
if (interface != null) {
|
||||
interface->setHeader(req);
|
||||
int32_t timeout = 500000; // 5 second
|
||||
while (timeout>=0) {
|
||||
while ( timeout>=0
|
||||
&& m_redirectInProgress == false) {
|
||||
if ( m_connectionValidate == true
|
||||
|| m_interface->isAlive() == false) {
|
||||
break;
|
||||
}
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
ethread::sleepMilliSeconds(10);
|
||||
timeout--;
|
||||
}
|
||||
if (m_redirectInProgress == true) {
|
||||
ENET_WARNING("Request a redirection (wait 500ms)");
|
||||
ethread::sleepMilliSeconds(500);
|
||||
ENET_WARNING("Request a redirection (wait-end)");
|
||||
} else {
|
||||
if ( m_connectionValidate == false
|
||||
|| m_interface->isAlive() == false) {
|
||||
ENET_ERROR("Connection refused by SERVER ...");
|
||||
}
|
||||
}
|
||||
}
|
||||
} while (m_redirectInProgress == true);
|
||||
}
|
||||
}
|
||||
|
||||
void enet::WebSocket::stop(bool _inThread) {
|
||||
ENET_DEBUG("Stop interface ...");
|
||||
if (m_interface == nullptr) {
|
||||
if (m_interface == null) {
|
||||
ENET_ERROR("Nullptr interface ...");
|
||||
return;
|
||||
}
|
||||
@ -185,15 +184,18 @@ void enet::WebSocket::onReceiveData(enet::Tcp& _connection) {
|
||||
ENET_VERBOSE("ReadRaw 2 [STOP]");
|
||||
return;
|
||||
}
|
||||
m_lastReceive = std::chrono::steady_clock::now();
|
||||
m_lastReceive = echrono::Steady::now();
|
||||
if ((opcode & 0x80) == 0) {
|
||||
ENET_ERROR("Multiple frames ... NOT managed ...");
|
||||
ENET_ERROR("Multiple frames ... NOT managed ... : " << (opcode & 0x80) << (opcode & 0x40) << (opcode & 0x20) << (opcode & 0x10) << (opcode & 0x08) << (opcode & 0x04) << (opcode & 0x02) << (opcode & 0x01));
|
||||
m_interface->stop(true);
|
||||
return;
|
||||
}
|
||||
int8_t size1 = 0;
|
||||
len = _connection.read(&size1, sizeof(uint8_t));
|
||||
if (len <= 0) {
|
||||
int32_t maxIteration = 50;
|
||||
// We must get the payload size in all case ... ==> otherwise it create problems
|
||||
while ( len <= 0
|
||||
&& maxIteration > 0) {
|
||||
if (len < 0) {
|
||||
if (_connection.getConnectionStatus() == enet::Tcp::status::link) {
|
||||
ENET_ERROR("Protocol error occured ...");
|
||||
@ -204,6 +206,12 @@ void enet::WebSocket::onReceiveData(enet::Tcp& _connection) {
|
||||
}
|
||||
ENET_ERROR("Time out ... ==> not managed ...");
|
||||
ENET_VERBOSE("ReadRaw 2 [STOP]");
|
||||
len = _connection.read(&size1, sizeof(uint8_t));
|
||||
maxIteration--;
|
||||
}
|
||||
if (maxIteration <= 0) {
|
||||
ENET_ERROR("Can not read the Socket >> auto kill");
|
||||
m_interface->stop(true);
|
||||
return;
|
||||
}
|
||||
uint64_t totalSize = size1 & 0x7F;
|
||||
@ -283,32 +291,32 @@ void enet::WebSocket::onReceiveData(enet::Tcp& _connection) {
|
||||
// check opcode:
|
||||
if ((opcode & 0x0F) == enet::websocket::OPCODE_FRAME_CLOSE) {
|
||||
// Close the conection by remote:
|
||||
ENET_INFO("Close connection by remote :");
|
||||
ENET_WARNING("Close connection by remote :");
|
||||
m_interface->stop(true);
|
||||
return;
|
||||
}
|
||||
if ((opcode & 0x0F) == enet::websocket::OPCODE_FRAME_PING) {
|
||||
// Close the conection by remote:
|
||||
ENET_DEBUG("Receive a ping (send a pong)");
|
||||
ENET_WARNING("Receive a ping (send a pong)");
|
||||
controlPong();
|
||||
return;
|
||||
}
|
||||
if ((opcode & 0x0F) == enet::websocket::OPCODE_FRAME_PONG) {
|
||||
// Close the conection by remote:
|
||||
ENET_DEBUG("Receive a pong");
|
||||
ENET_WARNING("Receive a pong");
|
||||
return;
|
||||
}
|
||||
if ((opcode & 0x0F) == enet::websocket::OPCODE_FRAME_TEXT) {
|
||||
// Close the conection by remote:
|
||||
ENET_DEBUG("Receive a Text(UTF-8) data " << m_buffer.size() << " Bytes");
|
||||
if (m_observer != nullptr) {
|
||||
ENET_WARNING("Receive a Text(UTF-8) data " << m_buffer.size() << " Bytes");
|
||||
if (m_observer != null) {
|
||||
m_observer(m_buffer, true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if ((opcode & 0x0F) == enet::websocket::OPCODE_FRAME_BINARY) {
|
||||
// Close the conection by remote:
|
||||
if (m_observer != nullptr) {
|
||||
if (m_observer != null) {
|
||||
m_observer(m_buffer, false);
|
||||
}
|
||||
return;
|
||||
@ -316,8 +324,8 @@ void enet::WebSocket::onReceiveData(enet::Tcp& _connection) {
|
||||
ENET_ERROR("ReadRaw [STOP] (no opcode manage ... " << int32_t(opcode & 0x0F));
|
||||
}
|
||||
|
||||
static std::string removeStartAndStopSpace(const std::string& _value) {
|
||||
std::string out;
|
||||
static etk::String removeStartAndStopSpace(const etk::String& _value) {
|
||||
etk::String out;
|
||||
out.reserve(_value.size());
|
||||
bool findSpace = false;
|
||||
for (auto &it : _value) {
|
||||
@ -337,7 +345,7 @@ static std::string removeStartAndStopSpace(const std::string& _value) {
|
||||
|
||||
void enet::WebSocket::onReceiveRequest(const enet::HttpRequest& _data) {
|
||||
ememory::SharedPtr<enet::HttpServer> interface = ememory::dynamicPointerCast<enet::HttpServer>(m_interface);
|
||||
if (interface == nullptr) {
|
||||
if (interface == null) {
|
||||
ENET_ERROR("Nullptr interface ...");
|
||||
return;
|
||||
}
|
||||
@ -375,16 +383,29 @@ void enet::WebSocket::onReceiveRequest(const enet::HttpRequest& _data) {
|
||||
return;
|
||||
}
|
||||
// parse all protocols:
|
||||
std::vector<std::string> listProtocol;
|
||||
etk::Vector<etk::String> listProtocol;
|
||||
if (_data.getKey("Sec-WebSocket-Protocol") != "") {
|
||||
listProtocol = etk::split(_data.getKey("Sec-WebSocket-Protocol"),',');
|
||||
for (size_t iii=0; iii<listProtocol.size(); ++iii) {
|
||||
listProtocol[iii] = removeStartAndStopSpace(listProtocol[iii]);
|
||||
}
|
||||
}
|
||||
|
||||
if (m_observerUriCheck != nullptr) {
|
||||
if (m_observerUriCheck(_data.getUri(), listProtocol) == false) {
|
||||
if (m_observerUriCheck != null) {
|
||||
etk::String ret = m_observerUriCheck(_data.getUri(), listProtocol);
|
||||
if (ret == "OK") {
|
||||
// Nothing to do
|
||||
} else if (ret.startWith("REDIRECT:") == true) {
|
||||
ENET_INFO("Request redirection of HTTP/WebSocket connection to : '" << ret.extract(9, ret.size()) << "'");
|
||||
enet::HttpAnswer answer(enet::HTTPAnswerCode::c307_temporaryRedirect);
|
||||
answer.setProtocol(enet::HTTPProtocol::http_1_1);
|
||||
answer.setKey("Location", ret.extract(9, ret.size()));
|
||||
interface->setHeader(answer);
|
||||
interface->stop(true);
|
||||
return;
|
||||
} else {
|
||||
if (ret != "CLOSE") {
|
||||
ENET_ERROR("UNKNOW return type of URI request: '" << ret << "'");
|
||||
}
|
||||
enet::HttpAnswer answer(enet::HTTPAnswerCode::c404_notFound);
|
||||
answer.setProtocol(enet::HTTPProtocol::http_1_1);
|
||||
answer.setKey("Connection", "close");
|
||||
@ -397,7 +418,7 @@ void enet::WebSocket::onReceiveRequest(const enet::HttpRequest& _data) {
|
||||
answer.setProtocol(enet::HTTPProtocol::http_1_1);
|
||||
answer.setKey("Upgrade", "websocket");
|
||||
answer.setKey("Connection", "Upgrade");
|
||||
std::string answerKey = generateCheckKey(_data.getKey("Sec-WebSocket-Key"));
|
||||
etk::String answerKey = generateCheckKey(_data.getKey("Sec-WebSocket-Key"));
|
||||
answer.setKey("Sec-WebSocket-Accept", answerKey);
|
||||
if (m_protocol != "") {
|
||||
answer.setKey("Sec-WebSocket-Protocol", m_protocol);
|
||||
@ -406,11 +427,19 @@ void enet::WebSocket::onReceiveRequest(const enet::HttpRequest& _data) {
|
||||
}
|
||||
|
||||
void enet::WebSocket::onReceiveAnswer(const enet::HttpAnswer& _data) {
|
||||
if (m_interface == nullptr) {
|
||||
if (m_interface == null) {
|
||||
ENET_ERROR("Nullptr interface ...");
|
||||
return;
|
||||
}
|
||||
_data.display();
|
||||
if (_data.getErrorCode() == enet::HTTPAnswerCode::c307_temporaryRedirect) {
|
||||
ENET_ERROR("Request connection redirection to '" << _data.getKey("Location") << "'");
|
||||
// We are a client mode, we need to recreate a TCP connection on the new remote interface
|
||||
// This is the generic way to accept a redirection
|
||||
m_redirectInProgress = true;
|
||||
m_interface->redirectTo(_data.getKey("Location"), true);
|
||||
return;
|
||||
}
|
||||
if (_data.getErrorCode() != enet::HTTPAnswerCode::c101_switchingProtocols) {
|
||||
ENET_ERROR("change protocol has not been accepted ... " << _data.getErrorCode() << " with message : " << _data.getHelp());
|
||||
m_interface->stop(true);
|
||||
@ -444,15 +473,10 @@ bool enet::WebSocket::configHeader(bool _isString, bool _mask) {
|
||||
m_sendBuffer.clear();
|
||||
m_sendBuffer.resize(ZEUS_BASE_OFFSET_HEADER, 0);
|
||||
if (_mask == true) {
|
||||
std::random_device rd;
|
||||
// Engine
|
||||
std::mt19937 e2(rd());
|
||||
// Distribtuions
|
||||
std::uniform_real_distribution<> dist(0, 0xFF);
|
||||
m_dataMask[0] = uint8_t(dist(e2));
|
||||
m_dataMask[1] = uint8_t(dist(e2));
|
||||
m_dataMask[2] = uint8_t(dist(e2));
|
||||
m_dataMask[3] = uint8_t(dist(e2));
|
||||
m_dataMask[0] = uint8_t(etk::tool::urand(0,255));
|
||||
m_dataMask[1] = uint8_t(etk::tool::urand(0,255));
|
||||
m_dataMask[2] = uint8_t(etk::tool::urand(0,255));
|
||||
m_dataMask[3] = uint8_t(etk::tool::urand(0,255));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -465,7 +489,7 @@ int32_t enet::WebSocket::writeData(uint8_t* _data, int32_t _len) {
|
||||
}
|
||||
|
||||
int32_t enet::WebSocket::send() {
|
||||
if (m_interface == nullptr) {
|
||||
if (m_interface == null) {
|
||||
ENET_ERROR("Nullptr interface ...");
|
||||
return -1;
|
||||
}
|
||||
@ -514,6 +538,7 @@ int32_t enet::WebSocket::send() {
|
||||
}
|
||||
|
||||
int32_t enet::WebSocket::write(const void* _data, int32_t _len, bool _isString, bool _mask) {
|
||||
ethread::UniqueLock lock(m_mutex);
|
||||
if (configHeader(_isString, _mask) == false) {
|
||||
return -1;
|
||||
}
|
||||
@ -522,39 +547,42 @@ int32_t enet::WebSocket::write(const void* _data, int32_t _len, bool _isString,
|
||||
}
|
||||
|
||||
void enet::WebSocket::controlPing() {
|
||||
if (m_interface == nullptr) {
|
||||
if (m_interface == null) {
|
||||
ENET_ERROR("Nullptr interface ...");
|
||||
return;
|
||||
}
|
||||
ethread::UniqueLock lock(m_mutex);
|
||||
uint8_t header = enet::websocket::FLAG_FIN
|
||||
| enet::websocket::OPCODE_FRAME_PING;
|
||||
m_lastSend = std::chrono::steady_clock::now();
|
||||
m_lastSend = echrono::Steady::now();
|
||||
m_interface->write(&header, sizeof(uint8_t));
|
||||
header = 0;
|
||||
m_interface->write(&header, sizeof(uint8_t));
|
||||
}
|
||||
|
||||
void enet::WebSocket::controlPong() {
|
||||
if (m_interface == nullptr) {
|
||||
if (m_interface == null) {
|
||||
ENET_ERROR("Nullptr interface ...");
|
||||
return;
|
||||
}
|
||||
ethread::UniqueLock lock(m_mutex);
|
||||
uint8_t header = enet::websocket::FLAG_FIN
|
||||
| enet::websocket::OPCODE_FRAME_PONG;
|
||||
m_lastSend = std::chrono::steady_clock::now();
|
||||
m_lastSend = echrono::Steady::now();
|
||||
m_interface->write(&header, sizeof(uint8_t));
|
||||
header = 0;
|
||||
m_interface->write(&header, sizeof(uint8_t));
|
||||
}
|
||||
|
||||
void enet::WebSocket::controlClose() {
|
||||
if (m_interface == nullptr) {
|
||||
if (m_interface == null) {
|
||||
ENET_ERROR("Nullptr interface ...");
|
||||
return;
|
||||
}
|
||||
ethread::UniqueLock lock(m_mutex);
|
||||
uint8_t header = enet::websocket::FLAG_FIN
|
||||
| enet::websocket::OPCODE_FRAME_CLOSE;
|
||||
m_lastSend = std::chrono::steady_clock::now();
|
||||
m_lastSend = echrono::Steady::now();
|
||||
m_interface->write(&header, sizeof(uint8_t));
|
||||
header = 0;
|
||||
m_interface->write(&header, sizeof(uint8_t));
|
||||
|
@ -1,30 +1,33 @@
|
||||
/** @file
|
||||
* @author Edouard DUPIN
|
||||
* @copyright 2014, Edouard DUPIN, all right reserved
|
||||
* @license APACHE v2.0 (see license file)
|
||||
* @license MPL v2.0 (see license file)
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <enet/Http.hpp>
|
||||
#include <ememory/memory.hpp>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <echrono/Steady.hpp>
|
||||
#include <etk/Vector.hpp>
|
||||
#include <etk/Map.hpp>
|
||||
|
||||
namespace enet {
|
||||
class WebSocket {
|
||||
protected:
|
||||
std::vector<uint8_t> m_sendBuffer;
|
||||
etk::Vector<uint8_t> m_sendBuffer;
|
||||
bool m_connectionValidate;
|
||||
ememory::SharedPtr<enet::Http> m_interface;
|
||||
std::vector<uint8_t> m_buffer;
|
||||
std::string m_checkKey;
|
||||
std::chrono::steady_clock::time_point m_lastReceive;
|
||||
std::chrono::steady_clock::time_point m_lastSend;
|
||||
etk::Vector<uint8_t> m_buffer;
|
||||
etk::String m_checkKey;
|
||||
echrono::Steady m_lastReceive;
|
||||
echrono::Steady m_lastSend;
|
||||
ethread::Mutex m_mutex;
|
||||
bool m_redirectInProgress = false;
|
||||
public:
|
||||
const std::chrono::steady_clock::time_point& getLastTimeReceive() {
|
||||
const echrono::Steady& getLastTimeReceive() {
|
||||
return m_lastReceive;
|
||||
}
|
||||
const std::chrono::steady_clock::time_point& getLastTimeSend() {
|
||||
const echrono::Steady& getLastTimeSend() {
|
||||
return m_lastSend;
|
||||
}
|
||||
public:
|
||||
@ -32,10 +35,10 @@ namespace enet {
|
||||
WebSocket(enet::Tcp _connection, bool _isServer=false);
|
||||
void setInterface(enet::Tcp _connection, bool _isServer=false);
|
||||
virtual ~WebSocket();
|
||||
void start(const std::string& _uri="", const std::vector<std::string>& _listProtocols=std::vector<std::string>());
|
||||
void start(const etk::String& _uri="", const etk::Vector<etk::String>& _listProtocols=etk::Vector<etk::String>());
|
||||
void stop(bool _inThread=false);
|
||||
bool isAlive() const {
|
||||
if (m_interface == nullptr) {
|
||||
if (m_interface == null) {
|
||||
return false;
|
||||
}
|
||||
return m_interface->isAlive();
|
||||
@ -44,13 +47,19 @@ namespace enet {
|
||||
void onReceiveRequest(const enet::HttpRequest& _data);
|
||||
void onReceiveAnswer(const enet::HttpAnswer& _data);
|
||||
protected:
|
||||
std::string m_protocol;
|
||||
etk::String m_protocol;
|
||||
public:
|
||||
void setProtocol(const std::string& _protocol) {
|
||||
void setProtocol(const etk::String& _protocol) {
|
||||
m_protocol = _protocol;
|
||||
}
|
||||
public:
|
||||
using Observer = std::function<void(std::vector<uint8_t>&, bool)>; //!< Define an Observer: function pointer
|
||||
/**
|
||||
* @brief Get the address of the connection source IP:port or empty string
|
||||
* @return string with the remote address name.
|
||||
*/
|
||||
const etk::String& getRemoteAddress() const;
|
||||
public:
|
||||
using Observer = etk::Function<void(etk::Vector<uint8_t>&, bool)>; //!< Define an Observer: function pointer
|
||||
protected:
|
||||
Observer m_observer;
|
||||
public:
|
||||
@ -61,8 +70,8 @@ namespace enet {
|
||||
* @param[in] _args Argument optinnal the user want to add.
|
||||
*/
|
||||
template<class CLASS_TYPE>
|
||||
void connect(CLASS_TYPE* _class, void (CLASS_TYPE::*_func)(std::vector<uint8_t>&, bool)) {
|
||||
m_observer = [=](std::vector<uint8_t>& _value, bool _isString){
|
||||
void connect(CLASS_TYPE* _class, void (CLASS_TYPE::*_func)(etk::Vector<uint8_t>&, bool)) {
|
||||
m_observer = [=](etk::Vector<uint8_t>& _value, bool _isString){
|
||||
(*_class.*_func)(_value, _isString);
|
||||
};
|
||||
}
|
||||
@ -71,7 +80,15 @@ namespace enet {
|
||||
}
|
||||
// Only server:
|
||||
public:
|
||||
using ObserverUriCheck = std::function<bool(const std::string&, const std::vector<std::string>&)>; //!< Define an Observer: function pointer
|
||||
/**
|
||||
* @brief Define an Observer: function pointer
|
||||
* @param[in] _uri Current HTTP URI
|
||||
* @param[in] _protocols List of protocol requested
|
||||
* @return "OK" Connection accepted ==> send header
|
||||
* @return "CLOSE" Close the current connection: 404
|
||||
* @return "REDIRECT:IP:port" Redirect at the specific IP and port
|
||||
*/
|
||||
using ObserverUriCheck = etk::Function<etk::String(const etk::String&, const etk::Vector<etk::String>&)>;
|
||||
protected:
|
||||
ObserverUriCheck m_observerUriCheck;
|
||||
public:
|
||||
@ -82,8 +99,8 @@ namespace enet {
|
||||
* @param[in] _args Argument optinnal the user want to add.
|
||||
*/
|
||||
template<class CLASS_TYPE>
|
||||
void connectUri(CLASS_TYPE* _class, bool (CLASS_TYPE::*_func)(const std::string&, const std::vector<std::string>&)) {
|
||||
m_observerUriCheck = [=](const std::string& _value, const std::vector<std::string>& _protocols){
|
||||
void connectUri(CLASS_TYPE* _class, etk::String (CLASS_TYPE::*_func)(const etk::String&, const etk::Vector<etk::String>&)) {
|
||||
m_observerUriCheck = [=](const etk::String& _value, const etk::Vector<etk::String>& _protocols){
|
||||
return (*_class.*_func)(_value, _protocols);
|
||||
};
|
||||
}
|
||||
@ -95,8 +112,20 @@ namespace enet {
|
||||
bool m_haveMask;
|
||||
uint8_t m_dataMask[4];
|
||||
public:
|
||||
ethread::UniqueLock getScopeLock() {
|
||||
return etk::move(ethread::UniqueLock(m_mutex));
|
||||
}
|
||||
/**
|
||||
* Compose the local header inside a temporary buffer ==> must lock external to prevent multiple simultaneous access
|
||||
*/
|
||||
bool configHeader(bool _isString=false, bool _mask= false);
|
||||
/**
|
||||
* CWrite data in a temporary buffer ==> must lock external to prevent multiple simultaneous access
|
||||
*/
|
||||
int32_t writeData(uint8_t* _data, int32_t _len);
|
||||
/**
|
||||
* Use temporary buffer to send it in the socket ==> must lock external to prevent multiple simultaneous access
|
||||
*/
|
||||
int32_t send();
|
||||
/**
|
||||
* @brief Write a chunk of data on the socket
|
||||
@ -104,8 +133,8 @@ namespace enet {
|
||||
* @param[in] _len Size that must be written socket
|
||||
* @return >0 byte size on the socket write
|
||||
* @return -1 an error occured.
|
||||
* @note: This function is locked ...
|
||||
*/
|
||||
//TODO : ...
|
||||
int32_t write(const void* _data, int32_t _len, bool _isString=false, bool _mask= false);
|
||||
/**
|
||||
* @brief Write a chunk of data on the socket
|
||||
@ -114,7 +143,7 @@ namespace enet {
|
||||
* @return >0 byte size on the socket write
|
||||
* @return -1 an error occured.
|
||||
*/
|
||||
int32_t write(const std::string& _data, bool _writeBackSlashZero = true) {
|
||||
int32_t write(const etk::String& _data, bool _writeBackSlashZero = true) {
|
||||
if (_data.size() == 0) {
|
||||
return 0;
|
||||
}
|
||||
@ -131,7 +160,7 @@ namespace enet {
|
||||
* @return -1 an error occured.
|
||||
*/
|
||||
template <class T>
|
||||
int32_t write(const std::vector<T>& _data) {
|
||||
int32_t write(const etk::Vector<T>& _data) {
|
||||
if (_data.size() == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
/** @file
|
||||
* @author Edouard DUPIN
|
||||
* @copyright 2014, Edouard DUPIN, all right reserved
|
||||
* @license APACHE v2.0 (see license file)
|
||||
* @license MPL v2.0 (see license file)
|
||||
*/
|
||||
|
||||
#include <enet/debug.hpp>
|
||||
|
@ -1,7 +1,7 @@
|
||||
/** @file
|
||||
* @author Edouard DUPIN
|
||||
* @copyright 2014, Edouard DUPIN, all right reserved
|
||||
* @license APACHE v2.0 (see license file)
|
||||
* @license MPL v2.0 (see license file)
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
/** @file
|
||||
* @author Edouard DUPIN
|
||||
* @copyright 2014, Edouard DUPIN, all right reserved
|
||||
* @license APACHE v2.0 (see license file)
|
||||
* @license MPL v2.0 (see license file)
|
||||
*/
|
||||
#include <enet/enet.hpp>
|
||||
#include <enet/debug.hpp>
|
||||
@ -19,7 +19,7 @@ static bool& getInitSatatus() {
|
||||
|
||||
void enet::init(int _argc, const char** _argv) {
|
||||
for (int32_t iii=0; iii<_argc; ++iii) {
|
||||
std::string value = _argv[iii];
|
||||
etk::String value = _argv[iii];
|
||||
if (etk::start_with(value, "--enet") == true) {
|
||||
ENET_ERROR("Unknow parameter type: '" << value << "'");
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
/** @file
|
||||
* @author Edouard DUPIN
|
||||
* @copyright 2014, Edouard DUPIN, all right reserved
|
||||
* @license APACHE v2.0 (see license file)
|
||||
* @license MPL v2.0 (see license file)
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
|
100
enet/pourcentEncoding.cpp
Normal file
100
enet/pourcentEncoding.cpp
Normal file
@ -0,0 +1,100 @@
|
||||
/** @file
|
||||
* @author Edouard DUPIN
|
||||
* @copyright 2014, Edouard DUPIN, all right reserved
|
||||
* @license MPL v2.0 (see license file)
|
||||
*/
|
||||
#include <enet/pourcentEncoding.hpp>
|
||||
#include <enet/debug.hpp>
|
||||
#include <etk/types.hpp>
|
||||
#include <etk/String.hpp>
|
||||
|
||||
const etk::String hexData = "0123456789ABCDEF";
|
||||
|
||||
etk::String enet::pourcentEncode(const etk::String& _data) {
|
||||
etk::String out;
|
||||
for (auto &it : _data) {
|
||||
if ( (it >= 'a' && it <= 'z')
|
||||
|| (it >= 'A' && it <= 'Z')
|
||||
|| (it >= '0' && it <= '9')
|
||||
|| it == '-'
|
||||
|| it == '_'
|
||||
|| it == '.'
|
||||
|| it == '~') {
|
||||
out += it;
|
||||
} else {
|
||||
out += "%";
|
||||
out += hexData[(uint32_t(it)>>4)&0x0F];
|
||||
out += hexData[uint32_t(it)&0x0F];
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
static int32_t convertStringHexToInt(const char _value) {
|
||||
if (_value >= 'a' && _value <= 'z') {
|
||||
return int32_t(_value) - int32_t('a') + 10;
|
||||
}
|
||||
if (_value >= 'A' && _value <= 'Z') {
|
||||
return int32_t(_value) - int32_t('A') + 10;
|
||||
}
|
||||
if (_value >= '0' && _value <= '9') {
|
||||
return int32_t(_value) - int32_t('0');
|
||||
}
|
||||
ENET_ERROR("Not a hexadecimal Value: '" << _value << "'");
|
||||
return 0;
|
||||
}
|
||||
|
||||
etk::String enet::pourcentDecode(const etk::String& _data) {
|
||||
etk::String out;
|
||||
for (size_t iii=0; iii<_data.size(); ++iii) {
|
||||
auto it = _data[iii];
|
||||
if (it == '%') {
|
||||
if (iii+2 < _data.size()) {
|
||||
auto val1 = convertStringHexToInt(_data[iii+1])<<4;
|
||||
val1 += convertStringHexToInt(_data[iii+2]);
|
||||
out += char(val1);
|
||||
iii += 2;
|
||||
} else {
|
||||
ENET_ERROR("can not convert pourcent ==> input size error: '" << _data << "'");
|
||||
return out;
|
||||
}
|
||||
} else {
|
||||
out += it;
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
etk::String enet::pourcentUriEncode(const etk::Map<etk::String, etk::String>& _data) {
|
||||
etk::String out;
|
||||
for (auto &it: _data) {
|
||||
if (out.empty() == false) {
|
||||
out += "&";
|
||||
}
|
||||
out += enet::pourcentEncode(it.first);
|
||||
if (it.second.empty() == false) {
|
||||
out += "=";
|
||||
out += enet::pourcentEncode(it.second);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
etk::Map<etk::String, etk::String> enet::pourcentUriDecode(const etk::String& _data) {
|
||||
etk::Map<etk::String, etk::String> out;
|
||||
auto listElements = etk::split(_data, '&');
|
||||
for (auto &it : listElements) {
|
||||
if (it.size() == 0) {
|
||||
continue;
|
||||
}
|
||||
auto offset = it.find('=');
|
||||
if (offset == etk::String::npos) {
|
||||
out.set(enet::pourcentDecode(it), "");
|
||||
continue;
|
||||
}
|
||||
out.set(enet::pourcentDecode(it.extract(0, offset)),
|
||||
enet::pourcentDecode(it.extract(offset+1, etk::String::npos)));
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
43
enet/pourcentEncoding.hpp
Normal file
43
enet/pourcentEncoding.hpp
Normal file
@ -0,0 +1,43 @@
|
||||
/** @file
|
||||
* @author Edouard DUPIN
|
||||
* @copyright 2014, Edouard DUPIN, all right reserved
|
||||
* @license MPL v2.0 (see license file)
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
|
||||
#include <etk/String.hpp>
|
||||
#include <etk/Map.hpp>
|
||||
|
||||
|
||||
namespace enet {
|
||||
/**
|
||||
* @brief Encode a string with transform the unvalid char in %XX.
|
||||
* @param[in] _data Raw string.
|
||||
* @return Encoded string.
|
||||
*/
|
||||
etk::String pourcentEncode(const etk::String& _data);
|
||||
|
||||
/**
|
||||
* @brief Decode a string with %XX with normal chars.
|
||||
* @param[in] _data Encoded string.
|
||||
* @return Raw string.
|
||||
*/
|
||||
etk::String pourcentDecode(const etk::String& _data);
|
||||
|
||||
/**
|
||||
* @brief Encode a map of string with %XX and "&" between element and = between key and value.
|
||||
* @param[in] _data Imput map to encode.
|
||||
* @return Encoded string.
|
||||
*/
|
||||
etk::String pourcentUriEncode(const etk::Map<etk::String, etk::String>& _data);
|
||||
|
||||
/**
|
||||
* @brief Encode a map of string with %XX and "&" between element and = between key and value.
|
||||
* @param[in] _data Encoded string like AAA=333&RRR=333&RRR
|
||||
* @return Te decoded map of values.
|
||||
*/
|
||||
etk::Map<etk::String, etk::String> pourcentUriDecode(const etk::String& _data);
|
||||
|
||||
}
|
||||
|
13
license.txt
13
license.txt
@ -1,13 +0,0 @@
|
||||
Copyright enet Edouard DUPIN
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
@ -1,5 +1,5 @@
|
||||
#!/usr/bin/python
|
||||
import lutin.debug as debug
|
||||
import realog.debug as debug
|
||||
import lutin.tools as tools
|
||||
|
||||
|
||||
@ -13,7 +13,7 @@ def get_desc():
|
||||
return "e-net TEST test software for enet"
|
||||
|
||||
def get_licence():
|
||||
return "APACHE-2"
|
||||
return "MPL-2"
|
||||
|
||||
def get_compagny_type():
|
||||
return "com"
|
||||
@ -28,7 +28,7 @@ def configure(target, my_module):
|
||||
my_module.add_path(".")
|
||||
my_module.add_depend([
|
||||
'enet',
|
||||
'gtest',
|
||||
'etest',
|
||||
'test-debug'
|
||||
])
|
||||
my_module.add_src_file([
|
||||
|
@ -1,5 +1,5 @@
|
||||
#!/usr/bin/python
|
||||
import lutin.debug as debug
|
||||
import realog.debug as debug
|
||||
import lutin.tools as tools
|
||||
|
||||
|
||||
@ -13,7 +13,7 @@ def get_desc():
|
||||
return "e-net TEST test software for enet"
|
||||
|
||||
def get_licence():
|
||||
return "APACHE-2"
|
||||
return "MPL-2"
|
||||
|
||||
def get_compagny_type():
|
||||
return "com"
|
||||
@ -28,7 +28,7 @@ def configure(target, my_module):
|
||||
my_module.add_path(".")
|
||||
my_module.add_depend([
|
||||
'enet',
|
||||
'gtest',
|
||||
'etest',
|
||||
'test-debug'
|
||||
])
|
||||
my_module.add_src_file([
|
||||
|
@ -1,5 +1,5 @@
|
||||
#!/usr/bin/python
|
||||
import lutin.debug as debug
|
||||
import realog.debug as debug
|
||||
import lutin.tools as tools
|
||||
|
||||
|
||||
@ -13,7 +13,7 @@ def get_desc():
|
||||
return "e-net TEST test software for enet"
|
||||
|
||||
def get_licence():
|
||||
return "APACHE-2"
|
||||
return "MPL-2"
|
||||
|
||||
def get_compagny_type():
|
||||
return "com"
|
||||
@ -28,7 +28,7 @@ def configure(target, my_module):
|
||||
my_module.add_path(".")
|
||||
my_module.add_depend([
|
||||
'enet',
|
||||
'gtest',
|
||||
'etest',
|
||||
'test-debug'
|
||||
])
|
||||
my_module.add_src_file([
|
||||
|
@ -1,5 +1,5 @@
|
||||
#!/usr/bin/python
|
||||
import lutin.debug as debug
|
||||
import realog.debug as debug
|
||||
import lutin.tools as tools
|
||||
|
||||
|
||||
@ -13,7 +13,7 @@ def get_desc():
|
||||
return "e-net TEST test software for enet"
|
||||
|
||||
def get_licence():
|
||||
return "APACHE-2"
|
||||
return "MPL-2"
|
||||
|
||||
def get_compagny_type():
|
||||
return "com"
|
||||
@ -28,7 +28,7 @@ def configure(target, my_module):
|
||||
my_module.add_path(".")
|
||||
my_module.add_depend([
|
||||
'enet',
|
||||
'gtest',
|
||||
'etest',
|
||||
'test-debug'
|
||||
])
|
||||
my_module.add_src_file([
|
||||
|
@ -1,5 +1,5 @@
|
||||
#!/usr/bin/python
|
||||
import lutin.debug as debug
|
||||
import realog.debug as debug
|
||||
import lutin.tools as tools
|
||||
|
||||
|
||||
@ -13,7 +13,7 @@ def get_desc():
|
||||
return "e-net TEST test software for enet"
|
||||
|
||||
def get_licence():
|
||||
return "APACHE-2"
|
||||
return "MPL-2"
|
||||
|
||||
def get_compagny_type():
|
||||
return "com"
|
||||
@ -28,7 +28,7 @@ def configure(target, my_module):
|
||||
my_module.add_path(".")
|
||||
my_module.add_depend([
|
||||
'enet',
|
||||
'gtest',
|
||||
'etest',
|
||||
'test-debug'
|
||||
])
|
||||
my_module.add_src_file([
|
||||
|
@ -1,5 +1,5 @@
|
||||
#!/usr/bin/python
|
||||
import lutin.debug as debug
|
||||
import realog.debug as debug
|
||||
import lutin.tools as tools
|
||||
|
||||
|
||||
@ -13,7 +13,7 @@ def get_desc():
|
||||
return "e-net TEST test software for enet"
|
||||
|
||||
def get_licence():
|
||||
return "APACHE-2"
|
||||
return "MPL-2"
|
||||
|
||||
def get_compagny_type():
|
||||
return "com"
|
||||
@ -28,7 +28,7 @@ def configure(target, my_module):
|
||||
my_module.add_path(".")
|
||||
my_module.add_depend([
|
||||
'enet',
|
||||
'gtest',
|
||||
'etest',
|
||||
'test-debug'
|
||||
])
|
||||
my_module.add_src_file([
|
||||
|
45
lutin_enet-test.py
Normal file
45
lutin_enet-test.py
Normal file
@ -0,0 +1,45 @@
|
||||
#!/usr/bin/python
|
||||
import realog.debug as debug
|
||||
import lutin.tools as tools
|
||||
|
||||
|
||||
def get_type():
|
||||
return "BINARY"
|
||||
|
||||
def get_sub_type():
|
||||
return "TEST"
|
||||
|
||||
def get_desc():
|
||||
return "e-net TEST basic backend"
|
||||
|
||||
def get_licence():
|
||||
return "MPL-2"
|
||||
|
||||
def get_compagny_type():
|
||||
return "com"
|
||||
|
||||
def get_compagny_name():
|
||||
return "atria-soft"
|
||||
|
||||
def get_maintainer():
|
||||
return "authors.txt"
|
||||
|
||||
def configure(target, my_module):
|
||||
my_module.add_path(".")
|
||||
my_module.add_depend([
|
||||
'enet',
|
||||
'etest',
|
||||
'test-debug'
|
||||
])
|
||||
my_module.add_src_file([
|
||||
'test/main-test.cpp',
|
||||
'test/main-unit-pourcentEncoding.cpp'
|
||||
])
|
||||
return True
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
#!/usr/bin/python
|
||||
import lutin.debug as debug
|
||||
import realog.debug as debug
|
||||
import lutin.tools as tools
|
||||
|
||||
|
||||
@ -10,7 +10,7 @@ def get_desc():
|
||||
return "TCP/UDP/HTTP/FTP interface"
|
||||
|
||||
def get_licence():
|
||||
return "APACHE-2"
|
||||
return "MPL-2"
|
||||
|
||||
def get_compagny_type():
|
||||
return "com"
|
||||
@ -42,6 +42,7 @@ def configure(target, my_module):
|
||||
'enet/Http.cpp',
|
||||
'enet/Ftp.cpp',
|
||||
'enet/WebSocket.cpp',
|
||||
'enet/pourcentEncoding.cpp',
|
||||
])
|
||||
my_module.add_header_file([
|
||||
'enet/enet.hpp',
|
||||
@ -53,6 +54,7 @@ def configure(target, my_module):
|
||||
'enet/Http.hpp',
|
||||
'enet/Ftp.hpp',
|
||||
'enet/WebSocket.hpp',
|
||||
'enet/pourcentEncoding.hpp',
|
||||
])
|
||||
if "Windows" in target.get_type():
|
||||
my_module.add_depend("ws2");
|
||||
|
@ -1,7 +1,7 @@
|
||||
/** @file
|
||||
* @author Edouard DUPIN
|
||||
* @copyright 2014, Edouard DUPIN, all right reserved
|
||||
* @license APACHE v2.0 (see license file)
|
||||
* @license MPL v2.0 (see license file)
|
||||
*/
|
||||
|
||||
#include <test-debug/debug.hpp>
|
||||
@ -15,7 +15,7 @@
|
||||
|
||||
|
||||
namespace appl {
|
||||
void onReceiveData(std::vector<uint8_t>& _data) {
|
||||
void onReceiveData(etk::Vector<uint8_t>& _data) {
|
||||
TEST_INFO("Receive Datas : " << _data.size() << " bytes");
|
||||
TEST_INFO("data:" << (char*)&_data[0] << "");
|
||||
}
|
||||
@ -25,7 +25,7 @@ int main(int _argc, const char *_argv[]) {
|
||||
etk::init(_argc, _argv);
|
||||
enet::init(_argc, _argv);
|
||||
for (int32_t iii=0; iii<_argc ; ++iii) {
|
||||
std::string data = _argv[iii];
|
||||
etk::String data = _argv[iii];
|
||||
if ( data == "-h"
|
||||
|| data == "--help") {
|
||||
TEST_PRINT(etk::getApplicationName() << " - help : ");
|
||||
@ -38,11 +38,11 @@ int main(int _argc, const char *_argv[]) {
|
||||
TEST_INFO("== Test HTTP client ==");
|
||||
TEST_INFO("==================================");
|
||||
// connect on TCP server:
|
||||
enet::Tcp tcpConnection = std::move(enet::connectTcpClient("127.0.0.1", 12345));
|
||||
enet::Tcp tcpConnection = etk::move(enet::connectTcpClient("127.0.0.1", 12345));
|
||||
// TODO : Check if connection is valid ...
|
||||
|
||||
// Create a HTTP connection in Client mode
|
||||
enet::HttpClient connection(std::move(tcpConnection));
|
||||
enet::HttpClient connection(etk::move(tcpConnection));
|
||||
// Set callbacks:
|
||||
connection.connect(appl::onReceiveData);
|
||||
|
||||
@ -53,7 +53,7 @@ int main(int _argc, const char *_argv[]) {
|
||||
connection.setHeader(req);
|
||||
|
||||
while (connection.isAlive() == true) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
ethread::sleepMilliSeconds((100));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
/** @file
|
||||
* @author Edouard DUPIN
|
||||
* @copyright 2014, Edouard DUPIN, all right reserved
|
||||
* @license APACHE v2.0 (see license file)
|
||||
* @license MPL v2.0 (see license file)
|
||||
*/
|
||||
|
||||
#include <test-debug/debug.hpp>
|
||||
@ -16,7 +16,7 @@
|
||||
|
||||
|
||||
namespace appl {
|
||||
void onReceiveData(std::vector<uint8_t>& _data, bool _isString) {
|
||||
void onReceiveData(etk::Vector<uint8_t>& _data, bool _isString) {
|
||||
TEST_INFO("Receive Datas : " << _data.size() << " bytes");
|
||||
if (_isString == true) {
|
||||
_data.resize(_data.size()+1);
|
||||
@ -32,7 +32,7 @@ int main(int _argc, const char *_argv[]) {
|
||||
etk::init(_argc, _argv);
|
||||
enet::init(_argc, _argv);
|
||||
for (int32_t iii=0; iii<_argc ; ++iii) {
|
||||
std::string data = _argv[iii];
|
||||
etk::String data = _argv[iii];
|
||||
if ( data == "-h"
|
||||
|| data == "--help") {
|
||||
TEST_PRINT(etk::getApplicationName() << " - help : ");
|
||||
@ -45,19 +45,19 @@ int main(int _argc, const char *_argv[]) {
|
||||
TEST_INFO("== Test WebSocket client ==");
|
||||
TEST_INFO("==================================");
|
||||
// connect on TCP server:
|
||||
enet::Tcp tcpConnection = std::move(enet::connectTcpClient("127.0.0.1", 12345));
|
||||
enet::Tcp tcpConnection = etk::move(enet::connectTcpClient("127.0.0.1", 12345));
|
||||
// TODO : Check if connection is valid ...
|
||||
|
||||
// Create a HTTP connection in Client mode
|
||||
enet::WebSocket connection(std::move(tcpConnection), false);
|
||||
enet::WebSocket connection(etk::move(tcpConnection), false);
|
||||
// Set callbacks:
|
||||
connection.connect(appl::onReceiveData);
|
||||
|
||||
// start http connection (the actual state is just TCP start ...)
|
||||
std::vector<std::string> protocols;
|
||||
protocols.push_back("test1526/1.0");
|
||||
protocols.push_back("test1526/1.5");
|
||||
protocols.push_back("Hello");
|
||||
etk::Vector<etk::String> protocols;
|
||||
protocols.pushBack("test1526/1.0");
|
||||
protocols.pushBack("test1526/1.5");
|
||||
protocols.pushBack("Hello");
|
||||
connection.start("/plop.txt", protocols);
|
||||
|
||||
// send some data to play ...
|
||||
@ -66,7 +66,7 @@ int main(int _argc, const char *_argv[]) {
|
||||
int32_t timeout = 20;
|
||||
while (connection.isAlive() == true
|
||||
&& timeout > 0) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
ethread::sleepMilliSeconds((100));
|
||||
timeout--;
|
||||
}
|
||||
return 0;
|
||||
|
@ -1,7 +1,7 @@
|
||||
/** @file
|
||||
* @author Edouard DUPIN
|
||||
* @copyright 2014, Edouard DUPIN, all right reserved
|
||||
* @license APACHE v2.0 (see license file)
|
||||
* @license MPL v2.0 (see license file)
|
||||
*/
|
||||
|
||||
#include <test-debug/debug.hpp>
|
||||
@ -10,14 +10,13 @@
|
||||
#include <enet/TcpClient.hpp>
|
||||
#include <enet/Http.hpp>
|
||||
#include <etk/etk.hpp>
|
||||
#include <iostream>
|
||||
#include <etk/stdTools.hpp>
|
||||
|
||||
int main(int _argc, const char *_argv[]) {
|
||||
etk::init(_argc, _argv);
|
||||
enet::init(_argc, _argv);
|
||||
for (int32_t iii=0; iii<_argc ; ++iii) {
|
||||
std::string data = _argv[iii];
|
||||
etk::String data = _argv[iii];
|
||||
if ( data == "-h"
|
||||
|| data == "--help") {
|
||||
TEST_PRINT(etk::getApplicationName() << " - help : ");
|
||||
@ -31,7 +30,7 @@ int main(int _argc, const char *_argv[]) {
|
||||
TEST_INFO("==================================");
|
||||
// client mode ...
|
||||
// connect on TCP server:
|
||||
enet::Tcp connection = std::move(enet::connectTcpClient("127.0.0.1", 12345));
|
||||
enet::Tcp connection = etk::move(enet::connectTcpClient("127.0.0.1", 12345));
|
||||
TEST_INFO("CLIENT connect ...");
|
||||
if (connection.getConnectionStatus() != enet::Tcp::status::link) {
|
||||
TEST_ERROR("can not link to the socket...");
|
||||
@ -45,7 +44,7 @@ int main(int _argc, const char *_argv[]) {
|
||||
int32_t len = connection.read(data, 1024);
|
||||
TEST_INFO("read len=" << len << " data='" << data << "'");
|
||||
//if (data[len-1] == '2') {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(delay));
|
||||
ethread::sleepMilliSeconds((delay));
|
||||
delay--;
|
||||
if (delay == 0) {
|
||||
delay = 500;
|
||||
|
@ -1,7 +1,7 @@
|
||||
/** @file
|
||||
* @author Edouard DUPIN
|
||||
* @copyright 2014, Edouard DUPIN, all right reserved
|
||||
* @license APACHE v2.0 (see license file)
|
||||
* @license MPL v2.0 (see license file)
|
||||
*/
|
||||
|
||||
#include <test-debug/debug.hpp>
|
||||
@ -14,7 +14,7 @@
|
||||
|
||||
#include <etk/stdTools.hpp>
|
||||
namespace appl {
|
||||
void onReceiveData(enet::HttpServer* _interface, std::vector<uint8_t>& _data) {
|
||||
void onReceiveData(enet::HttpServer* _interface, etk::Vector<uint8_t>& _data) {
|
||||
TEST_INFO("Receive Datas : " << _data.size() << " bytes");
|
||||
}
|
||||
void onReceiveHeader(enet::HttpServer* _interface, const enet::HttpRequest& _data) {
|
||||
@ -23,8 +23,8 @@ namespace appl {
|
||||
if (_data.getType() == enet::HTTPReqType::HTTP_GET) {
|
||||
if (_data.getUri() == "plop.txt") {
|
||||
enet::HttpAnswer answer(enet::HTTPAnswerCode::c200_ok);
|
||||
std::string data = "<html><head></head></body>coucou</body></html>";
|
||||
answer.setKey("Content-Length", etk::to_string(data.size()));
|
||||
etk::String data = "<html><head></head></body>coucou</body></html>";
|
||||
answer.setKey("Content-Length", etk::toString(data.size()));
|
||||
_interface->setHeader(answer);
|
||||
_interface->write(data);
|
||||
_interface->stop(true);
|
||||
@ -42,7 +42,7 @@ int main(int _argc, const char *_argv[]) {
|
||||
etk::init(_argc, _argv);
|
||||
enet::init(_argc, _argv);
|
||||
for (int32_t iii=0; iii<_argc ; ++iii) {
|
||||
std::string data = _argv[iii];
|
||||
etk::String data = _argv[iii];
|
||||
if ( data == "-h"
|
||||
|| data == "--help") {
|
||||
TEST_PRINT(etk::getApplicationName() << " - help : ");
|
||||
@ -62,16 +62,16 @@ int main(int _argc, const char *_argv[]) {
|
||||
// Start listening ...
|
||||
interface.link();
|
||||
// Wait a new connection ..
|
||||
enet::Tcp tcpConnection = std::move(interface.waitNext());
|
||||
enet::Tcp tcpConnection = etk::move(interface.waitNext());
|
||||
// Free Connected port
|
||||
interface.unlink();
|
||||
// TODO : Check if connection is valid ...
|
||||
|
||||
// Create a HTTP connection in Server mode
|
||||
enet::HttpServer connection(std::move(tcpConnection));
|
||||
enet::HttpServer connection(etk::move(tcpConnection));
|
||||
enet::HttpServer* tmp = &connection;
|
||||
// Set callbacks:
|
||||
connection.connect([=](std::vector<uint8_t>& _value){
|
||||
connection.connect([=](etk::Vector<uint8_t>& _value){
|
||||
appl::onReceiveData(tmp, _value);
|
||||
});
|
||||
connection.connectHeader([=](const enet::HttpRequest& _value){
|
||||
@ -82,7 +82,7 @@ int main(int _argc, const char *_argv[]) {
|
||||
connection.start();
|
||||
|
||||
while (connection.isAlive() == true) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
ethread::sleepMilliSeconds((100));
|
||||
}
|
||||
|
||||
|
||||
@ -97,8 +97,8 @@ int main(int _argc, const char *_argv[]) {
|
||||
|
||||
TEST_INFO("----------------------------");
|
||||
TEST_INFO("POST data : ");
|
||||
std::map<std::string, std::string> values;
|
||||
values.insert(std::make_pair<std::string, std::string>("plop", "valuePlop"));
|
||||
etk::Map<etk::String, etk::String> values;
|
||||
values.insert(etk::makePair<etk::String, etk::String>("plop", "valuePlop"));
|
||||
if (connection.post("", values) == false) {
|
||||
TEST_ERROR("can not POST data...");
|
||||
return -1;
|
||||
@ -107,7 +107,7 @@ int main(int _argc, const char *_argv[]) {
|
||||
|
||||
TEST_INFO("----------------------------");
|
||||
TEST_INFO("POST xml : ");
|
||||
if (connection.post("", "text/xml; charset=utf-8", "<plop><string>value1</string></plop>") == false) {
|
||||
if (connection.post("", "text/xml; charset=utf-8", "<plop><etk/String.hpp>value1</string></plop>") == false) {
|
||||
TEST_ERROR("can not POST XML data...");
|
||||
return -1;
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
/** @file
|
||||
* @author Edouard DUPIN
|
||||
* @copyright 2014, Edouard DUPIN, all right reserved
|
||||
* @license APACHE v2.0 (see license file)
|
||||
* @license MPL v2.0 (see license file)
|
||||
*/
|
||||
|
||||
#include <test-debug/debug.hpp>
|
||||
@ -15,7 +15,7 @@
|
||||
|
||||
#include <etk/stdTools.hpp>
|
||||
namespace appl {
|
||||
void onReceiveData(enet::WebSocket* _interface, std::vector<uint8_t>& _data, bool _isString) {
|
||||
void onReceiveData(enet::WebSocket* _interface, etk::Vector<uint8_t>& _data, bool _isString) {
|
||||
TEST_INFO("Receive Datas : " << _data.size() << " bytes");
|
||||
if (_isString == true) {
|
||||
_data.resize(_data.size()+1);
|
||||
@ -26,7 +26,7 @@ namespace appl {
|
||||
TEST_INFO("binary data: ... ");
|
||||
}
|
||||
}
|
||||
bool onReceiveUri(enet::WebSocket* _interface, const std::string& _uri, const std::vector<std::string>& _protocols) {
|
||||
bool onReceiveUri(enet::WebSocket* _interface, const etk::String& _uri, const etk::Vector<etk::String>& _protocols) {
|
||||
TEST_INFO("Receive Header uri: " << _uri);
|
||||
for (auto &it : _protocols) {
|
||||
if (it == "test1526/1.5") {
|
||||
@ -45,7 +45,7 @@ int main(int _argc, const char *_argv[]) {
|
||||
etk::init(_argc, _argv);
|
||||
enet::init(_argc, _argv);
|
||||
for (int32_t iii=0; iii<_argc ; ++iii) {
|
||||
std::string data = _argv[iii];
|
||||
etk::String data = _argv[iii];
|
||||
if ( data == "-h"
|
||||
|| data == "--help") {
|
||||
TEST_PRINT(etk::getApplicationName() << " - help : ");
|
||||
@ -65,19 +65,19 @@ int main(int _argc, const char *_argv[]) {
|
||||
// Start listening ...
|
||||
interface.link();
|
||||
// Wait a new connection ..
|
||||
enet::Tcp tcpConnection = std::move(interface.waitNext());
|
||||
enet::Tcp tcpConnection = etk::move(interface.waitNext());
|
||||
// Free Connected port
|
||||
interface.unlink();
|
||||
// TODO : Check if connection is valid ...
|
||||
|
||||
// Create a HTTP connection in Server mode
|
||||
enet::WebSocket connection(std::move(tcpConnection), true);
|
||||
enet::WebSocket connection(etk::move(tcpConnection), true);
|
||||
enet::WebSocket* tmp = &connection;
|
||||
// Set callbacks:
|
||||
connection.connect([=](std::vector<uint8_t>& _value, bool _isString){
|
||||
connection.connect([=](etk::Vector<uint8_t>& _value, bool _isString){
|
||||
appl::onReceiveData(tmp, _value, _isString);
|
||||
});
|
||||
connection.connectUri([=](const std::string& _value, const std::vector<std::string>& _protocols){
|
||||
connection.connectUri([=](const etk::String& _value, const etk::Vector<etk::String>& _protocols){
|
||||
return appl::onReceiveUri(tmp, _value, _protocols);
|
||||
});
|
||||
|
||||
@ -85,7 +85,7 @@ int main(int _argc, const char *_argv[]) {
|
||||
connection.start();
|
||||
|
||||
while (connection.isAlive() == true) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
ethread::sleepMilliSeconds((100));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
/** @file
|
||||
* @author Edouard DUPIN
|
||||
* @copyright 2014, Edouard DUPIN, all right reserved
|
||||
* @license APACHE v2.0 (see license file)
|
||||
* @license MPL v2.0 (see license file)
|
||||
*/
|
||||
|
||||
#include <test-debug/debug.hpp>
|
||||
@ -17,7 +17,7 @@ int main(int _argc, const char *_argv[]) {
|
||||
etk::init(_argc, _argv);
|
||||
enet::init(_argc, _argv);
|
||||
for (int32_t iii=0; iii<_argc ; ++iii) {
|
||||
std::string data = _argv[iii];
|
||||
etk::String data = _argv[iii];
|
||||
if ( data == "-h"
|
||||
|| data == "--help") {
|
||||
TEST_PRINT(etk::getApplicationName() << " - help : ");
|
||||
@ -37,13 +37,13 @@ int main(int _argc, const char *_argv[]) {
|
||||
// Start listening ...
|
||||
interface.link();
|
||||
// Wait a new connection ..
|
||||
enet::Tcp tcpConnection = std::move(interface.waitNext());
|
||||
enet::Tcp tcpConnection = etk::move(interface.waitNext());
|
||||
// Free Connected port
|
||||
interface.unlink();
|
||||
|
||||
int32_t iii = 0;
|
||||
while (tcpConnection.getConnectionStatus() == enet::Tcp::status::link) {
|
||||
int32_t len = tcpConnection.write("plop" + etk::to_string(iii));
|
||||
int32_t len = tcpConnection.write("plop" + etk::toString(iii));
|
||||
TEST_INFO("write len=" << len);
|
||||
char data[1024];
|
||||
len = tcpConnection.read(data, 1024);
|
||||
|
18
test/main-test.cpp
Normal file
18
test/main-test.cpp
Normal file
@ -0,0 +1,18 @@
|
||||
/** @file
|
||||
* @author Edouard DUPIN
|
||||
* @copyright 2018, Edouard DUPIN, all right reserved
|
||||
* @license MPL v2.0 (see license file)
|
||||
*/
|
||||
|
||||
#include <etk/types.hpp>
|
||||
#include <test-debug/debug.hpp>
|
||||
|
||||
|
||||
#include <etest/etest.hpp>
|
||||
|
||||
int main(int argc, const char *argv[]) {
|
||||
// init test engine:
|
||||
etest::init(argc, argv);
|
||||
TEST_INFO("TEST ETK");
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
38
test/main-unit-pourcentEncoding.cpp
Normal file
38
test/main-unit-pourcentEncoding.cpp
Normal file
@ -0,0 +1,38 @@
|
||||
/** @file
|
||||
* @author Edouard DUPIN
|
||||
* @copyright 2018, Edouard DUPIN, all right reserved
|
||||
* @license MPL v2.0 (see license file)
|
||||
*/
|
||||
|
||||
#include <etk/types.hpp>
|
||||
#include <test-debug/debug.hpp>
|
||||
#include <etest/etest.hpp>
|
||||
#include <enet/pourcentEncoding.hpp>
|
||||
|
||||
TEST(pourcentSerializer, stringEncode) {
|
||||
EXPECT_EQ(enet::pourcentEncode("hello"), "hello");
|
||||
EXPECT_EQ(enet::pourcentEncode("hello@plop plouf-_~"), "hello%40plop%20plouf-_~");
|
||||
}
|
||||
|
||||
TEST(pourcentSerializer, stringDecode) {
|
||||
EXPECT_EQ(enet::pourcentDecode("hello"), "hello");
|
||||
EXPECT_EQ(enet::pourcentDecode("hello%40plop%20plouf-_~"), "hello@plop plouf-_~");
|
||||
}
|
||||
|
||||
TEST(pourcentSerializer, mapEncode) {
|
||||
etk::Map<etk::String, etk::String> data;
|
||||
data.set("hello", "value1");
|
||||
data.set("simpleKey", "");
|
||||
data.set("special Key", "coucou");
|
||||
data.set("email", "you@example.com");
|
||||
EXPECT_EQ(enet::pourcentUriEncode(data), "email=you%40example.com&hello=value1&simpleKey&special%20Key=coucou");
|
||||
}
|
||||
|
||||
TEST(pourcentSerializer, mapDecode) {
|
||||
auto data = enet::pourcentUriDecode("hello=value1&simpleKey&special%20Key=coucou&email=you%40example.com");
|
||||
EXPECT_EQ(data.size(), 4);
|
||||
EXPECT_EQ(data["hello"], "value1");
|
||||
EXPECT_EQ(data["simpleKey"], "");
|
||||
EXPECT_EQ(data["special Key"], "coucou");
|
||||
EXPECT_EQ(data["email"], "you@example.com");
|
||||
}
|
@ -1 +1 @@
|
||||
0.4.0
|
||||
1.0.0
|
Loading…
x
Reference in New Issue
Block a user