Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Revision 0:a21b8a29df03, committed 2021-10-09
- Comitter:
- Hiroaki_Okoshi
- Date:
- Sat Oct 09 14:23:23 2021 +0000
- Commit message:
- Sample web server via WiFi program. for mbed os 5.15
Changed in this revision
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/.mbed Sat Oct 09 14:23:23 2021 +0000 @@ -0,0 +1,4 @@ +ROOT=. +TARGET=UBLOX_EVK_ODIN_W2 +TARGET_CODE=1236 +TARGET_SERIAL=066DFF555654677067075035
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/CONTRIBUTING.md Sat Oct 09 14:23:23 2021 +0000 @@ -0,0 +1,5 @@ +# Contributing to Mbed OS + +Mbed OS is an open-source, device software platform for the Internet of Things. Contributions are an important part of the platform, and our goal is to make it as simple as possible to become a contributor. + +To encourage productive collaboration, as well as robust, consistent and maintainable code, we have a set of guidelines for [contributing to Mbed OS](https://os.mbed.com/docs/mbed-os/latest/contributing/index.html).
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/Jenkinsfile Sat Oct 09 14:23:23 2021 +0000
@@ -0,0 +1,90 @@
+properties ([[$class: 'ParametersDefinitionProperty', parameterDefinitions: [
+ [$class: 'StringParameterDefinition', name: 'mbed_os_revision', defaultValue: '', description: 'Revision of mbed-os to build. Use format "pull/PR-NUMBER/head" to access mbed-os PR']
+ ]]])
+
+if (params.mbed_os_revision == '') {
+ echo 'Use mbed OS revision from mbed-os.lib'
+} else {
+ echo "Use mbed OS revisiong ${params.mbed_os_revision}"
+ if (params.mbed_os_revision.matches('pull/\\d+/head')) {
+ echo "Revision is a Pull Request"
+ }
+}
+
+// List of targets with supported RF shields to compile
+def targets = [
+ "UBLOX_EVK_ODIN_W2": ["internal"],
+ //"REALTEK_RTL8195AM": ["internal"], // Disabled from Mbed OS after ArmCC6
+ "K64F": ["esp8266-driver"],
+ "NUCLEO_F429ZI": ["esp8266-driver"]
+ ]
+
+// Map toolchains to compilers
+def toolchains = [
+ ARM: "armcc",
+ GCC_ARM: "arm-none-eabi-gcc",
+ IAR: "IAR-linux"
+ ]
+
+// Supported RF shields
+def radioshields = [
+ "internal",
+ "esp8266-driver"
+ ]
+
+def stepsForParallel = [:]
+
+// Jenkins pipeline does not support map.each, we need to use oldschool for loop
+for (int i = 0; i < targets.size(); i++) {
+ for(int j = 0; j < toolchains.size(); j++) {
+ for(int k = 0; k < radioshields.size(); k++) {
+ def target = targets.keySet().asList().get(i)
+ def allowed_shields = targets.get(target)
+ def toolchain = toolchains.keySet().asList().get(j)
+ def compilerLabel = toolchains.get(toolchain)
+ def radioshield = radioshields.get(k)
+
+ def stepName = "${target} ${toolchain} ${radioshield}"
+ if(allowed_shields.contains(radioshield)) {
+ stepsForParallel[stepName] = buildStep(target, compilerLabel, toolchain, radioshield)
+ }
+ }
+ }
+}
+
+timestamps {
+ parallel stepsForParallel
+}
+
+def buildStep(target, compilerLabel, toolchain, radioShield) {
+ return {
+ stage ("${target}_${compilerLabel}_${radioShield}") {
+ node ("${compilerLabel}") {
+ deleteDir()
+ dir("mbed-os-example-wifi") {
+ checkout scm
+ def config_file = "mbed_app.json"
+
+ // Set mbed-os to revision received as parameter
+ execute ("mbed deploy --protocol ssh")
+ if (params.mbed_os_revision != '') {
+ dir ("mbed-os") {
+ if (params.mbed_os_revision.matches('pull/\\d+/head')) {
+ execute("git fetch origin ${params.mbed_os_revision}:PR")
+ execute("git checkout PR")
+ } else {
+ execute ("git checkout ${params.mbed_os_revision}")
+ }
+ }
+ }
+ execute("mbed new .")
+
+ execute ("mbed compile --build out/${target}_${toolchain}_${radioShield}/ -m ${target} -t ${toolchain} -c --app-config ${config_file}")
+ }
+ stash name: "${target}_${toolchain}_${radioShield}", includes: '**/mbed-os-example-wifi.bin'
+ archive '**/mbed-os-example-wifi.bin'
+ step([$class: 'WsCleanup'])
+ }
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/LICENSE Sat Oct 09 14:23:23 2021 +0000
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "{}"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright {yyyy} {name of copyright owner}
+
+ 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.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/README.md Sat Oct 09 14:23:23 2021 +0000
@@ -0,0 +1,125 @@
+# mbed-os-example-wifi #
+
+Wi-Fi example for Mbed OS
+
+(Note: To see this example in a rendered form you can import into the Arm Mbed Online Compiler, please see [the documentation](https://os.mbed.com/docs/mbed-os/latest/apis/wi-fi.html#wi-fi-example).)
+
+## Getting started with the Wi-Fi API ##
+
+This is an example of a Wi-Fi application using the Wi-Fi APIs that [Mbed OS](https://github.com/ARMmbed/mbed-os) provides.
+
+The program brings up the Wi-Fi and the underlying network interface and uses it to scan available networks, connects to a network and prints interface and connection details.
+
+For more information about Wi-Fi APIs, please visit the [Mbed OS Wi-Fi](https://os.mbed.com/docs/latest/reference/wi-fi.html) documentation.
+
+### Supported hardware ###
+
+* All Mbed OS boards with build-in Wi-Fi module:
+ * [u-blox ODIN-W2](https://os.mbed.com/platforms/ublox-EVK-ODIN-W2/)
+ * [Realtek RTL8195AM](https://os.mbed.com/platforms/REALTEK-RTL8195AM/)
+ * [ST DISCO IOT board](https://os.mbed.com/platforms/ST-Discovery-L475E-IOT01A/) with integrated [ISM43362 WiFi Inventek module](https://github.com/ARMmbed/wifi-ism43362).
+ * [ST DISCO_F413ZH board](https://os.mbed.com/platforms/ST-Discovery-F413H/) with integrated [ISM43362 WiFi Inventek module](https://github.com/ARMmbed/wifi-ism43362).
+ * [Advantech WISE-150](https://os.mbed.com/modules/advantech-wise-1530/)
+ * USI WM-BN-BM-22
+ * MxChip EMW3166
+* Boards with external WiFi shields.
+ * [NUCLEO-F401RE](https://os.mbed.com/platforms/ST-Nucleo-F401RE/) with [X-NUCLEO-IDW04A1](http://www.st.com/content/st_com/en/products/ecosystems/stm32-open-development-environment/stm32-nucleo-expansion-boards/stm32-ode-connect-hw/x-nucleo-idw04a1.html) Wi-Fi expansion board using pins D8 and D2 _(of the Arduino connector)_.
+ * [NUCLEO-F401RE](https://os.mbed.com/platforms/ST-Nucleo-F401RE/) with [X-NUCLEO-IDW01M1](https://os.mbed.com/components/X-NUCLEO-IDW01M1/) Wi-Fi expansion board using pins PA_9 and PA_10 _(of the Morpho connector)_.
+ * [NUCLEO-F429ZI](https://os.mbed.com/platforms/ST-Nucleo-F429ZI/) with ESP8266-01 module using pins D1 and D0.
+ * Other Mbed targets with an ESP8266 module, [X-NUCLEO-IDW04A1](http://www.st.com/content/st_com/en/products/ecosystems/stm32-open-development-environment/stm32-nucleo-expansion-boards/stm32-ode-connect-hw/x-nucleo-idw04a1.html) or [X-NUCLEO-IDW01M1](https://os.mbed.com/components/X-NUCLEO-IDW01M1/) expansion board.
+
+#### Adding connectivity driver
+
+If the target does not have internal WiFi driver, or Mbed OS does not supply one, you need to add driver to your application and configure it to provide default WiFi interface.
+
+```
+mbed add <driver>
+```
+
+For example adding ISM43362 driver `mbed add wifi-ism43362` or X-Nucleo-IDW01M1 driver `mbed add wifi-x-nucleo-idw01m1`
+The ESP8266 driver is already suplied by Mbed OS.
+
+Then pin names need to be configured as instructed in the drivers README file.
+
+## Getting started ##
+
+1. Import the example.
+
+ ```
+ mbed import mbed-os-example-wifi
+ cd mbed-os-example-wifi
+ ```
+
+1. Configure the Wi-Fi shield and settings.
+ Edit ```mbed_app.json``` to include the correct Wi-Fi shield, SSID and password:
+
+```json
+{
+ "config": {
+ "wifi-ssid": {
+ "help": "WiFi SSID",
+ "value": "\"SSID\""
+ },
+ "wifi-password": {
+ "help": "WiFi Password",
+ "value": "\"PASSWORD\""
+ }
+ },
+ "target_overrides": {
+ "*": {
+ "platform.stdio-convert-newlines": true,
+ "esp8266.provide-default" : false
+ }
+ }
+}
+```
+
+ For build-in WiFi, you do not need to set any `provide-default` values. Those are required
+ if you use external WiFi shield.
+
+ Sample ```mbed_app.json``` files are provided for ESP8266 (```mbed_app_esp8266.json```), X-NUCLEO-IDW04A1 (```mbed_app_idw04a1.json```) and X-NUCLEO-IDW01M1 (```mbed_app_idw01m1```).
+
+
+1. Compile and generate binary.
+ For example, for `GCC`:
+ ```
+ mbed compile -t GCC_ARM -m UBLOX_EVK_ODIN_W2
+ ```
+
+1. Open a serial console session with the target platform using the following parameters:
+ * **Baud rate:** 9600
+ * **Data bits:** 8
+ * **Stop bits:** 1
+ * **Parity:** None
+
+1. Copy or drag the application `mbed-os-example-wifi.bin` in the folder `mbed-os-example-wifi/BUILD/<TARGET NAME>/<PLATFORM NAME>` onto the target board.
+
+1. The serial console should display a similar output to below, indicating a successful Wi-Fi connection:
+ ```
+ WiFi example
+
+ Scan:
+ Network: Dave Hot Spot secured: Unknown BSSID: 00:01:02:03:04:05 RSSI: -58 Ch: 1
+ 1 network available.
+
+ Connecting...
+ Success
+
+ MAC: 00:01:02:03:04:05
+ IP: 192.168.0.5
+ Netmask: 255.255.255.0
+ Gateway: 192.168.0.1
+ RSSI: -27
+
+ Done
+ ```
+
+## Troubleshooting
+
+If you have problems, you can review the [documentation](https://os.mbed.com/docs/latest/tutorials/debugging.html) for suggestions on what could be wrong and how to fix it.
+
+### License and contributions
+
+The software is provided under Apache-2.0 license. Contributions to this project are accepted under the same license. Please see contributing.md for more info.
+
+This project contains code from other projects. The original license text is included in those source files. They must comply with our license guide.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mbed-http/http_parser/http_parser.c Sat Oct 09 14:23:23 2021 +0000
@@ -0,0 +1,2470 @@
+/* Based on src/http/ngx_http_parse.c from NGINX copyright Igor Sysoev
+ *
+ * Additional changes are licensed under the same terms as NGINX and
+ * copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+#include "http_parser.h"
+#include <assert.h>
+#include <stddef.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+
+#ifndef ULLONG_MAX
+# define ULLONG_MAX ((uint64_t) -1) /* 2^64-1 */
+#endif
+
+#ifndef MIN
+# define MIN(a,b) ((a) < (b) ? (a) : (b))
+#endif
+
+#ifndef ARRAY_SIZE
+# define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
+#endif
+
+#ifndef BIT_AT
+# define BIT_AT(a, i) \
+ (!!((unsigned int) (a)[(unsigned int) (i) >> 3] & \
+ (1 << ((unsigned int) (i) & 7))))
+#endif
+
+#ifndef ELEM_AT
+# define ELEM_AT(a, i, v) ((unsigned int) (i) < ARRAY_SIZE(a) ? (a)[(i)] : (v))
+#endif
+
+#define SET_ERRNO(e) \
+do { \
+ parser->http_errno = (e); \
+} while(0)
+
+#define CURRENT_STATE() p_state
+#define UPDATE_STATE(V) p_state = (enum state) (V);
+#define RETURN(V) \
+do { \
+ parser->state = CURRENT_STATE(); \
+ return (V); \
+} while (0);
+#define REEXECUTE() \
+ goto reexecute; \
+
+
+#ifdef __GNUC__
+# define LIKELY(X) __builtin_expect(!!(X), 1)
+# define UNLIKELY(X) __builtin_expect(!!(X), 0)
+#else
+# define LIKELY(X) (X)
+# define UNLIKELY(X) (X)
+#endif
+
+
+/* Run the notify callback FOR, returning ER if it fails */
+#define CALLBACK_NOTIFY_(FOR, ER) \
+do { \
+ assert(HTTP_PARSER_ERRNO(parser) == HPE_OK); \
+ \
+ if (LIKELY(settings->on_##FOR)) { \
+ parser->state = CURRENT_STATE(); \
+ if (UNLIKELY(0 != settings->on_##FOR(parser))) { \
+ SET_ERRNO(HPE_CB_##FOR); \
+ } \
+ UPDATE_STATE(parser->state); \
+ \
+ /* We either errored above or got paused; get out */ \
+ if (UNLIKELY(HTTP_PARSER_ERRNO(parser) != HPE_OK)) { \
+ return (ER); \
+ } \
+ } \
+} while (0)
+
+/* Run the notify callback FOR and consume the current byte */
+#define CALLBACK_NOTIFY(FOR) CALLBACK_NOTIFY_(FOR, p - data + 1)
+
+/* Run the notify callback FOR and don't consume the current byte */
+#define CALLBACK_NOTIFY_NOADVANCE(FOR) CALLBACK_NOTIFY_(FOR, p - data)
+
+/* Run data callback FOR with LEN bytes, returning ER if it fails */
+#define CALLBACK_DATA_(FOR, LEN, ER) \
+do { \
+ assert(HTTP_PARSER_ERRNO(parser) == HPE_OK); \
+ \
+ if (FOR##_mark) { \
+ if (LIKELY(settings->on_##FOR)) { \
+ parser->state = CURRENT_STATE(); \
+ if (UNLIKELY(0 != \
+ settings->on_##FOR(parser, FOR##_mark, (LEN)))) { \
+ SET_ERRNO(HPE_CB_##FOR); \
+ } \
+ UPDATE_STATE(parser->state); \
+ \
+ /* We either errored above or got paused; get out */ \
+ if (UNLIKELY(HTTP_PARSER_ERRNO(parser) != HPE_OK)) { \
+ return (ER); \
+ } \
+ } \
+ FOR##_mark = NULL; \
+ } \
+} while (0)
+
+/* Run the data callback FOR and consume the current byte */
+#define CALLBACK_DATA(FOR) \
+ CALLBACK_DATA_(FOR, p - FOR##_mark, p - data + 1)
+
+/* Run the data callback FOR and don't consume the current byte */
+#define CALLBACK_DATA_NOADVANCE(FOR) \
+ CALLBACK_DATA_(FOR, p - FOR##_mark, p - data)
+
+/* Set the mark FOR; non-destructive if mark is already set */
+#define MARK(FOR) \
+do { \
+ if (!FOR##_mark) { \
+ FOR##_mark = p; \
+ } \
+} while (0)
+
+/* Don't allow the total size of the HTTP headers (including the status
+ * line) to exceed HTTP_MAX_HEADER_SIZE. This check is here to protect
+ * embedders against denial-of-service attacks where the attacker feeds
+ * us a never-ending header that the embedder keeps buffering.
+ *
+ * This check is arguably the responsibility of embedders but we're doing
+ * it on the embedder's behalf because most won't bother and this way we
+ * make the web a little safer. HTTP_MAX_HEADER_SIZE is still far bigger
+ * than any reasonable request or response so this should never affect
+ * day-to-day operation.
+ */
+#define COUNT_HEADER_SIZE(V) \
+do { \
+ parser->nread += (V); \
+ if (UNLIKELY(parser->nread > (HTTP_MAX_HEADER_SIZE))) { \
+ SET_ERRNO(HPE_HEADER_OVERFLOW); \
+ goto error; \
+ } \
+} while (0)
+
+
+#define PROXY_CONNECTION "proxy-connection"
+#define CONNECTION "connection"
+#define CONTENT_LENGTH "content-length"
+#define TRANSFER_ENCODING "transfer-encoding"
+#define UPGRADE "upgrade"
+#define CHUNKED "chunked"
+#define KEEP_ALIVE "keep-alive"
+#define CLOSE "close"
+
+
+static const char *method_strings[] =
+ {
+#define XX(num, name, string) #string,
+ HTTP_METHOD_MAP(XX)
+#undef XX
+ };
+
+
+/* Tokens as defined by rfc 2616. Also lowercases them.
+ * token = 1*<any CHAR except CTLs or separators>
+ * separators = "(" | ")" | "<" | ">" | "@"
+ * | "," | ";" | ":" | "\" | <">
+ * | "/" | "[" | "]" | "?" | "="
+ * | "{" | "}" | SP | HT
+ */
+static const char tokens[256] = {
+/* 0 nul 1 soh 2 stx 3 etx 4 eot 5 enq 6 ack 7 bel */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+/* 8 bs 9 ht 10 nl 11 vt 12 np 13 cr 14 so 15 si */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+/* 16 dle 17 dc1 18 dc2 19 dc3 20 dc4 21 nak 22 syn 23 etb */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+/* 24 can 25 em 26 sub 27 esc 28 fs 29 gs 30 rs 31 us */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+/* 32 sp 33 ! 34 " 35 # 36 $ 37 % 38 & 39 ' */
+ 0, '!', 0, '#', '$', '%', '&', '\'',
+/* 40 ( 41 ) 42 * 43 + 44 , 45 - 46 . 47 / */
+ 0, 0, '*', '+', 0, '-', '.', 0,
+/* 48 0 49 1 50 2 51 3 52 4 53 5 54 6 55 7 */
+ '0', '1', '2', '3', '4', '5', '6', '7',
+/* 56 8 57 9 58 : 59 ; 60 < 61 = 62 > 63 ? */
+ '8', '9', 0, 0, 0, 0, 0, 0,
+/* 64 @ 65 A 66 B 67 C 68 D 69 E 70 F 71 G */
+ 0, 'a', 'b', 'c', 'd', 'e', 'f', 'g',
+/* 72 H 73 I 74 J 75 K 76 L 77 M 78 N 79 O */
+ 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
+/* 80 P 81 Q 82 R 83 S 84 T 85 U 86 V 87 W */
+ 'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
+/* 88 X 89 Y 90 Z 91 [ 92 \ 93 ] 94 ^ 95 _ */
+ 'x', 'y', 'z', 0, 0, 0, '^', '_',
+/* 96 ` 97 a 98 b 99 c 100 d 101 e 102 f 103 g */
+ '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
+/* 104 h 105 i 106 j 107 k 108 l 109 m 110 n 111 o */
+ 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
+/* 112 p 113 q 114 r 115 s 116 t 117 u 118 v 119 w */
+ 'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
+/* 120 x 121 y 122 z 123 { 124 | 125 } 126 ~ 127 del */
+ 'x', 'y', 'z', 0, '|', 0, '~', 0 };
+
+
+static const int8_t unhex[256] =
+ {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
+ ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
+ ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
+ , 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1
+ ,-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1
+ ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
+ ,-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1
+ ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
+ };
+
+
+#if HTTP_PARSER_STRICT
+# define T(v) 0
+#else
+# define T(v) v
+#endif
+
+
+static const uint8_t normal_url_char[32] = {
+/* 0 nul 1 soh 2 stx 3 etx 4 eot 5 enq 6 ack 7 bel */
+ 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0,
+/* 8 bs 9 ht 10 nl 11 vt 12 np 13 cr 14 so 15 si */
+ 0 | T(2) | 0 | 0 | T(16) | 0 | 0 | 0,
+/* 16 dle 17 dc1 18 dc2 19 dc3 20 dc4 21 nak 22 syn 23 etb */
+ 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0,
+/* 24 can 25 em 26 sub 27 esc 28 fs 29 gs 30 rs 31 us */
+ 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0,
+/* 32 sp 33 ! 34 " 35 # 36 $ 37 % 38 & 39 ' */
+ 0 | 2 | 4 | 0 | 16 | 32 | 64 | 128,
+/* 40 ( 41 ) 42 * 43 + 44 , 45 - 46 . 47 / */
+ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
+/* 48 0 49 1 50 2 51 3 52 4 53 5 54 6 55 7 */
+ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
+/* 56 8 57 9 58 : 59 ; 60 < 61 = 62 > 63 ? */
+ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 0,
+/* 64 @ 65 A 66 B 67 C 68 D 69 E 70 F 71 G */
+ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
+/* 72 H 73 I 74 J 75 K 76 L 77 M 78 N 79 O */
+ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
+/* 80 P 81 Q 82 R 83 S 84 T 85 U 86 V 87 W */
+ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
+/* 88 X 89 Y 90 Z 91 [ 92 \ 93 ] 94 ^ 95 _ */
+ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
+/* 96 ` 97 a 98 b 99 c 100 d 101 e 102 f 103 g */
+ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
+/* 104 h 105 i 106 j 107 k 108 l 109 m 110 n 111 o */
+ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
+/* 112 p 113 q 114 r 115 s 116 t 117 u 118 v 119 w */
+ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128,
+/* 120 x 121 y 122 z 123 { 124 | 125 } 126 ~ 127 del */
+ 1 | 2 | 4 | 8 | 16 | 32 | 64 | 0, };
+
+#undef T
+
+enum state
+ { s_dead = 1 /* important that this is > 0 */
+
+ , s_start_req_or_res
+ , s_res_or_resp_H
+ , s_start_res
+ , s_res_H
+ , s_res_HT
+ , s_res_HTT
+ , s_res_HTTP
+ , s_res_first_http_major
+ , s_res_http_major
+ , s_res_first_http_minor
+ , s_res_http_minor
+ , s_res_first_status_code
+ , s_res_status_code
+ , s_res_status_start
+ , s_res_status
+ , s_res_line_almost_done
+
+ , s_start_req
+
+ , s_req_method
+ , s_req_spaces_before_url
+ , s_req_schema
+ , s_req_schema_slash
+ , s_req_schema_slash_slash
+ , s_req_server_start
+ , s_req_server
+ , s_req_server_with_at
+ , s_req_path
+ , s_req_query_string_start
+ , s_req_query_string
+ , s_req_fragment_start
+ , s_req_fragment
+ , s_req_http_start
+ , s_req_http_H
+ , s_req_http_HT
+ , s_req_http_HTT
+ , s_req_http_HTTP
+ , s_req_first_http_major
+ , s_req_http_major
+ , s_req_first_http_minor
+ , s_req_http_minor
+ , s_req_line_almost_done
+
+ , s_header_field_start
+ , s_header_field
+ , s_header_value_discard_ws
+ , s_header_value_discard_ws_almost_done
+ , s_header_value_discard_lws
+ , s_header_value_start
+ , s_header_value
+ , s_header_value_lws
+
+ , s_header_almost_done
+
+ , s_chunk_size_start
+ , s_chunk_size
+ , s_chunk_parameters
+ , s_chunk_size_almost_done
+
+ , s_headers_almost_done
+ , s_headers_done
+
+ /* Important: 's_headers_done' must be the last 'header' state. All
+ * states beyond this must be 'body' states. It is used for overflow
+ * checking. See the PARSING_HEADER() macro.
+ */
+
+ , s_chunk_data
+ , s_chunk_data_almost_done
+ , s_chunk_data_done
+
+ , s_body_identity
+ , s_body_identity_eof
+
+ , s_message_done
+ };
+
+
+#define PARSING_HEADER(state) (state <= s_headers_done)
+
+
+enum header_states
+ { h_general = 0
+ , h_C
+ , h_CO
+ , h_CON
+
+ , h_matching_connection
+ , h_matching_proxy_connection
+ , h_matching_content_length
+ , h_matching_transfer_encoding
+ , h_matching_upgrade
+
+ , h_connection
+ , h_content_length
+ , h_transfer_encoding
+ , h_upgrade
+
+ , h_matching_transfer_encoding_chunked
+ , h_matching_connection_token_start
+ , h_matching_connection_keep_alive
+ , h_matching_connection_close
+ , h_matching_connection_upgrade
+ , h_matching_connection_token
+
+ , h_transfer_encoding_chunked
+ , h_connection_keep_alive
+ , h_connection_close
+ , h_connection_upgrade
+ };
+
+enum http_host_state
+ {
+ s_http_host_dead = 1
+ , s_http_userinfo_start
+ , s_http_userinfo
+ , s_http_host_start
+ , s_http_host_v6_start
+ , s_http_host
+ , s_http_host_v6
+ , s_http_host_v6_end
+ , s_http_host_v6_zone_start
+ , s_http_host_v6_zone
+ , s_http_host_port_start
+ , s_http_host_port
+};
+
+/* Macros for character classes; depends on strict-mode */
+#define CR '\r'
+#define LF '\n'
+#define LOWER(c) (unsigned char)(c | 0x20)
+#define IS_ALPHA(c) (LOWER(c) >= 'a' && LOWER(c) <= 'z')
+#define IS_NUM(c) ((c) >= '0' && (c) <= '9')
+#define IS_ALPHANUM(c) (IS_ALPHA(c) || IS_NUM(c))
+#define IS_HEX(c) (IS_NUM(c) || (LOWER(c) >= 'a' && LOWER(c) <= 'f'))
+#define IS_MARK(c) ((c) == '-' || (c) == '_' || (c) == '.' || \
+ (c) == '!' || (c) == '~' || (c) == '*' || (c) == '\'' || (c) == '(' || \
+ (c) == ')')
+#define IS_USERINFO_CHAR(c) (IS_ALPHANUM(c) || IS_MARK(c) || (c) == '%' || \
+ (c) == ';' || (c) == ':' || (c) == '&' || (c) == '=' || (c) == '+' || \
+ (c) == '$' || (c) == ',')
+
+#define STRICT_TOKEN(c) (tokens[(unsigned char)c])
+
+#if HTTP_PARSER_STRICT
+#define TOKEN(c) (tokens[(unsigned char)c])
+#define IS_URL_CHAR(c) (BIT_AT(normal_url_char, (unsigned char)c))
+#define IS_HOST_CHAR(c) (IS_ALPHANUM(c) || (c) == '.' || (c) == '-')
+#else
+#define TOKEN(c) ((c == ' ') ? ' ' : tokens[(unsigned char)c])
+#define IS_URL_CHAR(c) \
+ (BIT_AT(normal_url_char, (unsigned char)c) || ((c) & 0x80))
+#define IS_HOST_CHAR(c) \
+ (IS_ALPHANUM(c) || (c) == '.' || (c) == '-' || (c) == '_')
+#endif
+
+/**
+ * Verify that a char is a valid visible (printable) US-ASCII
+ * character or %x80-FF
+ **/
+#define IS_HEADER_CHAR(ch) \
+ (ch == CR || ch == LF || ch == 9 || ((unsigned char)ch > 31 && ch != 127))
+
+#define start_state (parser->type == HTTP_REQUEST ? s_start_req : s_start_res)
+
+
+#if HTTP_PARSER_STRICT
+# define STRICT_CHECK(cond) \
+do { \
+ if (cond) { \
+ SET_ERRNO(HPE_STRICT); \
+ goto error; \
+ } \
+} while (0)
+# define NEW_MESSAGE() (http_should_keep_alive(parser) ? start_state : s_dead)
+#else
+# define STRICT_CHECK(cond)
+# define NEW_MESSAGE() start_state
+#endif
+
+
+/* Map errno values to strings for human-readable output */
+#define HTTP_STRERROR_GEN(n, s) { "HPE_" #n, s },
+static struct {
+ const char *name;
+ const char *description;
+} http_strerror_tab[] = {
+ HTTP_ERRNO_MAP(HTTP_STRERROR_GEN)
+};
+#undef HTTP_STRERROR_GEN
+
+int http_message_needs_eof(const http_parser *parser);
+
+/* Our URL parser.
+ *
+ * This is designed to be shared by http_parser_execute() for URL validation,
+ * hence it has a state transition + byte-for-byte interface. In addition, it
+ * is meant to be embedded in http_parser_parse_url(), which does the dirty
+ * work of turning state transitions URL components for its API.
+ *
+ * This function should only be invoked with non-space characters. It is
+ * assumed that the caller cares about (and can detect) the transition between
+ * URL and non-URL states by looking for these.
+ */
+static enum state
+parse_url_char(enum state s, const char ch)
+{
+ if (ch == ' ' || ch == '\r' || ch == '\n') {
+ return s_dead;
+ }
+
+#if HTTP_PARSER_STRICT
+ if (ch == '\t' || ch == '\f') {
+ return s_dead;
+ }
+#endif
+
+ switch (s) {
+ case s_req_spaces_before_url:
+ /* Proxied requests are followed by scheme of an absolute URI (alpha).
+ * All methods except CONNECT are followed by '/' or '*'.
+ */
+
+ if (ch == '/' || ch == '*') {
+ return s_req_path;
+ }
+
+ if (IS_ALPHA(ch)) {
+ return s_req_schema;
+ }
+
+ break;
+
+ case s_req_schema:
+ if (IS_ALPHA(ch)) {
+ return s;
+ }
+
+ if (ch == ':') {
+ return s_req_schema_slash;
+ }
+
+ break;
+
+ case s_req_schema_slash:
+ if (ch == '/') {
+ return s_req_schema_slash_slash;
+ }
+
+ break;
+
+ case s_req_schema_slash_slash:
+ if (ch == '/') {
+ return s_req_server_start;
+ }
+
+ break;
+
+ case s_req_server_with_at:
+ if (ch == '@') {
+ return s_dead;
+ }
+
+ /* FALLTHROUGH */
+ case s_req_server_start:
+ case s_req_server:
+ if (ch == '/') {
+ return s_req_path;
+ }
+
+ if (ch == '?') {
+ return s_req_query_string_start;
+ }
+
+ if (ch == '@') {
+ return s_req_server_with_at;
+ }
+
+ if (IS_USERINFO_CHAR(ch) || ch == '[' || ch == ']') {
+ return s_req_server;
+ }
+
+ break;
+
+ case s_req_path:
+ if (IS_URL_CHAR(ch)) {
+ return s;
+ }
+
+ switch (ch) {
+ case '?':
+ return s_req_query_string_start;
+
+ case '#':
+ return s_req_fragment_start;
+ }
+
+ break;
+
+ case s_req_query_string_start:
+ case s_req_query_string:
+ if (IS_URL_CHAR(ch)) {
+ return s_req_query_string;
+ }
+
+ switch (ch) {
+ case '?':
+ /* allow extra '?' in query string */
+ return s_req_query_string;
+
+ case '#':
+ return s_req_fragment_start;
+ }
+
+ break;
+
+ case s_req_fragment_start:
+ if (IS_URL_CHAR(ch)) {
+ return s_req_fragment;
+ }
+
+ switch (ch) {
+ case '?':
+ return s_req_fragment;
+
+ case '#':
+ return s;
+ }
+
+ break;
+
+ case s_req_fragment:
+ if (IS_URL_CHAR(ch)) {
+ return s;
+ }
+
+ switch (ch) {
+ case '?':
+ case '#':
+ return s;
+ }
+
+ break;
+
+ default:
+ break;
+ }
+
+ /* We should never fall out of the switch above unless there's an error */
+ return s_dead;
+}
+
+size_t http_parser_execute (http_parser *parser,
+ const http_parser_settings *settings,
+ const char *data,
+ size_t len)
+{
+ char c, ch;
+ int8_t unhex_val;
+ const char *p = data;
+ const char *header_field_mark = 0;
+ const char *header_value_mark = 0;
+ const char *url_mark = 0;
+ const char *body_mark = 0;
+ const char *status_mark = 0;
+ enum state p_state = (enum state) parser->state;
+ const unsigned int lenient = parser->lenient_http_headers;
+
+ /* We're in an error state. Don't bother doing anything. */
+ if (HTTP_PARSER_ERRNO(parser) != HPE_OK) {
+ return 0;
+ }
+
+ if (len == 0) {
+ switch (CURRENT_STATE()) {
+ case s_body_identity_eof:
+ /* Use of CALLBACK_NOTIFY() here would erroneously return 1 byte read if
+ * we got paused.
+ */
+ CALLBACK_NOTIFY_NOADVANCE(message_complete);
+ return 0;
+
+ case s_dead:
+ case s_start_req_or_res:
+ case s_start_res:
+ case s_start_req:
+ return 0;
+
+ default:
+ SET_ERRNO(HPE_INVALID_EOF_STATE);
+ return 1;
+ }
+ }
+
+
+ if (CURRENT_STATE() == s_header_field)
+ header_field_mark = data;
+ if (CURRENT_STATE() == s_header_value)
+ header_value_mark = data;
+ switch (CURRENT_STATE()) {
+ case s_req_path:
+ case s_req_schema:
+ case s_req_schema_slash:
+ case s_req_schema_slash_slash:
+ case s_req_server_start:
+ case s_req_server:
+ case s_req_server_with_at:
+ case s_req_query_string_start:
+ case s_req_query_string:
+ case s_req_fragment_start:
+ case s_req_fragment:
+ url_mark = data;
+ break;
+ case s_res_status:
+ status_mark = data;
+ break;
+ default:
+ break;
+ }
+
+ for (p=data; p != data + len; p++) {
+ ch = *p;
+
+ if (PARSING_HEADER(CURRENT_STATE()))
+ COUNT_HEADER_SIZE(1);
+
+reexecute:
+ switch (CURRENT_STATE()) {
+
+ case s_dead:
+ /* this state is used after a 'Connection: close' message
+ * the parser will error out if it reads another message
+ */
+ if (LIKELY(ch == CR || ch == LF))
+ break;
+
+ SET_ERRNO(HPE_CLOSED_CONNECTION);
+ goto error;
+
+ case s_start_req_or_res:
+ {
+ if (ch == CR || ch == LF)
+ break;
+ parser->flags = 0;
+ parser->content_length = ULLONG_MAX;
+
+ if (ch == 'H') {
+ UPDATE_STATE(s_res_or_resp_H);
+
+ CALLBACK_NOTIFY(message_begin);
+ } else {
+ parser->type = HTTP_REQUEST;
+ UPDATE_STATE(s_start_req);
+ REEXECUTE();
+ }
+
+ break;
+ }
+
+ case s_res_or_resp_H:
+ if (ch == 'T') {
+ parser->type = HTTP_RESPONSE;
+ UPDATE_STATE(s_res_HT);
+ } else {
+ if (UNLIKELY(ch != 'E')) {
+ SET_ERRNO(HPE_INVALID_CONSTANT);
+ goto error;
+ }
+
+ parser->type = HTTP_REQUEST;
+ parser->method = HTTP_HEAD;
+ parser->index = 2;
+ UPDATE_STATE(s_req_method);
+ }
+ break;
+
+ case s_start_res:
+ {
+ parser->flags = 0;
+ parser->content_length = ULLONG_MAX;
+
+ switch (ch) {
+ case 'H':
+ UPDATE_STATE(s_res_H);
+ break;
+
+ case CR:
+ case LF:
+ break;
+
+ default:
+ SET_ERRNO(HPE_INVALID_CONSTANT);
+ goto error;
+ }
+
+ CALLBACK_NOTIFY(message_begin);
+ break;
+ }
+
+ case s_res_H:
+ STRICT_CHECK(ch != 'T');
+ UPDATE_STATE(s_res_HT);
+ break;
+
+ case s_res_HT:
+ STRICT_CHECK(ch != 'T');
+ UPDATE_STATE(s_res_HTT);
+ break;
+
+ case s_res_HTT:
+ STRICT_CHECK(ch != 'P');
+ UPDATE_STATE(s_res_HTTP);
+ break;
+
+ case s_res_HTTP:
+ STRICT_CHECK(ch != '/');
+ UPDATE_STATE(s_res_first_http_major);
+ break;
+
+ case s_res_first_http_major:
+ if (UNLIKELY(ch < '0' || ch > '9')) {
+ SET_ERRNO(HPE_INVALID_VERSION);
+ goto error;
+ }
+
+ parser->http_major = ch - '0';
+ UPDATE_STATE(s_res_http_major);
+ break;
+
+ /* major HTTP version or dot */
+ case s_res_http_major:
+ {
+ if (ch == '.') {
+ UPDATE_STATE(s_res_first_http_minor);
+ break;
+ }
+
+ if (!IS_NUM(ch)) {
+ SET_ERRNO(HPE_INVALID_VERSION);
+ goto error;
+ }
+
+ parser->http_major *= 10;
+ parser->http_major += ch - '0';
+
+ if (UNLIKELY(parser->http_major > 999)) {
+ SET_ERRNO(HPE_INVALID_VERSION);
+ goto error;
+ }
+
+ break;
+ }
+
+ /* first digit of minor HTTP version */
+ case s_res_first_http_minor:
+ if (UNLIKELY(!IS_NUM(ch))) {
+ SET_ERRNO(HPE_INVALID_VERSION);
+ goto error;
+ }
+
+ parser->http_minor = ch - '0';
+ UPDATE_STATE(s_res_http_minor);
+ break;
+
+ /* minor HTTP version or end of request line */
+ case s_res_http_minor:
+ {
+ if (ch == ' ') {
+ UPDATE_STATE(s_res_first_status_code);
+ break;
+ }
+
+ if (UNLIKELY(!IS_NUM(ch))) {
+ SET_ERRNO(HPE_INVALID_VERSION);
+ goto error;
+ }
+
+ parser->http_minor *= 10;
+ parser->http_minor += ch - '0';
+
+ if (UNLIKELY(parser->http_minor > 999)) {
+ SET_ERRNO(HPE_INVALID_VERSION);
+ goto error;
+ }
+
+ break;
+ }
+
+ case s_res_first_status_code:
+ {
+ if (!IS_NUM(ch)) {
+ if (ch == ' ') {
+ break;
+ }
+
+ SET_ERRNO(HPE_INVALID_STATUS);
+ goto error;
+ }
+ parser->status_code = ch - '0';
+ UPDATE_STATE(s_res_status_code);
+ break;
+ }
+
+ case s_res_status_code:
+ {
+ if (!IS_NUM(ch)) {
+ switch (ch) {
+ case ' ':
+ UPDATE_STATE(s_res_status_start);
+ break;
+ case CR:
+ UPDATE_STATE(s_res_line_almost_done);
+ break;
+ case LF:
+ UPDATE_STATE(s_header_field_start);
+ break;
+ default:
+ SET_ERRNO(HPE_INVALID_STATUS);
+ goto error;
+ }
+ break;
+ }
+
+ parser->status_code *= 10;
+ parser->status_code += ch - '0';
+
+ if (UNLIKELY(parser->status_code > 999)) {
+ SET_ERRNO(HPE_INVALID_STATUS);
+ goto error;
+ }
+
+ break;
+ }
+
+ case s_res_status_start:
+ {
+ if (ch == CR) {
+ UPDATE_STATE(s_res_line_almost_done);
+ break;
+ }
+
+ if (ch == LF) {
+ UPDATE_STATE(s_header_field_start);
+ break;
+ }
+
+ MARK(status);
+ UPDATE_STATE(s_res_status);
+ parser->index = 0;
+ break;
+ }
+
+ case s_res_status:
+ if (ch == CR) {
+ UPDATE_STATE(s_res_line_almost_done);
+ CALLBACK_DATA(status);
+ break;
+ }
+
+ if (ch == LF) {
+ UPDATE_STATE(s_header_field_start);
+ CALLBACK_DATA(status);
+ break;
+ }
+
+ break;
+
+ case s_res_line_almost_done:
+ STRICT_CHECK(ch != LF);
+ UPDATE_STATE(s_header_field_start);
+ break;
+
+ case s_start_req:
+ {
+ if (ch == CR || ch == LF)
+ break;
+ parser->flags = 0;
+ parser->content_length = ULLONG_MAX;
+
+ if (UNLIKELY(!IS_ALPHA(ch))) {
+ SET_ERRNO(HPE_INVALID_METHOD);
+ goto error;
+ }
+
+ parser->method = (enum http_method) 0;
+ parser->index = 1;
+ switch (ch) {
+ case 'A': parser->method = HTTP_ACL; break;
+ case 'B': parser->method = HTTP_BIND; break;
+ case 'C': parser->method = HTTP_CONNECT; /* or COPY, CHECKOUT */ break;
+ case 'D': parser->method = HTTP_DELETE; break;
+ case 'G': parser->method = HTTP_GET; break;
+ case 'H': parser->method = HTTP_HEAD; break;
+ case 'L': parser->method = HTTP_LOCK; /* or LINK */ break;
+ case 'M': parser->method = HTTP_MKCOL; /* or MOVE, MKACTIVITY, MERGE, M-SEARCH, MKCALENDAR */ break;
+ case 'N': parser->method = HTTP_NOTIFY; break;
+ case 'O': parser->method = HTTP_OPTIONS; break;
+ case 'P': parser->method = HTTP_POST;
+ /* or PROPFIND|PROPPATCH|PUT|PATCH|PURGE */
+ break;
+ case 'R': parser->method = HTTP_REPORT; /* or REBIND */ break;
+ case 'S': parser->method = HTTP_SUBSCRIBE; /* or SEARCH */ break;
+ case 'T': parser->method = HTTP_TRACE; break;
+ case 'U': parser->method = HTTP_UNLOCK; /* or UNSUBSCRIBE, UNBIND, UNLINK */ break;
+ default:
+ SET_ERRNO(HPE_INVALID_METHOD);
+ goto error;
+ }
+ UPDATE_STATE(s_req_method);
+
+ CALLBACK_NOTIFY(message_begin);
+
+ break;
+ }
+
+ case s_req_method:
+ {
+ const char *matcher;
+ if (UNLIKELY(ch == '\0')) {
+ SET_ERRNO(HPE_INVALID_METHOD);
+ goto error;
+ }
+
+ matcher = method_strings[parser->method];
+ if (ch == ' ' && matcher[parser->index] == '\0') {
+ UPDATE_STATE(s_req_spaces_before_url);
+ } else if (ch == matcher[parser->index]) {
+ ; /* nada */
+ } else if (IS_ALPHA(ch)) {
+
+ switch (parser->method << 16 | parser->index << 8 | ch) {
+#define XX(meth, pos, ch, new_meth) \
+ case (HTTP_##meth << 16 | pos << 8 | ch): \
+ parser->method = HTTP_##new_meth; break;
+
+ XX(POST, 1, 'U', PUT)
+ XX(POST, 1, 'A', PATCH)
+ XX(CONNECT, 1, 'H', CHECKOUT)
+ XX(CONNECT, 2, 'P', COPY)
+ XX(MKCOL, 1, 'O', MOVE)
+ XX(MKCOL, 1, 'E', MERGE)
+ XX(MKCOL, 2, 'A', MKACTIVITY)
+ XX(MKCOL, 3, 'A', MKCALENDAR)
+ XX(SUBSCRIBE, 1, 'E', SEARCH)
+ XX(REPORT, 2, 'B', REBIND)
+ XX(POST, 1, 'R', PROPFIND)
+ XX(PROPFIND, 4, 'P', PROPPATCH)
+ XX(PUT, 2, 'R', PURGE)
+ XX(LOCK, 1, 'I', LINK)
+ XX(UNLOCK, 2, 'S', UNSUBSCRIBE)
+ XX(UNLOCK, 2, 'B', UNBIND)
+ XX(UNLOCK, 3, 'I', UNLINK)
+#undef XX
+
+ default:
+ SET_ERRNO(HPE_INVALID_METHOD);
+ goto error;
+ }
+ } else if (ch == '-' &&
+ parser->index == 1 &&
+ parser->method == HTTP_MKCOL) {
+ parser->method = HTTP_MSEARCH;
+ } else {
+ SET_ERRNO(HPE_INVALID_METHOD);
+ goto error;
+ }
+
+ ++parser->index;
+ break;
+ }
+
+ case s_req_spaces_before_url:
+ {
+ if (ch == ' ') break;
+
+ MARK(url);
+ if (parser->method == HTTP_CONNECT) {
+ UPDATE_STATE(s_req_server_start);
+ }
+
+ UPDATE_STATE(parse_url_char(CURRENT_STATE(), ch));
+ if (UNLIKELY(CURRENT_STATE() == s_dead)) {
+ SET_ERRNO(HPE_INVALID_URL);
+ goto error;
+ }
+
+ break;
+ }
+
+ case s_req_schema:
+ case s_req_schema_slash:
+ case s_req_schema_slash_slash:
+ case s_req_server_start:
+ {
+ switch (ch) {
+ /* No whitespace allowed here */
+ case ' ':
+ case CR:
+ case LF:
+ SET_ERRNO(HPE_INVALID_URL);
+ goto error;
+ default:
+ UPDATE_STATE(parse_url_char(CURRENT_STATE(), ch));
+ if (UNLIKELY(CURRENT_STATE() == s_dead)) {
+ SET_ERRNO(HPE_INVALID_URL);
+ goto error;
+ }
+ }
+
+ break;
+ }
+
+ case s_req_server:
+ case s_req_server_with_at:
+ case s_req_path:
+ case s_req_query_string_start:
+ case s_req_query_string:
+ case s_req_fragment_start:
+ case s_req_fragment:
+ {
+ switch (ch) {
+ case ' ':
+ UPDATE_STATE(s_req_http_start);
+ CALLBACK_DATA(url);
+ break;
+ case CR:
+ case LF:
+ parser->http_major = 0;
+ parser->http_minor = 9;
+ UPDATE_STATE((ch == CR) ?
+ s_req_line_almost_done :
+ s_header_field_start);
+ CALLBACK_DATA(url);
+ break;
+ default:
+ UPDATE_STATE(parse_url_char(CURRENT_STATE(), ch));
+ if (UNLIKELY(CURRENT_STATE() == s_dead)) {
+ SET_ERRNO(HPE_INVALID_URL);
+ goto error;
+ }
+ }
+ break;
+ }
+
+ case s_req_http_start:
+ switch (ch) {
+ case 'H':
+ UPDATE_STATE(s_req_http_H);
+ break;
+ case ' ':
+ break;
+ default:
+ SET_ERRNO(HPE_INVALID_CONSTANT);
+ goto error;
+ }
+ break;
+
+ case s_req_http_H:
+ STRICT_CHECK(ch != 'T');
+ UPDATE_STATE(s_req_http_HT);
+ break;
+
+ case s_req_http_HT:
+ STRICT_CHECK(ch != 'T');
+ UPDATE_STATE(s_req_http_HTT);
+ break;
+
+ case s_req_http_HTT:
+ STRICT_CHECK(ch != 'P');
+ UPDATE_STATE(s_req_http_HTTP);
+ break;
+
+ case s_req_http_HTTP:
+ STRICT_CHECK(ch != '/');
+ UPDATE_STATE(s_req_first_http_major);
+ break;
+
+ /* first digit of major HTTP version */
+ case s_req_first_http_major:
+ if (UNLIKELY(ch < '1' || ch > '9')) {
+ SET_ERRNO(HPE_INVALID_VERSION);
+ goto error;
+ }
+
+ parser->http_major = ch - '0';
+ UPDATE_STATE(s_req_http_major);
+ break;
+
+ /* major HTTP version or dot */
+ case s_req_http_major:
+ {
+ if (ch == '.') {
+ UPDATE_STATE(s_req_first_http_minor);
+ break;
+ }
+
+ if (UNLIKELY(!IS_NUM(ch))) {
+ SET_ERRNO(HPE_INVALID_VERSION);
+ goto error;
+ }
+
+ parser->http_major *= 10;
+ parser->http_major += ch - '0';
+
+ if (UNLIKELY(parser->http_major > 999)) {
+ SET_ERRNO(HPE_INVALID_VERSION);
+ goto error;
+ }
+
+ break;
+ }
+
+ /* first digit of minor HTTP version */
+ case s_req_first_http_minor:
+ if (UNLIKELY(!IS_NUM(ch))) {
+ SET_ERRNO(HPE_INVALID_VERSION);
+ goto error;
+ }
+
+ parser->http_minor = ch - '0';
+ UPDATE_STATE(s_req_http_minor);
+ break;
+
+ /* minor HTTP version or end of request line */
+ case s_req_http_minor:
+ {
+ if (ch == CR) {
+ UPDATE_STATE(s_req_line_almost_done);
+ break;
+ }
+
+ if (ch == LF) {
+ UPDATE_STATE(s_header_field_start);
+ break;
+ }
+
+ /* XXX allow spaces after digit? */
+
+ if (UNLIKELY(!IS_NUM(ch))) {
+ SET_ERRNO(HPE_INVALID_VERSION);
+ goto error;
+ }
+
+ parser->http_minor *= 10;
+ parser->http_minor += ch - '0';
+
+ if (UNLIKELY(parser->http_minor > 999)) {
+ SET_ERRNO(HPE_INVALID_VERSION);
+ goto error;
+ }
+
+ break;
+ }
+
+ /* end of request line */
+ case s_req_line_almost_done:
+ {
+ if (UNLIKELY(ch != LF)) {
+ SET_ERRNO(HPE_LF_EXPECTED);
+ goto error;
+ }
+
+ UPDATE_STATE(s_header_field_start);
+ break;
+ }
+
+ case s_header_field_start:
+ {
+ if (ch == CR) {
+ UPDATE_STATE(s_headers_almost_done);
+ break;
+ }
+
+ if (ch == LF) {
+ /* they might be just sending \n instead of \r\n so this would be
+ * the second \n to denote the end of headers*/
+ UPDATE_STATE(s_headers_almost_done);
+ REEXECUTE();
+ }
+
+ c = TOKEN(ch);
+
+ if (UNLIKELY(!c)) {
+ SET_ERRNO(HPE_INVALID_HEADER_TOKEN);
+ goto error;
+ }
+
+ MARK(header_field);
+
+ parser->index = 0;
+ UPDATE_STATE(s_header_field);
+
+ switch (c) {
+ case 'c':
+ parser->header_state = h_C;
+ break;
+
+ case 'p':
+ parser->header_state = h_matching_proxy_connection;
+ break;
+
+ case 't':
+ parser->header_state = h_matching_transfer_encoding;
+ break;
+
+ case 'u':
+ parser->header_state = h_matching_upgrade;
+ break;
+
+ default:
+ parser->header_state = h_general;
+ break;
+ }
+ break;
+ }
+
+ case s_header_field:
+ {
+ const char* start = p;
+ for (; p != data + len; p++) {
+ ch = *p;
+ c = TOKEN(ch);
+
+ if (!c)
+ break;
+
+ switch (parser->header_state) {
+ case h_general:
+ break;
+
+ case h_C:
+ parser->index++;
+ parser->header_state = (c == 'o' ? h_CO : h_general);
+ break;
+
+ case h_CO:
+ parser->index++;
+ parser->header_state = (c == 'n' ? h_CON : h_general);
+ break;
+
+ case h_CON:
+ parser->index++;
+ switch (c) {
+ case 'n':
+ parser->header_state = h_matching_connection;
+ break;
+ case 't':
+ parser->header_state = h_matching_content_length;
+ break;
+ default:
+ parser->header_state = h_general;
+ break;
+ }
+ break;
+
+ /* connection */
+
+ case h_matching_connection:
+ parser->index++;
+ if (parser->index > sizeof(CONNECTION)-1
+ || c != CONNECTION[parser->index]) {
+ parser->header_state = h_general;
+ } else if (parser->index == sizeof(CONNECTION)-2) {
+ parser->header_state = h_connection;
+ }
+ break;
+
+ /* proxy-connection */
+
+ case h_matching_proxy_connection:
+ parser->index++;
+ if (parser->index > sizeof(PROXY_CONNECTION)-1
+ || c != PROXY_CONNECTION[parser->index]) {
+ parser->header_state = h_general;
+ } else if (parser->index == sizeof(PROXY_CONNECTION)-2) {
+ parser->header_state = h_connection;
+ }
+ break;
+
+ /* content-length */
+
+ case h_matching_content_length:
+ parser->index++;
+ if (parser->index > sizeof(CONTENT_LENGTH)-1
+ || c != CONTENT_LENGTH[parser->index]) {
+ parser->header_state = h_general;
+ } else if (parser->index == sizeof(CONTENT_LENGTH)-2) {
+ parser->header_state = h_content_length;
+ }
+ break;
+
+ /* transfer-encoding */
+
+ case h_matching_transfer_encoding:
+ parser->index++;
+ if (parser->index > sizeof(TRANSFER_ENCODING)-1
+ || c != TRANSFER_ENCODING[parser->index]) {
+ parser->header_state = h_general;
+ } else if (parser->index == sizeof(TRANSFER_ENCODING)-2) {
+ parser->header_state = h_transfer_encoding;
+ }
+ break;
+
+ /* upgrade */
+
+ case h_matching_upgrade:
+ parser->index++;
+ if (parser->index > sizeof(UPGRADE)-1
+ || c != UPGRADE[parser->index]) {
+ parser->header_state = h_general;
+ } else if (parser->index == sizeof(UPGRADE)-2) {
+ parser->header_state = h_upgrade;
+ }
+ break;
+
+ case h_connection:
+ case h_content_length:
+ case h_transfer_encoding:
+ case h_upgrade:
+ if (ch != ' ') parser->header_state = h_general;
+ break;
+
+ default:
+ assert(0 && "Unknown header_state");
+ break;
+ }
+ }
+
+ COUNT_HEADER_SIZE(p - start);
+
+ if (p == data + len) {
+ --p;
+ break;
+ }
+
+ if (ch == ':') {
+ UPDATE_STATE(s_header_value_discard_ws);
+ CALLBACK_DATA(header_field);
+ break;
+ }
+
+ SET_ERRNO(HPE_INVALID_HEADER_TOKEN);
+ goto error;
+ }
+
+ case s_header_value_discard_ws:
+ if (ch == ' ' || ch == '\t') break;
+
+ if (ch == CR) {
+ UPDATE_STATE(s_header_value_discard_ws_almost_done);
+ break;
+ }
+
+ if (ch == LF) {
+ UPDATE_STATE(s_header_value_discard_lws);
+ break;
+ }
+
+ /* FALLTHROUGH */
+
+ case s_header_value_start:
+ {
+ MARK(header_value);
+
+ UPDATE_STATE(s_header_value);
+ parser->index = 0;
+
+ c = LOWER(ch);
+
+ switch (parser->header_state) {
+ case h_upgrade:
+ parser->flags |= F_UPGRADE;
+ parser->header_state = h_general;
+ break;
+
+ case h_transfer_encoding:
+ /* looking for 'Transfer-Encoding: chunked' */
+ if ('c' == c) {
+ parser->header_state = h_matching_transfer_encoding_chunked;
+ } else {
+ parser->header_state = h_general;
+ }
+ break;
+
+ case h_content_length:
+ if (UNLIKELY(!IS_NUM(ch))) {
+ SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
+ goto error;
+ }
+
+ if (parser->flags & F_CONTENTLENGTH) {
+ SET_ERRNO(HPE_UNEXPECTED_CONTENT_LENGTH);
+ goto error;
+ }
+
+ parser->flags |= F_CONTENTLENGTH;
+ parser->content_length = ch - '0';
+ break;
+
+ case h_connection:
+ /* looking for 'Connection: keep-alive' */
+ if (c == 'k') {
+ parser->header_state = h_matching_connection_keep_alive;
+ /* looking for 'Connection: close' */
+ } else if (c == 'c') {
+ parser->header_state = h_matching_connection_close;
+ } else if (c == 'u') {
+ parser->header_state = h_matching_connection_upgrade;
+ } else {
+ parser->header_state = h_matching_connection_token;
+ }
+ break;
+
+ /* Multi-value `Connection` header */
+ case h_matching_connection_token_start:
+ break;
+
+ default:
+ parser->header_state = h_general;
+ break;
+ }
+ break;
+ }
+
+ case s_header_value:
+ {
+ const char* start = p;
+ enum header_states h_state = (enum header_states) parser->header_state;
+ for (; p != data + len; p++) {
+ ch = *p;
+ if (ch == CR) {
+ UPDATE_STATE(s_header_almost_done);
+ parser->header_state = h_state;
+ CALLBACK_DATA(header_value);
+ break;
+ }
+
+ if (ch == LF) {
+ UPDATE_STATE(s_header_almost_done);
+ COUNT_HEADER_SIZE(p - start);
+ parser->header_state = h_state;
+ CALLBACK_DATA_NOADVANCE(header_value);
+ REEXECUTE();
+ }
+
+ if (!lenient && !IS_HEADER_CHAR(ch)) {
+ SET_ERRNO(HPE_INVALID_HEADER_TOKEN);
+ goto error;
+ }
+
+ c = LOWER(ch);
+
+ switch (h_state) {
+ case h_general:
+ {
+ const char* p_cr;
+ const char* p_lf;
+ size_t limit = data + len - p;
+
+ limit = MIN(limit, HTTP_MAX_HEADER_SIZE);
+
+ p_cr = (const char*) memchr(p, CR, limit);
+ p_lf = (const char*) memchr(p, LF, limit);
+ if (p_cr != NULL) {
+ if (p_lf != NULL && p_cr >= p_lf)
+ p = p_lf;
+ else
+ p = p_cr;
+ } else if (UNLIKELY(p_lf != NULL)) {
+ p = p_lf;
+ } else {
+ p = data + len;
+ }
+ --p;
+
+ break;
+ }
+
+ case h_connection:
+ case h_transfer_encoding:
+ assert(0 && "Shouldn't get here.");
+ break;
+
+ case h_content_length:
+ {
+ uint64_t t;
+
+ if (ch == ' ') break;
+
+ if (UNLIKELY(!IS_NUM(ch))) {
+ SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
+ parser->header_state = h_state;
+ goto error;
+ }
+
+ t = parser->content_length;
+ t *= 10;
+ t += ch - '0';
+
+ /* Overflow? Test against a conservative limit for simplicity. */
+ if (UNLIKELY((ULLONG_MAX - 10) / 10 < parser->content_length)) {
+ SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
+ parser->header_state = h_state;
+ goto error;
+ }
+
+ parser->content_length = t;
+ break;
+ }
+
+ /* Transfer-Encoding: chunked */
+ case h_matching_transfer_encoding_chunked:
+ parser->index++;
+ if (parser->index > sizeof(CHUNKED)-1
+ || c != CHUNKED[parser->index]) {
+ h_state = h_general;
+ } else if (parser->index == sizeof(CHUNKED)-2) {
+ h_state = h_transfer_encoding_chunked;
+ }
+ break;
+
+ case h_matching_connection_token_start:
+ /* looking for 'Connection: keep-alive' */
+ if (c == 'k') {
+ h_state = h_matching_connection_keep_alive;
+ /* looking for 'Connection: close' */
+ } else if (c == 'c') {
+ h_state = h_matching_connection_close;
+ } else if (c == 'u') {
+ h_state = h_matching_connection_upgrade;
+ } else if (STRICT_TOKEN(c)) {
+ h_state = h_matching_connection_token;
+ } else if (c == ' ' || c == '\t') {
+ /* Skip lws */
+ } else {
+ h_state = h_general;
+ }
+ break;
+
+ /* looking for 'Connection: keep-alive' */
+ case h_matching_connection_keep_alive:
+ parser->index++;
+ if (parser->index > sizeof(KEEP_ALIVE)-1
+ || c != KEEP_ALIVE[parser->index]) {
+ h_state = h_matching_connection_token;
+ } else if (parser->index == sizeof(KEEP_ALIVE)-2) {
+ h_state = h_connection_keep_alive;
+ }
+ break;
+
+ /* looking for 'Connection: close' */
+ case h_matching_connection_close:
+ parser->index++;
+ if (parser->index > sizeof(CLOSE)-1 || c != CLOSE[parser->index]) {
+ h_state = h_matching_connection_token;
+ } else if (parser->index == sizeof(CLOSE)-2) {
+ h_state = h_connection_close;
+ }
+ break;
+
+ /* looking for 'Connection: upgrade' */
+ case h_matching_connection_upgrade:
+ parser->index++;
+ if (parser->index > sizeof(UPGRADE) - 1 ||
+ c != UPGRADE[parser->index]) {
+ h_state = h_matching_connection_token;
+ } else if (parser->index == sizeof(UPGRADE)-2) {
+ h_state = h_connection_upgrade;
+ }
+ break;
+
+ case h_matching_connection_token:
+ if (ch == ',') {
+ h_state = h_matching_connection_token_start;
+ parser->index = 0;
+ }
+ break;
+
+ case h_transfer_encoding_chunked:
+ if (ch != ' ') h_state = h_general;
+ break;
+
+ case h_connection_keep_alive:
+ case h_connection_close:
+ case h_connection_upgrade:
+ if (ch == ',') {
+ if (h_state == h_connection_keep_alive) {
+ parser->flags |= F_CONNECTION_KEEP_ALIVE;
+ } else if (h_state == h_connection_close) {
+ parser->flags |= F_CONNECTION_CLOSE;
+ } else if (h_state == h_connection_upgrade) {
+ parser->flags |= F_CONNECTION_UPGRADE;
+ }
+ h_state = h_matching_connection_token_start;
+ parser->index = 0;
+ } else if (ch != ' ') {
+ h_state = h_matching_connection_token;
+ }
+ break;
+
+ default:
+ UPDATE_STATE(s_header_value);
+ h_state = h_general;
+ break;
+ }
+ }
+ parser->header_state = h_state;
+
+ COUNT_HEADER_SIZE(p - start);
+
+ if (p == data + len)
+ --p;
+ break;
+ }
+
+ case s_header_almost_done:
+ {
+ if (UNLIKELY(ch != LF)) {
+ SET_ERRNO(HPE_LF_EXPECTED);
+ goto error;
+ }
+
+ UPDATE_STATE(s_header_value_lws);
+ break;
+ }
+
+ case s_header_value_lws:
+ {
+ if (ch == ' ' || ch == '\t') {
+ UPDATE_STATE(s_header_value_start);
+ REEXECUTE();
+ }
+
+ /* finished the header */
+ switch (parser->header_state) {
+ case h_connection_keep_alive:
+ parser->flags |= F_CONNECTION_KEEP_ALIVE;
+ break;
+ case h_connection_close:
+ parser->flags |= F_CONNECTION_CLOSE;
+ break;
+ case h_transfer_encoding_chunked:
+ parser->flags |= F_CHUNKED;
+ break;
+ case h_connection_upgrade:
+ parser->flags |= F_CONNECTION_UPGRADE;
+ break;
+ default:
+ break;
+ }
+
+ UPDATE_STATE(s_header_field_start);
+ REEXECUTE();
+ }
+
+ case s_header_value_discard_ws_almost_done:
+ {
+ STRICT_CHECK(ch != LF);
+ UPDATE_STATE(s_header_value_discard_lws);
+ break;
+ }
+
+ case s_header_value_discard_lws:
+ {
+ if (ch == ' ' || ch == '\t') {
+ UPDATE_STATE(s_header_value_discard_ws);
+ break;
+ } else {
+ switch (parser->header_state) {
+ case h_connection_keep_alive:
+ parser->flags |= F_CONNECTION_KEEP_ALIVE;
+ break;
+ case h_connection_close:
+ parser->flags |= F_CONNECTION_CLOSE;
+ break;
+ case h_connection_upgrade:
+ parser->flags |= F_CONNECTION_UPGRADE;
+ break;
+ case h_transfer_encoding_chunked:
+ parser->flags |= F_CHUNKED;
+ break;
+ default:
+ break;
+ }
+
+ /* header value was empty */
+ MARK(header_value);
+ UPDATE_STATE(s_header_field_start);
+ CALLBACK_DATA_NOADVANCE(header_value);
+ REEXECUTE();
+ }
+ }
+
+ case s_headers_almost_done:
+ {
+ STRICT_CHECK(ch != LF);
+
+ if (parser->flags & F_TRAILING) {
+ /* End of a chunked request */
+ UPDATE_STATE(s_message_done);
+ CALLBACK_NOTIFY_NOADVANCE(chunk_complete);
+ REEXECUTE();
+ }
+
+ /* Cannot use chunked encoding and a content-length header together
+ per the HTTP specification. */
+ if ((parser->flags & F_CHUNKED) &&
+ (parser->flags & F_CONTENTLENGTH)) {
+ SET_ERRNO(HPE_UNEXPECTED_CONTENT_LENGTH);
+ goto error;
+ }
+
+ UPDATE_STATE(s_headers_done);
+
+ /* Set this here so that on_headers_complete() callbacks can see it */
+ parser->upgrade =
+ ((parser->flags & (F_UPGRADE | F_CONNECTION_UPGRADE)) ==
+ (F_UPGRADE | F_CONNECTION_UPGRADE) ||
+ parser->method == HTTP_CONNECT);
+
+ /* Here we call the headers_complete callback. This is somewhat
+ * different than other callbacks because if the user returns 1, we
+ * will interpret that as saying that this message has no body. This
+ * is needed for the annoying case of recieving a response to a HEAD
+ * request.
+ *
+ * We'd like to use CALLBACK_NOTIFY_NOADVANCE() here but we cannot, so
+ * we have to simulate it by handling a change in errno below.
+ */
+ if (settings->on_headers_complete) {
+ switch (settings->on_headers_complete(parser)) {
+ case 0:
+ break;
+
+ case 2:
+ parser->upgrade = 1;
+
+ case 1:
+ parser->flags |= F_SKIPBODY;
+ break;
+
+ default:
+ SET_ERRNO(HPE_CB_headers_complete);
+ RETURN(p - data); /* Error */
+ }
+ }
+
+ if (HTTP_PARSER_ERRNO(parser) != HPE_OK) {
+ RETURN(p - data);
+ }
+
+ REEXECUTE();
+ }
+
+ case s_headers_done:
+ {
+ int hasBody;
+ STRICT_CHECK(ch != LF);
+
+ parser->nread = 0;
+
+ hasBody = parser->flags & F_CHUNKED ||
+ (parser->content_length > 0 && parser->content_length != ULLONG_MAX);
+ if (parser->upgrade && (parser->method == HTTP_CONNECT ||
+ (parser->flags & F_SKIPBODY) || !hasBody)) {
+ /* Exit, the rest of the message is in a different protocol. */
+ UPDATE_STATE(NEW_MESSAGE());
+ CALLBACK_NOTIFY(message_complete);
+ RETURN((p - data) + 1);
+ }
+
+ if (parser->flags & F_SKIPBODY) {
+ UPDATE_STATE(NEW_MESSAGE());
+ CALLBACK_NOTIFY(message_complete);
+ } else if (parser->flags & F_CHUNKED) {
+ /* chunked encoding - ignore Content-Length header */
+ UPDATE_STATE(s_chunk_size_start);
+ } else {
+ if (parser->content_length == 0) {
+ /* Content-Length header given but zero: Content-Length: 0\r\n */
+ UPDATE_STATE(NEW_MESSAGE());
+ CALLBACK_NOTIFY(message_complete);
+ } else if (parser->content_length != ULLONG_MAX) {
+ /* Content-Length header given and non-zero */
+ UPDATE_STATE(s_body_identity);
+ } else {
+ if (!http_message_needs_eof(parser)) {
+ /* Assume content-length 0 - read the next */
+ UPDATE_STATE(NEW_MESSAGE());
+ CALLBACK_NOTIFY(message_complete);
+ } else {
+ /* Read body until EOF */
+ UPDATE_STATE(s_body_identity_eof);
+ }
+ }
+ }
+
+ break;
+ }
+
+ case s_body_identity:
+ {
+ uint64_t to_read = MIN(parser->content_length,
+ (uint64_t) ((data + len) - p));
+
+ assert(parser->content_length != 0
+ && parser->content_length != ULLONG_MAX);
+
+ /* The difference between advancing content_length and p is because
+ * the latter will automaticaly advance on the next loop iteration.
+ * Further, if content_length ends up at 0, we want to see the last
+ * byte again for our message complete callback.
+ */
+ MARK(body);
+ parser->content_length -= to_read;
+ p += to_read - 1;
+
+ if (parser->content_length == 0) {
+ UPDATE_STATE(s_message_done);
+
+ /* Mimic CALLBACK_DATA_NOADVANCE() but with one extra byte.
+ *
+ * The alternative to doing this is to wait for the next byte to
+ * trigger the data callback, just as in every other case. The
+ * problem with this is that this makes it difficult for the test
+ * harness to distinguish between complete-on-EOF and
+ * complete-on-length. It's not clear that this distinction is
+ * important for applications, but let's keep it for now.
+ */
+ CALLBACK_DATA_(body, p - body_mark + 1, p - data);
+ REEXECUTE();
+ }
+
+ break;
+ }
+
+ /* read until EOF */
+ case s_body_identity_eof:
+ MARK(body);
+ p = data + len - 1;
+
+ break;
+
+ case s_message_done:
+ UPDATE_STATE(NEW_MESSAGE());
+ CALLBACK_NOTIFY(message_complete);
+ if (parser->upgrade) {
+ /* Exit, the rest of the message is in a different protocol. */
+ RETURN((p - data) + 1);
+ }
+ break;
+
+ case s_chunk_size_start:
+ {
+ assert(parser->nread == 1);
+ assert(parser->flags & F_CHUNKED);
+
+ unhex_val = unhex[(unsigned char)ch];
+ if (UNLIKELY(unhex_val == -1)) {
+ SET_ERRNO(HPE_INVALID_CHUNK_SIZE);
+ goto error;
+ }
+
+ parser->content_length = unhex_val;
+ UPDATE_STATE(s_chunk_size);
+ break;
+ }
+
+ case s_chunk_size:
+ {
+ uint64_t t;
+
+ assert(parser->flags & F_CHUNKED);
+
+ if (ch == CR) {
+ UPDATE_STATE(s_chunk_size_almost_done);
+ break;
+ }
+
+ unhex_val = unhex[(unsigned char)ch];
+
+ if (unhex_val == -1) {
+ if (ch == ';' || ch == ' ') {
+ UPDATE_STATE(s_chunk_parameters);
+ break;
+ }
+
+ SET_ERRNO(HPE_INVALID_CHUNK_SIZE);
+ goto error;
+ }
+
+ t = parser->content_length;
+ t *= 16;
+ t += unhex_val;
+
+ /* Overflow? Test against a conservative limit for simplicity. */
+ if (UNLIKELY((ULLONG_MAX - 16) / 16 < parser->content_length)) {
+ SET_ERRNO(HPE_INVALID_CONTENT_LENGTH);
+ goto error;
+ }
+
+ parser->content_length = t;
+ break;
+ }
+
+ case s_chunk_parameters:
+ {
+ assert(parser->flags & F_CHUNKED);
+ /* just ignore this shit. TODO check for overflow */
+ if (ch == CR) {
+ UPDATE_STATE(s_chunk_size_almost_done);
+ break;
+ }
+ break;
+ }
+
+ case s_chunk_size_almost_done:
+ {
+ assert(parser->flags & F_CHUNKED);
+ STRICT_CHECK(ch != LF);
+
+ parser->nread = 0;
+
+ if (parser->content_length == 0) {
+ parser->flags |= F_TRAILING;
+ UPDATE_STATE(s_header_field_start);
+ } else {
+ UPDATE_STATE(s_chunk_data);
+ }
+ CALLBACK_NOTIFY(chunk_header);
+ break;
+ }
+
+ case s_chunk_data:
+ {
+ uint64_t to_read = MIN(parser->content_length,
+ (uint64_t) ((data + len) - p));
+
+ assert(parser->flags & F_CHUNKED);
+ assert(parser->content_length != 0
+ && parser->content_length != ULLONG_MAX);
+
+ /* See the explanation in s_body_identity for why the content
+ * length and data pointers are managed this way.
+ */
+ MARK(body);
+ parser->content_length -= to_read;
+ p += to_read - 1;
+
+ if (parser->content_length == 0) {
+ UPDATE_STATE(s_chunk_data_almost_done);
+ }
+
+ break;
+ }
+
+ case s_chunk_data_almost_done:
+ assert(parser->flags & F_CHUNKED);
+ assert(parser->content_length == 0);
+ STRICT_CHECK(ch != CR);
+ UPDATE_STATE(s_chunk_data_done);
+ CALLBACK_DATA(body);
+ break;
+
+ case s_chunk_data_done:
+ assert(parser->flags & F_CHUNKED);
+ STRICT_CHECK(ch != LF);
+ parser->nread = 0;
+ UPDATE_STATE(s_chunk_size_start);
+ CALLBACK_NOTIFY(chunk_complete);
+ break;
+
+ default:
+ assert(0 && "unhandled state");
+ SET_ERRNO(HPE_INVALID_INTERNAL_STATE);
+ goto error;
+ }
+ }
+
+ /* Run callbacks for any marks that we have leftover after we ran our of
+ * bytes. There should be at most one of these set, so it's OK to invoke
+ * them in series (unset marks will not result in callbacks).
+ *
+ * We use the NOADVANCE() variety of callbacks here because 'p' has already
+ * overflowed 'data' and this allows us to correct for the off-by-one that
+ * we'd otherwise have (since CALLBACK_DATA() is meant to be run with a 'p'
+ * value that's in-bounds).
+ */
+
+ assert(((header_field_mark ? 1 : 0) +
+ (header_value_mark ? 1 : 0) +
+ (url_mark ? 1 : 0) +
+ (body_mark ? 1 : 0) +
+ (status_mark ? 1 : 0)) <= 1);
+
+ CALLBACK_DATA_NOADVANCE(header_field);
+ CALLBACK_DATA_NOADVANCE(header_value);
+ CALLBACK_DATA_NOADVANCE(url);
+ CALLBACK_DATA_NOADVANCE(body);
+ CALLBACK_DATA_NOADVANCE(status);
+
+ RETURN(len);
+
+error:
+ if (HTTP_PARSER_ERRNO(parser) == HPE_OK) {
+ SET_ERRNO(HPE_UNKNOWN);
+ }
+
+ RETURN(p - data);
+}
+
+
+/* Does the parser need to see an EOF to find the end of the message? */
+int
+http_message_needs_eof (const http_parser *parser)
+{
+ if (parser->type == HTTP_REQUEST) {
+ return 0;
+ }
+
+ /* See RFC 2616 section 4.4 */
+ if (parser->status_code / 100 == 1 || /* 1xx e.g. Continue */
+ parser->status_code == 204 || /* No Content */
+ parser->status_code == 304 || /* Not Modified */
+ parser->flags & F_SKIPBODY) { /* response to a HEAD request */
+ return 0;
+ }
+
+ if ((parser->flags & F_CHUNKED) || parser->content_length != ULLONG_MAX) {
+ return 0;
+ }
+
+ return 1;
+}
+
+
+int
+http_should_keep_alive (const http_parser *parser)
+{
+ if (parser->http_major > 0 && parser->http_minor > 0) {
+ /* HTTP/1.1 */
+ if (parser->flags & F_CONNECTION_CLOSE) {
+ return 0;
+ }
+ } else {
+ /* HTTP/1.0 or earlier */
+ if (!(parser->flags & F_CONNECTION_KEEP_ALIVE)) {
+ return 0;
+ }
+ }
+
+ return !http_message_needs_eof(parser);
+}
+
+
+const char *
+http_method_str (enum http_method m)
+{
+ return ELEM_AT(method_strings, m, "<unknown>");
+}
+
+
+void
+http_parser_init (http_parser *parser, enum http_parser_type t)
+{
+ void *data = parser->data; /* preserve application data */
+ memset(parser, 0, sizeof(*parser));
+ parser->data = data;
+ parser->type = t;
+ parser->state = (t == HTTP_REQUEST ? s_start_req : (t == HTTP_RESPONSE ? s_start_res : s_start_req_or_res));
+ parser->http_errno = HPE_OK;
+}
+
+void
+http_parser_settings_init(http_parser_settings *settings)
+{
+ memset(settings, 0, sizeof(*settings));
+}
+
+const char *
+http_errno_name(enum http_errno err) {
+ assert(((size_t) err) < ARRAY_SIZE(http_strerror_tab));
+ return http_strerror_tab[err].name;
+}
+
+const char *
+http_errno_description(enum http_errno err) {
+ assert(((size_t) err) < ARRAY_SIZE(http_strerror_tab));
+ return http_strerror_tab[err].description;
+}
+
+static enum http_host_state
+http_parse_host_char(enum http_host_state s, const char ch) {
+ switch(s) {
+ case s_http_userinfo:
+ case s_http_userinfo_start:
+ if (ch == '@') {
+ return s_http_host_start;
+ }
+
+ if (IS_USERINFO_CHAR(ch)) {
+ return s_http_userinfo;
+ }
+ break;
+
+ case s_http_host_start:
+ if (ch == '[') {
+ return s_http_host_v6_start;
+ }
+
+ if (IS_HOST_CHAR(ch)) {
+ return s_http_host;
+ }
+
+ break;
+
+ case s_http_host:
+ if (IS_HOST_CHAR(ch)) {
+ return s_http_host;
+ }
+
+ /* FALLTHROUGH */
+ case s_http_host_v6_end:
+ if (ch == ':') {
+ return s_http_host_port_start;
+ }
+
+ break;
+
+ case s_http_host_v6:
+ if (ch == ']') {
+ return s_http_host_v6_end;
+ }
+
+ /* FALLTHROUGH */
+ case s_http_host_v6_start:
+ if (IS_HEX(ch) || ch == ':' || ch == '.') {
+ return s_http_host_v6;
+ }
+
+ if (s == s_http_host_v6 && ch == '%') {
+ return s_http_host_v6_zone_start;
+ }
+ break;
+
+ case s_http_host_v6_zone:
+ if (ch == ']') {
+ return s_http_host_v6_end;
+ }
+
+ /* FALLTHROUGH */
+ case s_http_host_v6_zone_start:
+ /* RFC 6874 Zone ID consists of 1*( unreserved / pct-encoded) */
+ if (IS_ALPHANUM(ch) || ch == '%' || ch == '.' || ch == '-' || ch == '_' ||
+ ch == '~') {
+ return s_http_host_v6_zone;
+ }
+ break;
+
+ case s_http_host_port:
+ case s_http_host_port_start:
+ if (IS_NUM(ch)) {
+ return s_http_host_port;
+ }
+
+ break;
+
+ default:
+ break;
+ }
+ return s_http_host_dead;
+}
+
+static int
+http_parse_host(const char * buf, struct http_parser_url *u, int found_at) {
+ enum http_host_state s;
+
+ const char *p;
+ size_t buflen = u->field_data[UF_HOST].off + u->field_data[UF_HOST].len;
+
+ assert(u->field_set & (1 << UF_HOST));
+
+ u->field_data[UF_HOST].len = 0;
+
+ s = found_at ? s_http_userinfo_start : s_http_host_start;
+
+ for (p = buf + u->field_data[UF_HOST].off; p < buf + buflen; p++) {
+ enum http_host_state new_s = http_parse_host_char(s, *p);
+
+ if (new_s == s_http_host_dead) {
+ return 1;
+ }
+
+ switch(new_s) {
+ case s_http_host:
+ if (s != s_http_host) {
+ u->field_data[UF_HOST].off = p - buf;
+ }
+ u->field_data[UF_HOST].len++;
+ break;
+
+ case s_http_host_v6:
+ if (s != s_http_host_v6) {
+ u->field_data[UF_HOST].off = p - buf;
+ }
+ u->field_data[UF_HOST].len++;
+ break;
+
+ case s_http_host_v6_zone_start:
+ case s_http_host_v6_zone:
+ u->field_data[UF_HOST].len++;
+ break;
+
+ case s_http_host_port:
+ if (s != s_http_host_port) {
+ u->field_data[UF_PORT].off = p - buf;
+ u->field_data[UF_PORT].len = 0;
+ u->field_set |= (1 << UF_PORT);
+ }
+ u->field_data[UF_PORT].len++;
+ break;
+
+ case s_http_userinfo:
+ if (s != s_http_userinfo) {
+ u->field_data[UF_USERINFO].off = p - buf ;
+ u->field_data[UF_USERINFO].len = 0;
+ u->field_set |= (1 << UF_USERINFO);
+ }
+ u->field_data[UF_USERINFO].len++;
+ break;
+
+ default:
+ break;
+ }
+ s = new_s;
+ }
+
+ /* Make sure we don't end somewhere unexpected */
+ switch (s) {
+ case s_http_host_start:
+ case s_http_host_v6_start:
+ case s_http_host_v6:
+ case s_http_host_v6_zone_start:
+ case s_http_host_v6_zone:
+ case s_http_host_port_start:
+ case s_http_userinfo:
+ case s_http_userinfo_start:
+ return 1;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+void
+http_parser_url_init(struct http_parser_url *u) {
+ memset(u, 0, sizeof(*u));
+}
+
+int
+http_parser_parse_url(const char *buf, size_t buflen, int is_connect,
+ struct http_parser_url *u)
+{
+ enum state s;
+ const char *p;
+ enum http_parser_url_fields uf, old_uf;
+ int found_at = 0;
+
+ u->port = u->field_set = 0;
+ s = is_connect ? s_req_server_start : s_req_spaces_before_url;
+ old_uf = UF_MAX;
+
+ for (p = buf; p < buf + buflen; p++) {
+ s = parse_url_char(s, *p);
+
+ /* Figure out the next field that we're operating on */
+ switch (s) {
+ case s_dead:
+ return 1;
+
+ /* Skip delimeters */
+ case s_req_schema_slash:
+ case s_req_schema_slash_slash:
+ case s_req_server_start:
+ case s_req_query_string_start:
+ case s_req_fragment_start:
+ continue;
+
+ case s_req_schema:
+ uf = UF_SCHEMA;
+ break;
+
+ case s_req_server_with_at:
+ found_at = 1;
+
+ /* FALLTROUGH */
+ case s_req_server:
+ uf = UF_HOST;
+ break;
+
+ case s_req_path:
+ uf = UF_PATH;
+ break;
+
+ case s_req_query_string:
+ uf = UF_QUERY;
+ break;
+
+ case s_req_fragment:
+ uf = UF_FRAGMENT;
+ break;
+
+ default:
+ assert(!"Unexpected state");
+ return 1;
+ }
+
+ /* Nothing's changed; soldier on */
+ if (uf == old_uf) {
+ u->field_data[uf].len++;
+ continue;
+ }
+
+ u->field_data[uf].off = p - buf;
+ u->field_data[uf].len = 1;
+
+ u->field_set |= (1 << uf);
+ old_uf = uf;
+ }
+
+ /* host must be present if there is a schema */
+ /* parsing http:///toto will fail */
+ if ((u->field_set & (1 << UF_SCHEMA)) &&
+ (u->field_set & (1 << UF_HOST)) == 0) {
+ return 1;
+ }
+
+ if (u->field_set & (1 << UF_HOST)) {
+ if (http_parse_host(buf, u, found_at) != 0) {
+ return 1;
+ }
+ }
+
+ /* CONNECT requests can only contain "hostname:port" */
+ if (is_connect && u->field_set != ((1 << UF_HOST)|(1 << UF_PORT))) {
+ return 1;
+ }
+
+ if (u->field_set & (1 << UF_PORT)) {
+ /* Don't bother with endp; we've already validated the string */
+ unsigned long v = strtoul(buf + u->field_data[UF_PORT].off, NULL, 10);
+
+ /* Ports have a max value of 2^16 */
+ if (v > 0xffff) {
+ return 1;
+ }
+
+ u->port = (uint16_t) v;
+ }
+
+ return 0;
+}
+
+void
+http_parser_pause(http_parser *parser, int paused) {
+ /* Users should only be pausing/unpausing a parser that is not in an error
+ * state. In non-debug builds, there's not much that we can do about this
+ * other than ignore it.
+ */
+ if (HTTP_PARSER_ERRNO(parser) == HPE_OK ||
+ HTTP_PARSER_ERRNO(parser) == HPE_PAUSED) {
+ SET_ERRNO((paused) ? HPE_PAUSED : HPE_OK);
+ } else {
+ assert(0 && "Attempting to pause parser in error state");
+ }
+}
+
+int
+http_body_is_final(const struct http_parser *parser) {
+ return parser->state == s_message_done;
+}
+
+unsigned long
+http_parser_version(void) {
+ return HTTP_PARSER_VERSION_MAJOR * 0x10000 |
+ HTTP_PARSER_VERSION_MINOR * 0x00100 |
+ HTTP_PARSER_VERSION_PATCH * 0x00001;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mbed-http/http_parser/http_parser.h Sat Oct 09 14:23:23 2021 +0000
@@ -0,0 +1,433 @@
+/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+#ifndef http_parser_h
+#define http_parser_h
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Also update SONAME in the Makefile whenever you change these. */
+#define HTTP_PARSER_VERSION_MAJOR 2
+#define HTTP_PARSER_VERSION_MINOR 7
+#define HTTP_PARSER_VERSION_PATCH 1
+
+typedef unsigned int size_t;
+
+#if defined(_WIN32) && !defined(__MINGW32__) && \
+ (!defined(_MSC_VER) || _MSC_VER<1600) && !defined(__WINE__)
+#include <BaseTsd.h>
+#include <stddef.h>
+typedef __int8 int8_t;
+typedef unsigned __int8 uint8_t;
+typedef __int16 int16_t;
+typedef unsigned __int16 uint16_t;
+typedef __int32 int32_t;
+typedef unsigned __int32 uint32_t;
+typedef __int64 int64_t;
+typedef unsigned __int64 uint64_t;
+#else
+#include <stdint.h>
+#endif
+
+/* Compile with -DHTTP_PARSER_STRICT=0 to make less checks, but run
+ * faster
+ */
+#ifndef HTTP_PARSER_STRICT
+# define HTTP_PARSER_STRICT 1
+#endif
+
+/* Maximium header size allowed. If the macro is not defined
+ * before including this header then the default is used. To
+ * change the maximum header size, define the macro in the build
+ * environment (e.g. -DHTTP_MAX_HEADER_SIZE=<value>). To remove
+ * the effective limit on the size of the header, define the macro
+ * to a very large number (e.g. -DHTTP_MAX_HEADER_SIZE=0x7fffffff)
+ */
+#ifndef HTTP_MAX_HEADER_SIZE
+# define HTTP_MAX_HEADER_SIZE (80*1024)
+#endif
+
+typedef struct http_parser http_parser;
+typedef struct http_parser_settings http_parser_settings;
+
+
+/* Callbacks should return non-zero to indicate an error. The parser will
+ * then halt execution.
+ *
+ * The one exception is on_headers_complete. In a HTTP_RESPONSE parser
+ * returning '1' from on_headers_complete will tell the parser that it
+ * should not expect a body. This is used when receiving a response to a
+ * HEAD request which may contain 'Content-Length' or 'Transfer-Encoding:
+ * chunked' headers that indicate the presence of a body.
+ *
+ * Returning `2` from on_headers_complete will tell parser that it should not
+ * expect neither a body nor any futher responses on this connection. This is
+ * useful for handling responses to a CONNECT request which may not contain
+ * `Upgrade` or `Connection: upgrade` headers.
+ *
+ * http_data_cb does not return data chunks. It will be called arbitrarily
+ * many times for each string. E.G. you might get 10 callbacks for "on_url"
+ * each providing just a few characters more data.
+ */
+typedef int (*http_data_cb) (http_parser*, const char *at, size_t length);
+typedef int (*http_cb) (http_parser*);
+
+
+/* Status Codes */
+#define HTTP_STATUS_MAP(XX) \
+ XX(100, CONTINUE, Continue) \
+ XX(101, SWITCHING_PROTOCOLS, Switching Protocols) \
+ XX(102, PROCESSING, Processing) \
+ XX(200, OK, OK) \
+ XX(201, CREATED, Created) \
+ XX(202, ACCEPTED, Accepted) \
+ XX(203, NON_AUTHORITATIVE_INFORMATION, Non-Authoritative Information) \
+ XX(204, NO_CONTENT, No Content) \
+ XX(205, RESET_CONTENT, Reset Content) \
+ XX(206, PARTIAL_CONTENT, Partial Content) \
+ XX(207, MULTI_STATUS, Multi-Status) \
+ XX(208, ALREADY_REPORTED, Already Reported) \
+ XX(226, IM_USED, IM Used) \
+ XX(300, MULTIPLE_CHOICES, Multiple Choices) \
+ XX(301, MOVED_PERMANENTLY, Moved Permanently) \
+ XX(302, FOUND, Found) \
+ XX(303, SEE_OTHER, See Other) \
+ XX(304, NOT_MODIFIED, Not Modified) \
+ XX(305, USE_PROXY, Use Proxy) \
+ XX(307, TEMPORARY_REDIRECT, Temporary Redirect) \
+ XX(308, PERMANENT_REDIRECT, Permanent Redirect) \
+ XX(400, BAD_REQUEST, Bad Request) \
+ XX(401, UNAUTHORIZED, Unauthorized) \
+ XX(402, PAYMENT_REQUIRED, Payment Required) \
+ XX(403, FORBIDDEN, Forbidden) \
+ XX(404, NOT_FOUND, Not Found) \
+ XX(405, METHOD_NOT_ALLOWED, Method Not Allowed) \
+ XX(406, NOT_ACCEPTABLE, Not Acceptable) \
+ XX(407, PROXY_AUTHENTICATION_REQUIRED, Proxy Authentication Required) \
+ XX(408, REQUEST_TIMEOUT, Request Timeout) \
+ XX(409, CONFLICT, Conflict) \
+ XX(410, GONE, Gone) \
+ XX(411, LENGTH_REQUIRED, Length Required) \
+ XX(412, PRECONDITION_FAILED, Precondition Failed) \
+ XX(413, PAYLOAD_TOO_LARGE, Payload Too Large) \
+ XX(414, URI_TOO_LONG, URI Too Long) \
+ XX(415, UNSUPPORTED_MEDIA_TYPE, Unsupported Media Type) \
+ XX(416, RANGE_NOT_SATISFIABLE, Range Not Satisfiable) \
+ XX(417, EXPECTATION_FAILED, Expectation Failed) \
+ XX(421, MISDIRECTED_REQUEST, Misdirected Request) \
+ XX(422, UNPROCESSABLE_ENTITY, Unprocessable Entity) \
+ XX(423, LOCKED, Locked) \
+ XX(424, FAILED_DEPENDENCY, Failed Dependency) \
+ XX(426, UPGRADE_REQUIRED, Upgrade Required) \
+ XX(428, PRECONDITION_REQUIRED, Precondition Required) \
+ XX(429, TOO_MANY_REQUESTS, Too Many Requests) \
+ XX(431, REQUEST_HEADER_FIELDS_TOO_LARGE, Request Header Fields Too Large) \
+ XX(451, UNAVAILABLE_FOR_LEGAL_REASONS, Unavailable For Legal Reasons) \
+ XX(500, INTERNAL_SERVER_ERROR, Internal Server Error) \
+ XX(501, NOT_IMPLEMENTED, Not Implemented) \
+ XX(502, BAD_GATEWAY, Bad Gateway) \
+ XX(503, SERVICE_UNAVAILABLE, Service Unavailable) \
+ XX(504, GATEWAY_TIMEOUT, Gateway Timeout) \
+ XX(505, HTTP_VERSION_NOT_SUPPORTED, HTTP Version Not Supported) \
+ XX(506, VARIANT_ALSO_NEGOTIATES, Variant Also Negotiates) \
+ XX(507, INSUFFICIENT_STORAGE, Insufficient Storage) \
+ XX(508, LOOP_DETECTED, Loop Detected) \
+ XX(510, NOT_EXTENDED, Not Extended) \
+ XX(511, NETWORK_AUTHENTICATION_REQUIRED, Network Authentication Required) \
+
+enum http_status
+ {
+#define XX(num, name, string) HTTP_STATUS_##name = num,
+ HTTP_STATUS_MAP(XX)
+#undef XX
+ };
+
+
+/* Request Methods */
+#define HTTP_METHOD_MAP(XX) \
+ XX(0, DELETE, DELETE) \
+ XX(1, GET, GET) \
+ XX(2, HEAD, HEAD) \
+ XX(3, POST, POST) \
+ XX(4, PUT, PUT) \
+ /* pathological */ \
+ XX(5, CONNECT, CONNECT) \
+ XX(6, OPTIONS, OPTIONS) \
+ XX(7, TRACE, TRACE) \
+ /* WebDAV */ \
+ XX(8, COPY, COPY) \
+ XX(9, LOCK, LOCK) \
+ XX(10, MKCOL, MKCOL) \
+ XX(11, MOVE, MOVE) \
+ XX(12, PROPFIND, PROPFIND) \
+ XX(13, PROPPATCH, PROPPATCH) \
+ XX(14, SEARCH, SEARCH) \
+ XX(15, UNLOCK, UNLOCK) \
+ XX(16, BIND, BIND) \
+ XX(17, REBIND, REBIND) \
+ XX(18, UNBIND, UNBIND) \
+ XX(19, ACL, ACL) \
+ /* subversion */ \
+ XX(20, REPORT, REPORT) \
+ XX(21, MKACTIVITY, MKACTIVITY) \
+ XX(22, CHECKOUT, CHECKOUT) \
+ XX(23, MERGE, MERGE) \
+ /* upnp */ \
+ XX(24, MSEARCH, M-SEARCH) \
+ XX(25, NOTIFY, NOTIFY) \
+ XX(26, SUBSCRIBE, SUBSCRIBE) \
+ XX(27, UNSUBSCRIBE, UNSUBSCRIBE) \
+ /* RFC-5789 */ \
+ XX(28, PATCH, PATCH) \
+ XX(29, PURGE, PURGE) \
+ /* CalDAV */ \
+ XX(30, MKCALENDAR, MKCALENDAR) \
+ /* RFC-2068, section 19.6.1.2 */ \
+ XX(31, LINK, LINK) \
+ XX(32, UNLINK, UNLINK) \
+
+enum http_method
+ {
+#define XX(num, name, string) HTTP_##name = num,
+ HTTP_METHOD_MAP(XX)
+#undef XX
+ };
+
+
+enum http_parser_type { HTTP_REQUEST, HTTP_RESPONSE, HTTP_BOTH };
+
+
+/* Flag values for http_parser.flags field */
+enum flags
+ { F_CHUNKED = 1 << 0
+ , F_CONNECTION_KEEP_ALIVE = 1 << 1
+ , F_CONNECTION_CLOSE = 1 << 2
+ , F_CONNECTION_UPGRADE = 1 << 3
+ , F_TRAILING = 1 << 4
+ , F_UPGRADE = 1 << 5
+ , F_SKIPBODY = 1 << 6
+ , F_CONTENTLENGTH = 1 << 7
+ };
+
+
+/* Map for errno-related constants
+ *
+ * The provided argument should be a macro that takes 2 arguments.
+ */
+#define HTTP_ERRNO_MAP(XX) \
+ /* No error */ \
+ XX(OK, "success") \
+ \
+ /* Callback-related errors */ \
+ XX(CB_message_begin, "the on_message_begin callback failed") \
+ XX(CB_url, "the on_url callback failed") \
+ XX(CB_header_field, "the on_header_field callback failed") \
+ XX(CB_header_value, "the on_header_value callback failed") \
+ XX(CB_headers_complete, "the on_headers_complete callback failed") \
+ XX(CB_body, "the on_body callback failed") \
+ XX(CB_message_complete, "the on_message_complete callback failed") \
+ XX(CB_status, "the on_status callback failed") \
+ XX(CB_chunk_header, "the on_chunk_header callback failed") \
+ XX(CB_chunk_complete, "the on_chunk_complete callback failed") \
+ \
+ /* Parsing-related errors */ \
+ XX(INVALID_EOF_STATE, "stream ended at an unexpected time") \
+ XX(HEADER_OVERFLOW, \
+ "too many header bytes seen; overflow detected") \
+ XX(CLOSED_CONNECTION, \
+ "data received after completed connection: close message") \
+ XX(INVALID_VERSION, "invalid HTTP version") \
+ XX(INVALID_STATUS, "invalid HTTP status code") \
+ XX(INVALID_METHOD, "invalid HTTP method") \
+ XX(INVALID_URL, "invalid URL") \
+ XX(INVALID_HOST, "invalid host") \
+ XX(INVALID_PORT, "invalid port") \
+ XX(INVALID_PATH, "invalid path") \
+ XX(INVALID_QUERY_STRING, "invalid query string") \
+ XX(INVALID_FRAGMENT, "invalid fragment") \
+ XX(LF_EXPECTED, "LF character expected") \
+ XX(INVALID_HEADER_TOKEN, "invalid character in header") \
+ XX(INVALID_CONTENT_LENGTH, \
+ "invalid character in content-length header") \
+ XX(UNEXPECTED_CONTENT_LENGTH, \
+ "unexpected content-length header") \
+ XX(INVALID_CHUNK_SIZE, \
+ "invalid character in chunk size header") \
+ XX(INVALID_CONSTANT, "invalid constant string") \
+ XX(INVALID_INTERNAL_STATE, "encountered unexpected internal state")\
+ XX(STRICT, "strict mode assertion failed") \
+ XX(PAUSED, "parser is paused") \
+ XX(UNKNOWN, "an unknown error occurred")
+
+
+/* Define HPE_* values for each errno value above */
+#define HTTP_ERRNO_GEN(n, s) HPE_##n,
+enum http_errno {
+ HTTP_ERRNO_MAP(HTTP_ERRNO_GEN)
+};
+#undef HTTP_ERRNO_GEN
+
+
+/* Get an http_errno value from an http_parser */
+#define HTTP_PARSER_ERRNO(p) ((enum http_errno) (p)->http_errno)
+
+
+struct http_parser {
+ /** PRIVATE **/
+ unsigned int type : 2; /* enum http_parser_type */
+ unsigned int flags : 8; /* F_* values from 'flags' enum; semi-public */
+ unsigned int state : 7; /* enum state from http_parser.c */
+ unsigned int header_state : 7; /* enum header_state from http_parser.c */
+ unsigned int index : 7; /* index into current matcher */
+ unsigned int lenient_http_headers : 1;
+
+ uint32_t nread; /* # bytes read in various scenarios */
+ uint64_t content_length; /* # bytes in body (0 if no Content-Length header) */
+
+ /** READ-ONLY **/
+ unsigned short http_major;
+ unsigned short http_minor;
+ unsigned int status_code : 16; /* responses only */
+ unsigned int method : 8; /* requests only */
+ unsigned int http_errno : 7;
+
+ /* 1 = Upgrade header was present and the parser has exited because of that.
+ * 0 = No upgrade header present.
+ * Should be checked when http_parser_execute() returns in addition to
+ * error checking.
+ */
+ unsigned int upgrade : 1;
+
+ /** PUBLIC **/
+ void *data; /* A pointer to get hook to the "connection" or "socket" object */
+};
+
+
+struct http_parser_settings {
+ http_cb on_message_begin;
+ http_data_cb on_url;
+ http_data_cb on_status;
+ http_data_cb on_header_field;
+ http_data_cb on_header_value;
+ http_cb on_headers_complete;
+ http_data_cb on_body;
+ http_cb on_message_complete;
+ /* When on_chunk_header is called, the current chunk length is stored
+ * in parser->content_length.
+ */
+ http_cb on_chunk_header;
+ http_cb on_chunk_complete;
+};
+
+
+enum http_parser_url_fields
+ { UF_SCHEMA = 0
+ , UF_HOST = 1
+ , UF_PORT = 2
+ , UF_PATH = 3
+ , UF_QUERY = 4
+ , UF_FRAGMENT = 5
+ , UF_USERINFO = 6
+ , UF_MAX = 7
+ };
+
+
+/* Result structure for http_parser_parse_url().
+ *
+ * Callers should index into field_data[] with UF_* values iff field_set
+ * has the relevant (1 << UF_*) bit set. As a courtesy to clients (and
+ * because we probably have padding left over), we convert any port to
+ * a uint16_t.
+ */
+struct http_parser_url {
+ uint16_t field_set; /* Bitmask of (1 << UF_*) values */
+ uint16_t port; /* Converted UF_PORT string */
+
+ struct {
+ uint16_t off; /* Offset into buffer in which field starts */
+ uint16_t len; /* Length of run in buffer */
+ } field_data[UF_MAX];
+};
+
+
+/* Returns the library version. Bits 16-23 contain the major version number,
+ * bits 8-15 the minor version number and bits 0-7 the patch level.
+ * Usage example:
+ *
+ * unsigned long version = http_parser_version();
+ * unsigned major = (version >> 16) & 255;
+ * unsigned minor = (version >> 8) & 255;
+ * unsigned patch = version & 255;
+ * printf("http_parser v%u.%u.%u\n", major, minor, patch);
+ */
+unsigned long http_parser_version(void);
+
+void http_parser_init(http_parser *parser, enum http_parser_type type);
+
+
+/* Initialize http_parser_settings members to 0
+ */
+void http_parser_settings_init(http_parser_settings *settings);
+
+
+/* Executes the parser. Returns number of parsed bytes. Sets
+ * `parser->http_errno` on error. */
+size_t http_parser_execute(http_parser *parser,
+ const http_parser_settings *settings,
+ const char *data,
+ size_t len);
+
+
+/* If http_should_keep_alive() in the on_headers_complete or
+ * on_message_complete callback returns 0, then this should be
+ * the last message on the connection.
+ * If you are the server, respond with the "Connection: close" header.
+ * If you are the client, close the connection.
+ */
+int http_should_keep_alive(const http_parser *parser);
+
+/* Returns a string version of the HTTP method. */
+const char *http_method_str(enum http_method m);
+
+/* Return a string name of the given error */
+const char *http_errno_name(enum http_errno err);
+
+/* Return a string description of the given error */
+const char *http_errno_description(enum http_errno err);
+
+/* Initialize all http_parser_url members to 0 */
+void http_parser_url_init(struct http_parser_url *u);
+
+/* Parse a URL; return nonzero on failure */
+int http_parser_parse_url(const char *buf, size_t buflen,
+ int is_connect,
+ struct http_parser_url *u);
+
+/* Pause or un-pause the parser; a nonzero value pauses */
+void http_parser_pause(http_parser *parser, int paused);
+
+/* Checks if this is the final chunk of the body. */
+int http_body_is_final(const http_parser *parser);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mbed-http/mbed_lib.json Sat Oct 09 14:23:23 2021 +0000
@@ -0,0 +1,10 @@
+{
+ "name": "mbed-http",
+ "config": {
+ "http-buffer-size": {
+ "help": "Size of the HTTP receive buffer in bytes",
+ "value": 8192,
+ "macro_name": "HTTP_RECEIVE_BUFFER_SIZE"
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mbed-http/source/http_parsed_url.h Sat Oct 09 14:23:23 2021 +0000
@@ -0,0 +1,93 @@
+/*
+ * PackageLicenseDeclared: Apache-2.0
+ * Copyright (c) 2017 ARM Limited
+ *
+ * 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.
+ */
+
+#ifndef _MBED_HTTP_PARSED_URL_H_
+#define _MBED_HTTP_PARSED_URL_H_
+
+#include "http_parser.h"
+
+class ParsedUrl {
+public:
+ ParsedUrl(const char* url) {
+ struct http_parser_url parsed_url;
+ http_parser_parse_url(url, strlen(url), false, &parsed_url);
+
+ for (size_t ix = 0; ix < UF_MAX; ix++) {
+ char* value;
+ if (parsed_url.field_set & (1 << ix)) {
+ value = (char*)calloc(parsed_url.field_data[ix].len + 1, 1);
+ memcpy((void*)value, url + parsed_url.field_data[ix].off,
+ parsed_url.field_data[ix].len);
+ }
+ else {
+ value = (char*)calloc(1, 1);
+ }
+
+ switch ((http_parser_url_fields)ix) {
+ case UF_SCHEMA: _schema = value; break;
+ case UF_HOST: _host = value; break;
+ case UF_PATH: _path = value; break;
+ case UF_QUERY: _query = value; break;
+ case UF_USERINFO: _userinfo = value; break;
+ default:
+ // PORT is already parsed, FRAGMENT is not relevant for HTTP requests
+ free((void*)value);
+ break;
+ }
+ }
+
+ _port = parsed_url.port;
+ if (!_port) {
+ if (strcmp(_schema, "https") == 0) {
+ _port = 443;
+ }
+ else {
+ _port = 80;
+ }
+ }
+
+ if (strcmp(_path, "") == 0) {
+ _path = (char*)calloc(2, 1);
+ _path[0] = '/';
+ }
+ }
+
+ ~ParsedUrl() {
+ if (_schema) free((void*)_schema);
+ if (_host) free((void*)_host);
+ if (_path) free((void*)_path);
+ if (_query) free((void*)_query);
+ if (_userinfo) free((void*)_userinfo);
+ }
+
+ uint16_t port() const { return _port; }
+ char* schema() const { return _schema; }
+ char* host() const { return _host; }
+ char* path() const { return _path; }
+ char* query() const { return _query; }
+ char* userinfo() const { return _userinfo; }
+
+private:
+ uint16_t _port;
+ char* _schema;
+ char* _host;
+ char* _path;
+ char* _query;
+ char* _userinfo;
+};
+
+#endif // _MBED_HTTP_PARSED_URL_H_
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mbed-http/source/http_request.h Sat Oct 09 14:23:23 2021 +0000
@@ -0,0 +1,232 @@
+/*
+ * PackageLicenseDeclared: Apache-2.0
+ * Copyright (c) 2017 ARM Limited
+ *
+ * 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.
+ */
+
+#ifndef _HTTP_REQUEST_
+#define _HTTP_REQUEST_
+
+#include <string>
+#include <vector>
+#include <map>
+#include "http_parser.h"
+#include "http_response.h"
+#include "http_request_builder.h"
+#include "http_request_parser.h"
+#include "http_parsed_url.h"
+
+/**
+ * @todo:
+ * - Userinfo parameter is not handled
+ */
+
+
+/**
+ * \brief HttpRequest implements the logic for interacting with HTTP servers.
+ */
+class HttpRequest {
+public:
+ /**
+ * HttpRequest Constructor
+ *
+ * @param[in] aNetwork The network interface
+ * @param[in] aMethod HTTP method to use
+ * @param[in] url URL to the resource
+ * @param[in] aBodyCallback Callback on which to retrieve chunks of the response body.
+ If not set, the complete body will be allocated on the HttpResponse object,
+ which might use lots of memory.
+ */
+ HttpRequest(NetworkInterface* aNetwork, http_method aMethod, const char* url, Callback<void(const char *at, size_t length)> aBodyCallback = 0)
+ : network(aNetwork), method(aMethod), body_callback(aBodyCallback)
+ {
+ error = 0;
+ response = NULL;
+
+ parsed_url = new ParsedUrl(url);
+ request_builder = new HttpRequestBuilder(method, parsed_url);
+
+ socket = new TCPSocket();
+ we_created_socket = true;
+ }
+
+ /**
+ * HttpRequest Constructor
+ *
+ * @param[in] aSocket An open TCPSocket
+ * @param[in] aMethod HTTP method to use
+ * @param[in] url URL to the resource
+ * @param[in] aBodyCallback Callback on which to retrieve chunks of the response body.
+ If not set, the complete body will be allocated on the HttpResponse object,
+ which might use lots of memory.
+ */
+ HttpRequest(TCPSocket* aSocket, http_method aMethod, const char* url, Callback<void(const char *at, size_t length)> aBodyCallback = 0)
+ : socket(aSocket), method(aMethod), body_callback(aBodyCallback)
+ {
+ error = 0;
+ response = NULL;
+ network = NULL;
+
+ parsed_url = new ParsedUrl(url);
+ request_builder = new HttpRequestBuilder(method, parsed_url);
+
+ we_created_socket = false;
+ }
+
+ /**
+ * HttpRequest Constructor
+ */
+ ~HttpRequest() {
+ // should response be owned by us? Or should user free it?
+ // maybe implement copy constructor on response...
+ if (response) {
+ delete response;
+ }
+
+ if (parsed_url) {
+ delete parsed_url;
+ }
+
+ if (request_builder) {
+ delete request_builder;
+ }
+
+ if (socket && we_created_socket) {
+ delete socket;
+ }
+ }
+
+ /**
+ * Execute the request and receive the response.
+ */
+ HttpResponse* send(const void* body = NULL, nsapi_size_t body_size = 0) {
+ if (response != NULL) {
+ // already executed this response
+ error = -2100; // @todo, make a lookup table with errors
+ return NULL;
+ }
+
+ error = 0;
+
+ if (we_created_socket) {
+ nsapi_error_t open_result = socket->open(network);
+ if (open_result != 0) {
+ error = open_result;
+ return NULL;
+ }
+
+ nsapi_error_t connection_result = socket->connect(parsed_url->host(), parsed_url->port());
+ if (connection_result != 0) {
+ error = connection_result;
+ return NULL;
+ }
+ }
+
+ size_t request_size = 0;
+ char* request = request_builder->build(body, body_size, request_size);
+
+ nsapi_size_or_error_t send_result = socket->send(request, request_size);
+
+ free(request);
+
+ if (send_result != request_size) {
+ error = send_result;
+ return NULL;
+ }
+
+ // Create a response object
+ response = new HttpResponse();
+ // And a response parser
+ HttpParser parser(response, HTTP_RESPONSE, body_callback);
+
+ // Set up a receive buffer (on the heap)
+ uint8_t* recv_buffer = (uint8_t*)malloc(HTTP_RECEIVE_BUFFER_SIZE);
+
+ // TCPSocket::recv is called until we don't have any data anymore
+ nsapi_size_or_error_t recv_ret;
+ while ((recv_ret = socket->recv(recv_buffer, HTTP_RECEIVE_BUFFER_SIZE)) > 0) {
+
+ // Pass the chunk into the http_parser
+ size_t nparsed = parser.execute((const char*)recv_buffer, recv_ret);
+ if (nparsed != recv_ret) {
+ // printf("Parsing failed... parsed %d bytes, received %d bytes\n", nparsed, recv_ret);
+ error = -2101;
+ free(recv_buffer);
+ return NULL;
+ }
+
+ if (response->is_message_complete()) {
+ break;
+ }
+ }
+ // error?
+ if (recv_ret < 0) {
+ error = recv_ret;
+ free(recv_buffer);
+ return NULL;
+ }
+
+ // When done, call parser.finish()
+ parser.finish();
+
+ // Free the receive buffer
+ free(recv_buffer);
+
+ if (we_created_socket) {
+ // Close the socket
+ socket->close();
+ }
+
+ return response;
+ }
+
+ /**
+ * Set a header for the request.
+ *
+ * The 'Host' and 'Content-Length' headers are set automatically.
+ * Setting the same header twice will overwrite the previous entry.
+ *
+ * @param[in] key Header key
+ * @param[in] value Header value
+ */
+ void set_header(string key, string value) {
+ request_builder->set_header(key, value);
+ }
+
+ /**
+ * Get the error code.
+ *
+ * When send() fails, this error is set.
+ */
+ nsapi_error_t get_error() {
+ return error;
+ }
+
+private:
+ NetworkInterface* network;
+ TCPSocket* socket;
+ http_method method;
+ Callback<void(const char *at, size_t length)> body_callback;
+
+ ParsedUrl* parsed_url;
+
+ HttpRequestBuilder* request_builder;
+ HttpResponse* response;
+
+ bool we_created_socket;
+
+ nsapi_error_t error;
+};
+
+#endif // _HTTP_REQUEST_
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mbed-http/source/http_request_builder.h Sat Oct 09 14:23:23 2021 +0000
@@ -0,0 +1,122 @@
+/*
+ * PackageLicenseDeclared: Apache-2.0
+ * Copyright (c) 2017 ARM Limited
+ *
+ * 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.
+ */
+
+#ifndef _MBED_HTTP_REQUEST_BUILDER_H_
+#define _MBED_HTTP_REQUEST_BUILDER_H_
+
+#include <string>
+#include <map>
+#include "http_parser.h"
+#include "http_parsed_url.h"
+
+class HttpRequestBuilder {
+public:
+ HttpRequestBuilder(http_method a_method, ParsedUrl* a_parsed_url)
+ : method(a_method), parsed_url(a_parsed_url)
+ {
+ set_header("Host", string(parsed_url->host()));
+ }
+
+ /**
+ * Set a header for the request
+ * If the key already exists, it will be overwritten...
+ */
+ void set_header(string key, string value) {
+ map<string, string>::iterator it = headers.find(key);
+
+ if (it != headers.end()) {
+ it->second = value;
+ }
+ else {
+ headers.insert(headers.end(), pair<string, string>(key, value));
+ }
+ }
+
+ char* build(const void* body, size_t body_size, size_t &size) {
+ const char* method_str = http_method_str(method);
+
+ if (method == HTTP_POST || method == HTTP_PUT || method == HTTP_DELETE || body_size > 0) {
+ char buffer[10];
+ snprintf(buffer, 10, "%d", body_size);
+ set_header("Content-Length", string(buffer));
+ }
+
+ size = 0;
+
+ // first line is METHOD PATH+QUERY HTTP/1.1\r\n
+ size += strlen(method_str) + 1 + strlen(parsed_url->path()) + (strlen(parsed_url->query()) ? strlen(parsed_url->query()) + 1 : 0) + 1 + 8 + 2;
+
+ // after that we'll do the headers
+ typedef map<string, string>::iterator it_type;
+ for(it_type it = headers.begin(); it != headers.end(); it++) {
+ // line is KEY: VALUE\r\n
+ size += it->first.length() + 1 + 1 + it->second.length() + 2;
+ }
+
+ // then the body, first an extra newline
+ size += 2;
+
+ // body
+ size += body_size;
+
+ // extra newline
+ size += 2;
+
+ // Now let's print it
+ char* req = (char*)calloc(size + 1, 1);
+ char* originalReq = req;
+
+ if (strlen(parsed_url->query())) {
+ sprintf(req, "%s %s?%s HTTP/1.1\r\n", method_str, parsed_url->path(), parsed_url->query());
+ } else {
+ sprintf(req, "%s %s%s HTTP/1.1\r\n", method_str, parsed_url->path(), parsed_url->query());
+ }
+ req += strlen(method_str) + 1 + strlen(parsed_url->path()) + (strlen(parsed_url->query()) ? strlen(parsed_url->query()) + 1 : 0) + 1 + 8 + 2;
+
+ typedef map<string, string>::iterator it_type;
+ for(it_type it = headers.begin(); it != headers.end(); it++) {
+ // line is KEY: VALUE\r\n
+ sprintf(req, "%s: %s\r\n", it->first.c_str(), it->second.c_str());
+ req += it->first.length() + 1 + 1 + it->second.length() + 2;
+ }
+
+ sprintf(req, "\r\n");
+ req += 2;
+
+ if (body_size > 0) {
+ memcpy(req, body, body_size);
+ }
+ req += body_size;
+
+ sprintf(req, "\r\n");
+ req += 2;
+
+ // Uncomment to debug...
+ // printf("----- BEGIN REQUEST -----\n");
+ // printf("%s", originalReq);
+ // printf("----- END REQUEST -----\n");
+
+ return originalReq;
+ }
+
+private:
+ http_method method;
+ ParsedUrl* parsed_url;
+ map<string, string> headers;
+};
+
+#endif // _MBED_HTTP_REQUEST_BUILDER_H_
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mbed-http/source/http_request_parser.h Sat Oct 09 14:23:23 2021 +0000
@@ -0,0 +1,177 @@
+/*
+ * PackageLicenseDeclared: Apache-2.0
+ * Copyright (c) 2017 ARM Limited
+ *
+ * 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.
+ */
+
+#ifndef _HTTP_RESPONSE_PARSER_H_
+#define _HTTP_RESPONSE_PARSER_H_
+
+#include "http_parser.h"
+#include "http_response.h"
+
+class HttpParser {
+public:
+
+ HttpParser(HttpResponse* a_response, http_parser_type parser_type, Callback<void(const char *at, size_t length)> a_body_callback = 0)
+ : response(a_response), body_callback(a_body_callback)
+ {
+ settings = new http_parser_settings();
+
+ settings->on_message_begin = &HttpParser::on_message_begin_callback;
+ settings->on_url = &HttpParser::on_url_callback;
+ settings->on_status = &HttpParser::on_status_callback;
+ settings->on_header_field = &HttpParser::on_header_field_callback;
+ settings->on_header_value = &HttpParser::on_header_value_callback;
+ settings->on_headers_complete = &HttpParser::on_headers_complete_callback;
+ settings->on_chunk_header = &HttpParser::on_chunk_header_callback;
+ settings->on_chunk_complete = &HttpParser::on_chunk_complete_callback;
+ settings->on_body = &HttpParser::on_body_callback;
+ settings->on_message_complete = &HttpParser::on_message_complete_callback;
+
+ // Construct the http_parser object
+ parser = new http_parser();
+ http_parser_init(parser, parser_type);
+ parser->data = (void*)this;
+ }
+
+ ~HttpParser() {
+ if (parser) {
+ delete parser;
+ }
+ if (settings) {
+ delete settings;
+ }
+ }
+
+ size_t execute(const char* buffer, size_t buffer_size) {
+ return http_parser_execute(parser, settings, buffer, buffer_size);
+ }
+
+ void finish() {
+ http_parser_execute(parser, settings, NULL, 0);
+ }
+
+private:
+ // Member functions
+ int on_message_begin(http_parser* parser) {
+ return 0;
+ }
+
+ int on_url(http_parser* parser, const char *at, size_t length) {
+ string s(at, length);
+ response->set_url(s);
+ return 0;
+ }
+
+ int on_status(http_parser* parser, const char *at, size_t length) {
+ string s(at, length);
+ response->set_status(parser->status_code, s);
+ return 0;
+ }
+
+ int on_header_field(http_parser* parser, const char *at, size_t length) {
+ string s(at, length);
+ response->set_header_field(s);
+ return 0;
+ }
+
+ int on_header_value(http_parser* parser, const char *at, size_t length) {
+ string s(at, length);
+ response->set_header_value(s);
+ return 0;
+ }
+
+ int on_headers_complete(http_parser* parser) {
+ response->set_headers_complete();
+ response->set_method((http_method)parser->method);
+ return 0;
+ }
+
+ int on_body(http_parser* parser, const char *at, size_t length) {
+ response->increase_body_length(length);
+
+ if (body_callback) {
+ body_callback(at, length);
+ return 0;
+ }
+
+ response->set_body(at, length);
+ return 0;
+ }
+
+ int on_message_complete(http_parser* parser) {
+ response->set_message_complete();
+
+ return 0;
+ }
+
+ int on_chunk_header(http_parser* parser) {
+ response->set_chunked();
+
+ return 0;
+ }
+
+ int on_chunk_complete(http_parser* parser) {
+ return 0;
+ }
+
+ // Static http_parser callback functions
+ static int on_message_begin_callback(http_parser* parser) {
+ return ((HttpParser*)parser->data)->on_message_begin(parser);
+ }
+
+ static int on_url_callback(http_parser* parser, const char *at, size_t length) {
+ return ((HttpParser*)parser->data)->on_url(parser, at, length);
+ }
+
+ static int on_status_callback(http_parser* parser, const char *at, size_t length) {
+ return ((HttpParser*)parser->data)->on_status(parser, at, length);
+ }
+
+ static int on_header_field_callback(http_parser* parser, const char *at, size_t length) {
+ return ((HttpParser*)parser->data)->on_header_field(parser, at, length);
+ }
+
+ static int on_header_value_callback(http_parser* parser, const char *at, size_t length) {
+ return ((HttpParser*)parser->data)->on_header_value(parser, at, length);
+ }
+
+ static int on_headers_complete_callback(http_parser* parser) {
+ return ((HttpParser*)parser->data)->on_headers_complete(parser);
+ }
+
+ static int on_body_callback(http_parser* parser, const char *at, size_t length) {
+ return ((HttpParser*)parser->data)->on_body(parser, at, length);
+ }
+
+ static int on_message_complete_callback(http_parser* parser) {
+ return ((HttpParser*)parser->data)->on_message_complete(parser);
+ }
+
+ static int on_chunk_header_callback(http_parser* parser) {
+ return ((HttpParser*)parser->data)->on_chunk_header(parser);
+ }
+
+ static int on_chunk_complete_callback(http_parser* parser) {
+ return ((HttpParser*)parser->data)->on_chunk_complete(parser);
+ }
+
+ HttpResponse* response;
+ Callback<void(const char *at, size_t length)> body_callback;
+ http_parser* parser;
+ http_parser_settings* settings;
+};
+
+#endif // _HTTP_RESPONSE_PARSER_H_
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mbed-http/source/http_response.h Sat Oct 09 14:23:23 2021 +0000
@@ -0,0 +1,224 @@
+/*
+ * PackageLicenseDeclared: Apache-2.0
+ * Copyright (c) 2017 ARM Limited
+ *
+ * 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.
+ */
+
+#ifndef _MBED_HTTP_HTTP_RESPONSE
+#define _MBED_HTTP_HTTP_RESPONSE
+#include <string>
+#include <vector>
+#include "http_parser.h"
+
+using namespace std;
+
+class HttpResponse {
+public:
+ HttpResponse() {
+ status_code = 0;
+ concat_header_field = false;
+ concat_header_value = false;
+ expected_content_length = 0;
+ is_chunked = false;
+ is_message_completed = false;
+ body_length = 0;
+ body_offset = 0;
+ body = NULL;
+ }
+
+ ~HttpResponse() {
+ if (body != NULL) {
+ free(body);
+ }
+
+ for (size_t ix = 0; ix < header_fields.size(); ix++) {
+ delete header_fields[ix];
+ delete header_values[ix];
+ }
+ }
+
+ void set_status(int a_status_code, string a_status_message) {
+ status_code = a_status_code;
+ status_message = a_status_message;
+ }
+
+ int get_status_code() {
+ return status_code;
+ }
+
+ string get_status_message() {
+ return status_message;
+ }
+
+ void set_url(string a_url) {
+ url = a_url;
+ }
+
+ string get_url() {
+ return url;
+ }
+
+ void set_method(http_method a_method) {
+ method = a_method;
+ }
+
+ http_method get_method() {
+ return method;
+ }
+
+ void set_header_field(string field) {
+ concat_header_value = false;
+
+ // headers can be chunked
+ if (concat_header_field) {
+ *header_fields[header_fields.size() - 1] = (*header_fields[header_fields.size() - 1]) + field;
+ }
+ else {
+ header_fields.push_back(new string(field));
+ }
+
+ concat_header_field = true;
+ }
+
+ void set_header_value(string value) {
+ concat_header_field = false;
+
+ // headers can be chunked
+ if (concat_header_value) {
+ *header_values[header_values.size() - 1] = (*header_values[header_values.size() - 1]) + value;
+ }
+ else {
+ header_values.push_back(new string(value));
+ }
+
+ concat_header_value = true;
+ }
+
+ void set_headers_complete() {
+ for (size_t ix = 0; ix < header_fields.size(); ix++) {
+ if (strcicmp(header_fields[ix]->c_str(), "content-length") == 0) {
+ expected_content_length = (size_t)atoi(header_values[ix]->c_str());
+ break;
+ }
+ }
+ }
+
+ size_t get_headers_length() {
+ return header_fields.size();
+ }
+
+ vector<string*> get_headers_fields() {
+ return header_fields;
+ }
+
+ vector<string*> get_headers_values() {
+ return header_values;
+ }
+
+ void set_body(const char *at, size_t length) {
+ // Connection: close, could not specify Content-Length, nor chunked... So do it like this:
+ if (expected_content_length == 0 && length > 0) {
+ is_chunked = true;
+ }
+
+ // only malloc when this fn is called, so we don't alloc when body callback's are enabled
+ if (body == NULL && !is_chunked) {
+ body = (char*)malloc(expected_content_length);
+ }
+
+ if (is_chunked) {
+ if (body == NULL) {
+ body = (char*)malloc(length);
+ }
+ else {
+ body = (char*)realloc(body, body_offset + length);
+ }
+ }
+
+ memcpy(body + body_offset, at, length);
+
+ body_offset += length;
+ }
+
+ void* get_body() {
+ return (void*)body;
+ }
+
+ string get_body_as_string() {
+ string s(body, body_offset);
+ return s;
+ }
+
+ void increase_body_length(size_t length) {
+ body_length += length;
+ }
+
+ size_t get_body_length() {
+ return body_offset;
+ }
+
+ bool is_message_complete() {
+ return is_message_completed;
+ }
+
+ void set_chunked() {
+ is_chunked = true;
+ }
+
+ void set_message_complete() {
+ is_message_completed = true;
+ }
+
+private:
+ // from http://stackoverflow.com/questions/5820810/case-insensitive-string-comp-in-c
+ int strcicmp(char const *a, char const *b) {
+ for (;; a++, b++) {
+ int d = tolower(*a) - tolower(*b);
+ if (d != 0 || !*a) {
+ return d;
+ }
+ }
+ }
+
+ char tolower(char c) {
+ if(('A' <= c) && (c <= 'Z')) {
+ return 'a' + (c - 'A');
+ }
+
+ return c;
+ }
+
+ int status_code;
+ string status_message;
+ string url;
+ http_method method;
+
+ vector<string*> header_fields;
+ vector<string*> header_values;
+
+ bool concat_header_field;
+ bool concat_header_value;
+
+ size_t expected_content_length;
+
+ bool is_chunked;
+
+ bool is_message_completed;
+
+ char * body;
+ size_t body_length;
+ size_t body_offset;
+};
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mbed-http/source/https_request.h Sat Oct 09 14:23:23 2021 +0000
@@ -0,0 +1,277 @@
+/*
+ * PackageLicenseDeclared: Apache-2.0
+ * Copyright (c) 2017 ARM Limited
+ *
+ * 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.
+ */
+
+#ifndef _MBED_HTTPS_REQUEST_H_
+#define _MBED_HTTPS_REQUEST_H_
+
+#include <string>
+#include <vector>
+#include <map>
+#include "http_parser.h"
+#include "http_response.h"
+#include "http_request_builder.h"
+#include "http_request_parser.h"
+#include "http_parsed_url.h"
+#include "tls_socket.h"
+
+/**
+ * \brief HttpsRequest implements the logic for interacting with HTTPS servers.
+ */
+class HttpsRequest {
+public:
+ /**
+ * HttpsRequest Constructor
+ * Initializes the TCP socket, sets up event handlers and flags.
+ *
+ * @param[in] net_iface The network interface
+ * @param[in] ssl_ca_pem String containing the trusted CAs
+ * @param[in] method HTTP method to use
+ * @param[in] url URL to the resource
+ * @param[in] body_callback Callback on which to retrieve chunks of the response body.
+ If not set, the complete body will be allocated on the HttpResponse object,
+ which might use lots of memory.
+ */
+ HttpsRequest(NetworkInterface* net_iface,
+ const char* ssl_ca_pem,
+ http_method method,
+ const char* url,
+ Callback<void(const char *at, size_t length)> body_callback = 0)
+ {
+ _parsed_url = new ParsedUrl(url);
+ _body_callback = body_callback;
+ _request_builder = new HttpRequestBuilder(method, _parsed_url);
+ _response = NULL;
+ _debug = false;
+
+ _tlssocket = new TLSSocket(net_iface, _parsed_url->host(), _parsed_url->port(), ssl_ca_pem);
+ _we_created_the_socket = true;
+ }
+
+ /**
+ * HttpsRequest Constructor
+ * Sets up event handlers and flags.
+ *
+ * @param[in] socket A connected TLSSocket
+ * @param[in] method HTTP method to use
+ * @param[in] url URL to the resource
+ * @param[in] body_callback Callback on which to retrieve chunks of the response body.
+ If not set, the complete body will be allocated on the HttpResponse object,
+ which might use lots of memory.
+ */
+ HttpsRequest(TLSSocket* socket,
+ http_method method,
+ const char* url,
+ Callback<void(const char *at, size_t length)> body_callback = 0)
+ {
+ _parsed_url = new ParsedUrl(url);
+ _body_callback = body_callback;
+ _request_builder = new HttpRequestBuilder(method, _parsed_url);
+ _response = NULL;
+ _debug = false;
+
+ _tlssocket = socket;
+ _we_created_the_socket = false;
+ }
+
+ /**
+ * HttpsRequest Destructor
+ */
+ ~HttpsRequest() {
+ if (_request_builder) {
+ delete _request_builder;
+ }
+
+ if (_tlssocket && _we_created_the_socket) {
+ delete _tlssocket;
+ }
+
+ if (_parsed_url) {
+ delete _parsed_url;
+ }
+
+ if (_response) {
+ delete _response;
+ }
+ }
+
+ /**
+ * Execute the HTTPS request.
+ *
+ * @param[in] body Pointer to the request body
+ * @param[in] body_size Size of the request body
+ * @return An HttpResponse pointer on success, or NULL on failure.
+ * See get_error() for the error code.
+ */
+ HttpResponse* send(const void* body = NULL, nsapi_size_t body_size = 0) {
+ // not tried to connect before?
+ if (_tlssocket->error() != 0) {
+ _error = _tlssocket->error();
+ return NULL;
+ }
+
+ bool socket_was_open = _tlssocket->connected();
+
+ if (!socket_was_open) {
+ nsapi_error_t r = _tlssocket->connect();
+ if (r != 0) {
+ _error = r;
+ return NULL;
+ }
+ }
+
+ int ret;
+
+ size_t request_size = 0;
+ char* request = _request_builder->build(body, body_size, request_size);
+
+ ret = mbedtls_ssl_write(_tlssocket->get_ssl_context(), (const unsigned char *) request, request_size);
+
+ free(request);
+
+ if (ret < 0) {
+ if (ret != MBEDTLS_ERR_SSL_WANT_READ &&
+ ret != MBEDTLS_ERR_SSL_WANT_WRITE) {
+ print_mbedtls_error("mbedtls_ssl_write", ret);
+ onError(_tlssocket->get_tcp_socket(), -1 );
+ }
+ else {
+ _error = ret;
+ }
+ return NULL;
+ }
+
+ // Create a response object
+ _response = new HttpResponse();
+ // And a response parser
+ HttpParser parser(_response, HTTP_RESPONSE, _body_callback);
+
+ // Set up a receive buffer (on the heap)
+ uint8_t* recv_buffer = (uint8_t*)malloc(HTTP_RECEIVE_BUFFER_SIZE);
+
+ /* Read data out of the socket */
+ while ((ret = mbedtls_ssl_read(_tlssocket->get_ssl_context(), (unsigned char *) recv_buffer, HTTP_RECEIVE_BUFFER_SIZE)) > 0) {
+ // Don't know if this is actually needed, but OK
+ size_t _bpos = static_cast<size_t>(ret);
+ recv_buffer[_bpos] = 0;
+
+ size_t nparsed = parser.execute((const char*)recv_buffer, _bpos);
+ if (nparsed != _bpos) {
+ print_mbedtls_error("parser_error", nparsed);
+ // parser error...
+ _error = -2101;
+ free(recv_buffer);
+ return NULL;
+ }
+
+ if (_response->is_message_complete()) {
+ break;
+ }
+ }
+ if (ret < 0) {
+ if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) {
+ print_mbedtls_error("mbedtls_ssl_read", ret);
+ onError(_tlssocket->get_tcp_socket(), -1 );
+ }
+ else {
+ _error = ret;
+ }
+ free(recv_buffer);
+ return NULL;
+ }
+
+ parser.finish();
+
+ if (!socket_was_open) {
+ _tlssocket->get_tcp_socket()->close();
+ }
+
+ free(recv_buffer);
+
+ return _response;
+ }
+
+ /**
+ * Closes the underlying TCP socket
+ */
+ void close() {
+ _tlssocket->get_tcp_socket()->close();
+ }
+
+ /**
+ * Set a header for the request.
+ *
+ * The 'Host' and 'Content-Length' headers are set automatically.
+ * Setting the same header twice will overwrite the previous entry.
+ *
+ * @param[in] key Header key
+ * @param[in] value Header value
+ */
+ void set_header(string key, string value) {
+ _request_builder->set_header(key, value);
+ }
+
+ /**
+ * Get the error code.
+ *
+ * When send() fails, this error is set.
+ */
+ nsapi_error_t get_error() {
+ return _error;
+ }
+
+ /**
+ * Set the debug flag.
+ *
+ * If this flag is set, debug information from mbed TLS will be logged to stdout.
+ */
+ void set_debug(bool debug) {
+ _debug = debug;
+
+ _tlssocket->set_debug(debug);
+ }
+
+
+protected:
+ /**
+ * Helper for pretty-printing mbed TLS error codes
+ */
+ static void print_mbedtls_error(const char *name, int err) {
+ char buf[128];
+ mbedtls_strerror(err, buf, sizeof (buf));
+ mbedtls_printf("%s() failed: -0x%04x (%d): %s\r\n", name, -err, err, buf);
+ }
+
+ void onError(TCPSocket *s, int error) {
+ s->close();
+ _error = error;
+ }
+
+protected:
+ TLSSocket* _tlssocket;
+ bool _we_created_the_socket;
+
+ Callback<void(const char *at, size_t length)> _body_callback;
+ ParsedUrl* _parsed_url;
+ HttpRequestBuilder* _request_builder;
+ HttpResponse* _response;
+
+ nsapi_error_t _error;
+ bool _debug;
+
+};
+
+#endif // _MBED_HTTPS_REQUEST_H_
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mbed-http/source/tls_socket.h Sat Oct 09 14:23:23 2021 +0000
@@ -0,0 +1,332 @@
+/*
+ * PackageLicenseDeclared: Apache-2.0
+ * Copyright (c) 2017 ARM Limited
+ *
+ * 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.
+ */
+
+#ifndef _MBED_HTTPS_TLS_SOCKET_H_
+#define _MBED_HTTPS_TLS_SOCKET_H_
+
+/* Change to a number between 1 and 4 to debug the TLS connection */
+#define DEBUG_LEVEL 0
+
+#include <string>
+#include <vector>
+#include <map>
+#include "http_parser.h"
+#include "http_response.h"
+#include "http_request_builder.h"
+#include "http_request_parser.h"
+#include "http_parsed_url.h"
+
+#include "mbedtls/platform.h"
+#include "mbedtls/ssl.h"
+#include "mbedtls/entropy.h"
+#include "mbedtls/ctr_drbg.h"
+#include "mbedtls/error.h"
+
+#if DEBUG_LEVEL > 0
+#include "mbedtls/debug.h"
+#endif
+
+/**
+ * \brief TLSSocket a wrapper around TCPSocket for interacting with TLS servers
+ */
+class TLSSocket {
+public:
+ TLSSocket(NetworkInterface* net_iface, const char* hostname, uint16_t port, const char* ssl_ca_pem) {
+ _tcpsocket = new TCPSocket(net_iface);
+ _ssl_ca_pem = ssl_ca_pem;
+ _is_connected = false;
+ _debug = false;
+ _hostname = hostname;
+ _port = port;
+ _error = 0;
+
+ DRBG_PERS = "mbed TLS helloword client";
+
+ mbedtls_entropy_init(&_entropy);
+ mbedtls_ctr_drbg_init(&_ctr_drbg);
+ mbedtls_x509_crt_init(&_cacert);
+ mbedtls_ssl_init(&_ssl);
+ mbedtls_ssl_config_init(&_ssl_conf);
+ }
+
+ ~TLSSocket() {
+ mbedtls_entropy_free(&_entropy);
+ mbedtls_ctr_drbg_free(&_ctr_drbg);
+ mbedtls_x509_crt_free(&_cacert);
+ mbedtls_ssl_free(&_ssl);
+ mbedtls_ssl_config_free(&_ssl_conf);
+
+ if (_tcpsocket) {
+ _tcpsocket->close();
+ delete _tcpsocket;
+ }
+
+ // @todo: free DRBG_PERS ?
+ }
+
+ nsapi_error_t connect() {
+ /* Initialize the flags */
+ /*
+ * Initialize TLS-related stuf.
+ */
+ int ret;
+ if ((ret = mbedtls_ctr_drbg_seed(&_ctr_drbg, mbedtls_entropy_func, &_entropy,
+ (const unsigned char *) DRBG_PERS,
+ sizeof (DRBG_PERS))) != 0) {
+ print_mbedtls_error("mbedtls_crt_drbg_init", ret);
+ _error = ret;
+ return _error;
+ }
+
+ if ((ret = mbedtls_x509_crt_parse(&_cacert, (const unsigned char *)_ssl_ca_pem,
+ strlen(_ssl_ca_pem) + 1)) != 0) {
+ print_mbedtls_error("mbedtls_x509_crt_parse", ret);
+ _error = ret;
+ return _error;
+ }
+
+ if ((ret = mbedtls_ssl_config_defaults(&_ssl_conf,
+ MBEDTLS_SSL_IS_CLIENT,
+ MBEDTLS_SSL_TRANSPORT_STREAM,
+ MBEDTLS_SSL_PRESET_DEFAULT)) != 0) {
+ print_mbedtls_error("mbedtls_ssl_config_defaults", ret);
+ _error = ret;
+ return _error;
+ }
+
+ mbedtls_ssl_conf_ca_chain(&_ssl_conf, &_cacert, NULL);
+ mbedtls_ssl_conf_rng(&_ssl_conf, mbedtls_ctr_drbg_random, &_ctr_drbg);
+
+ /* It is possible to disable authentication by passing
+ * MBEDTLS_SSL_VERIFY_NONE in the call to mbedtls_ssl_conf_authmode()
+ */
+ mbedtls_ssl_conf_authmode(&_ssl_conf, MBEDTLS_SSL_VERIFY_REQUIRED);
+
+#if DEBUG_LEVEL > 0
+ mbedtls_ssl_conf_verify(&_ssl_conf, my_verify, NULL);
+ mbedtls_ssl_conf_dbg(&_ssl_conf, my_debug, NULL);
+ mbedtls_debug_set_threshold(DEBUG_LEVEL);
+#endif
+
+ if ((ret = mbedtls_ssl_setup(&_ssl, &_ssl_conf)) != 0) {
+ print_mbedtls_error("mbedtls_ssl_setup", ret);
+ _error = ret;
+ return _error;
+ }
+
+ mbedtls_ssl_set_hostname(&_ssl, _hostname);
+
+ mbedtls_ssl_set_bio(&_ssl, static_cast<void *>(_tcpsocket),
+ ssl_send, ssl_recv, NULL );
+
+ /* Connect to the server */
+ if (_debug) mbedtls_printf("Connecting to %s:%d\r\n", _hostname, _port);
+ ret = _tcpsocket->connect(_hostname, _port);
+ if (ret != NSAPI_ERROR_OK) {
+ if (_debug) mbedtls_printf("Failed to connect\r\n");
+ onError(_tcpsocket, -1);
+ return _error;
+ }
+
+ /* Start the handshake, the rest will be done in onReceive() */
+ if (_debug) mbedtls_printf("Starting the TLS handshake...\r\n");
+ ret = mbedtls_ssl_handshake(&_ssl);
+ if (ret < 0) {
+ if (ret != MBEDTLS_ERR_SSL_WANT_READ &&
+ ret != MBEDTLS_ERR_SSL_WANT_WRITE) {
+ print_mbedtls_error("mbedtls_ssl_handshake", ret);
+ onError(_tcpsocket, -1);
+ }
+ else {
+ _error = ret;
+ }
+ return _error;
+ }
+
+ /* It also means the handshake is done, time to print info */
+ if (_debug) mbedtls_printf("TLS connection to %s:%d established\r\n", _hostname, _port);
+
+ const uint32_t buf_size = 1024;
+ char *buf = new char[buf_size];
+ mbedtls_x509_crt_info(buf, buf_size, "\r ",
+ mbedtls_ssl_get_peer_cert(&_ssl));
+ if (_debug) mbedtls_printf("Server certificate:\r\n%s\r", buf);
+
+ uint32_t flags = mbedtls_ssl_get_verify_result(&_ssl);
+ if( flags != 0 )
+ {
+ mbedtls_x509_crt_verify_info(buf, buf_size, "\r ! ", flags);
+ if (_debug) mbedtls_printf("Certificate verification failed:\r\n%s\r\r\n", buf);
+ }
+ else {
+ if (_debug) mbedtls_printf("Certificate verification passed\r\n\r\n");
+ }
+
+ _is_connected = true;
+
+ return 0;
+ }
+
+ bool connected() {
+ return _is_connected;
+ }
+
+ nsapi_error_t error() {
+ return _error;
+ }
+
+ TCPSocket* get_tcp_socket() {
+ return _tcpsocket;
+ }
+
+ mbedtls_ssl_context* get_ssl_context() {
+ return &_ssl;
+ }
+
+ /**
+ * Set the debug flag.
+ *
+ * If this flag is set, debug information from mbed TLS will be logged to stdout.
+ */
+ void set_debug(bool debug) {
+ _debug = debug;
+ }
+
+protected:
+ /**
+ * Helper for pretty-printing mbed TLS error codes
+ */
+ static void print_mbedtls_error(const char *name, int err) {
+ char buf[128];
+ mbedtls_strerror(err, buf, sizeof (buf));
+ mbedtls_printf("%s() failed: -0x%04x (%d): %s\r\n", name, -err, err, buf);
+ }
+
+#if DEBUG_LEVEL > 0
+ /**
+ * Debug callback for mbed TLS
+ * Just prints on the USB serial port
+ */
+ static void my_debug(void *ctx, int level, const char *file, int line,
+ const char *str)
+ {
+ const char *p, *basename;
+ (void) ctx;
+
+ /* Extract basename from file */
+ for(p = basename = file; *p != '\0'; p++) {
+ if(*p == '/' || *p == '\\') {
+ basename = p + 1;
+ }
+ }
+
+ if (_debug) {
+ mbedtls_printf("%s:%04d: |%d| %s", basename, line, level, str);
+ }
+ }
+
+ /**
+ * Certificate verification callback for mbed TLS
+ * Here we only use it to display information on each cert in the chain
+ */
+ static int my_verify(void *data, mbedtls_x509_crt *crt, int depth, uint32_t *flags)
+ {
+ const uint32_t buf_size = 1024;
+ char *buf = new char[buf_size];
+ (void) data;
+
+ if (_debug) mbedtls_printf("\nVerifying certificate at depth %d:\n", depth);
+ mbedtls_x509_crt_info(buf, buf_size - 1, " ", crt);
+ if (_debug) mbedtls_printf("%s", buf);
+
+ if (*flags == 0)
+ if (_debug) mbedtls_printf("No verification issue for this certificate\n");
+ else
+ {
+ mbedtls_x509_crt_verify_info(buf, buf_size, " ! ", *flags);
+ if (_debug) mbedtls_printf("%s\n", buf);
+ }
+
+ delete[] buf;
+ return 0;
+ }
+#endif
+
+ /**
+ * Receive callback for mbed TLS
+ */
+ static int ssl_recv(void *ctx, unsigned char *buf, size_t len) {
+ int recv = -1;
+ TCPSocket *socket = static_cast<TCPSocket *>(ctx);
+ recv = socket->recv(buf, len);
+
+ if (NSAPI_ERROR_WOULD_BLOCK == recv) {
+ return MBEDTLS_ERR_SSL_WANT_READ;
+ }
+ else if (recv < 0) {
+ return -1;
+ }
+ else {
+ return recv;
+ }
+ }
+
+ /**
+ * Send callback for mbed TLS
+ */
+ static int ssl_send(void *ctx, const unsigned char *buf, size_t len) {
+ int size = -1;
+ TCPSocket *socket = static_cast<TCPSocket *>(ctx);
+ size = socket->send(buf, len);
+
+ if(NSAPI_ERROR_WOULD_BLOCK == size) {
+ return len;
+ }
+ else if (size < 0){
+ return -1;
+ }
+ else {
+ return size;
+ }
+ }
+
+private:
+ void onError(TCPSocket *s, int error) {
+ s->close();
+ _error = error;
+ }
+
+ TCPSocket* _tcpsocket;
+
+ const char* DRBG_PERS;
+ const char* _ssl_ca_pem;
+ const char* _hostname;
+ uint16_t _port;
+
+ bool _debug;
+ bool _is_connected;
+
+ nsapi_error_t _error;
+
+ mbedtls_entropy_context _entropy;
+ mbedtls_ctr_drbg_context _ctr_drbg;
+ mbedtls_x509_crt _cacert;
+ mbedtls_ssl_context _ssl;
+ mbedtls_ssl_config _ssl_conf;
+};
+
+#endif // _MBED_HTTPS_TLS_SOCKET_H_
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mbed-os.lib Sat Oct 09 14:23:23 2021 +0000 @@ -0,0 +1,1 @@ +https://github.com/ARMmbed/mbed-os/#0be7685a27f28c02737f42055f4a9c182a7b483c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mbed_app.json Sat Oct 09 14:23:23 2021 +0000
@@ -0,0 +1,12 @@
+{
+ "macros": ["MBED_SYS_STATS_ENABLED"],
+ "target_overrides": {
+ "*": {
+ "platform.stdio-baud-rate": 115200,
+ "platform.stdio-convert-newlines": true,
+ "platform.default-serial-baud-rate": 115200,
+ "mbed-trace.enable": 0,
+ "mbed-http.http-buffer-size": 2048
+ }
+ }
+}
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/profiles/debug-c++11.json Sat Oct 09 14:23:23 2021 +0000
@@ -0,0 +1,49 @@
+{
+ "GCC_ARM": {
+ "common": ["-c", "-Wall", "-Wextra",
+ "-Wno-unused-parameter", "-Wno-missing-field-initializers",
+ "-fmessage-length=0", "-fno-exceptions", "-fno-builtin",
+ "-ffunction-sections", "-fdata-sections", "-funsigned-char",
+ "-MMD", "-fno-delete-null-pointer-checks",
+ "-fomit-frame-pointer", "-O0", "-g3", "-DMBED_DEBUG",
+ "-DMBED_TRAP_ERRORS_ENABLED=1"],
+ "asm": ["-x", "assembler-with-cpp"],
+ "c": ["-std=gnu99"],
+ "cxx": ["-std=gnu++11", "-fno-rtti", "-Wvla"],
+ "ld": ["-Wl,--gc-sections", "-Wl,--wrap,main", "-Wl,--wrap,_malloc_r",
+ "-Wl,--wrap,_free_r", "-Wl,--wrap,_realloc_r", "-Wl,--wrap,_memalign_r",
+ "-Wl,--wrap,_calloc_r", "-Wl,--wrap,exit", "-Wl,--wrap,atexit",
+ "-Wl,-n"]
+ },
+ "ARM": {
+ "common": ["-c", "--gnu", "-Otime", "--split_sections",
+ "--apcs=interwork", "--brief_diagnostics", "--restrict",
+ "--multibyte_chars", "-O0", "-g", "-DMBED_DEBUG",
+ "-DMBED_TRAP_ERRORS_ENABLED=1"],
+ "asm": [],
+ "c": ["--md", "--no_depend_system_headers", "--c99", "-D__ASSERT_MSG"],
+ "cxx": ["--cpp", "--no_rtti", "--no_vla"],
+ "ld": []
+ },
+ "uARM": {
+ "common": ["-c", "--gnu", "-Otime", "--split_sections",
+ "--apcs=interwork", "--brief_diagnostics", "--restrict",
+ "--multibyte_chars", "-O0", "-D__MICROLIB", "-g",
+ "--library_type=microlib", "-DMBED_RTOS_SINGLE_THREAD", "-DMBED_DEBUG",
+ "-DMBED_TRAP_ERRORS_ENABLED=1"],
+ "asm": [],
+ "c": ["--md", "--no_depend_system_headers", "--c99", "-D__ASSERT_MSG"],
+ "cxx": ["--cpp", "--no_rtti", "--no_vla"],
+ "ld": ["--library_type=microlib"]
+ },
+ "IAR": {
+ "common": [
+ "--no_wrap_diagnostics", "-e",
+ "--diag_suppress=Pa050,Pa084,Pa093,Pa082", "-On", "-r", "-DMBED_DEBUG",
+ "-DMBED_TRAP_ERRORS_ENABLED=1"],
+ "asm": [],
+ "c": ["--vla"],
+ "cxx": ["--guard_calls", "--no_static_destruction"],
+ "ld": ["--skip_dynamic_initialization", "--threaded_lib"]
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/profiles/develop-c++11.json Sat Oct 09 14:23:23 2021 +0000
@@ -0,0 +1,45 @@
+{
+ "GCC_ARM": {
+ "common": ["-c", "-Wall", "-Wextra",
+ "-Wno-unused-parameter", "-Wno-missing-field-initializers",
+ "-fmessage-length=0", "-fno-exceptions", "-fno-builtin",
+ "-ffunction-sections", "-fdata-sections", "-funsigned-char",
+ "-MMD", "-fno-delete-null-pointer-checks",
+ "-fomit-frame-pointer", "-Os"],
+ "asm": ["-x", "assembler-with-cpp"],
+ "c": ["-std=gnu99"],
+ "cxx": ["-std=gnu++11", "-fno-rtti", "-Wvla"],
+ "ld": ["-Wl,--gc-sections", "-Wl,--wrap,main", "-Wl,--wrap,_malloc_r",
+ "-Wl,--wrap,_free_r", "-Wl,--wrap,_realloc_r", "-Wl,--wrap,_memalign_r",
+ "-Wl,--wrap,_calloc_r", "-Wl,--wrap,exit", "-Wl,--wrap,atexit",
+ "-Wl,-n"]
+ },
+ "ARM": {
+ "common": ["-c", "--gnu", "-Otime", "--split_sections",
+ "--apcs=interwork", "--brief_diagnostics", "--restrict",
+ "--multibyte_chars", "-O3"],
+ "asm": [],
+ "c": ["--md", "--no_depend_system_headers", "--c99", "-D__ASSERT_MSG"],
+ "cxx": ["--cpp", "--no_rtti", "--no_vla"],
+ "ld": []
+ },
+ "uARM": {
+ "common": ["-c", "--gnu", "-Otime", "--split_sections",
+ "--apcs=interwork", "--brief_diagnostics", "--restrict",
+ "--multibyte_chars", "-O3", "-D__MICROLIB",
+ "--library_type=microlib", "-DMBED_RTOS_SINGLE_THREAD"],
+ "asm": [],
+ "c": ["--md", "--no_depend_system_headers", "--c99", "-D__ASSERT_MSG"],
+ "cxx": ["--cpp", "--no_rtti", "--no_vla"],
+ "ld": ["--library_type=microlib"]
+ },
+ "IAR": {
+ "common": [
+ "--no_wrap_diagnostics", "-e",
+ "--diag_suppress=Pa050,Pa084,Pa093,Pa082", "-Oh"],
+ "asm": [],
+ "c": ["--vla"],
+ "cxx": ["--guard_calls", "--no_static_destruction"],
+ "ld": ["--skip_dynamic_initialization", "--threaded_lib"]
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/profiles/release-c++11.json Sat Oct 09 14:23:23 2021 +0000
@@ -0,0 +1,45 @@
+{
+ "GCC_ARM": {
+ "common": ["-c", "-Wall", "-Wextra",
+ "-Wno-unused-parameter", "-Wno-missing-field-initializers",
+ "-fmessage-length=0", "-fno-exceptions", "-fno-builtin",
+ "-ffunction-sections", "-fdata-sections", "-funsigned-char",
+ "-MMD", "-fno-delete-null-pointer-checks",
+ "-fomit-frame-pointer", "-Os", "-DNDEBUG"],
+ "asm": ["-x", "assembler-with-cpp"],
+ "c": ["-std=gnu99"],
+ "cxx": ["-std=gnu++11", "-fno-rtti", "-Wvla"],
+ "ld": ["-Wl,--gc-sections", "-Wl,--wrap,main", "-Wl,--wrap,_malloc_r",
+ "-Wl,--wrap,_free_r", "-Wl,--wrap,_realloc_r", "-Wl,--wrap,_memalign_r",
+ "-Wl,--wrap,_calloc_r", "-Wl,--wrap,exit", "-Wl,--wrap,atexit",
+ "-Wl,-n"]
+ },
+ "ARM": {
+ "common": ["-c", "--gnu", "-Ospace", "--split_sections",
+ "--apcs=interwork", "--brief_diagnostics", "--restrict",
+ "--multibyte_chars", "-O3", "-DNDEBUG"],
+ "asm": [],
+ "c": ["--md", "--no_depend_system_headers", "--c99", "-D__ASSERT_MSG"],
+ "cxx": ["--cpp", "--no_rtti", "--no_vla"],
+ "ld": []
+ },
+ "uARM": {
+ "common": ["-c", "--gnu", "-Ospace", "--split_sections",
+ "--apcs=interwork", "--brief_diagnostics", "--restrict",
+ "--multibyte_chars", "-O3", "-D__MICROLIB",
+ "--library_type=microlib", "-DMBED_RTOS_SINGLE_THREAD", "-DNDEBUG"],
+ "asm": [],
+ "c": ["--md", "--no_depend_system_headers", "--c99", "-D__ASSERT_MSG"],
+ "cxx": ["--cpp", "--no_rtti", "--no_vla"],
+ "ld": ["--library_type=microlib"]
+ },
+ "IAR": {
+ "common": [
+ "--no_wrap_diagnostics", "-e",
+ "--diag_suppress=Pa050,Pa084,Pa093,Pa082", "-Ohz", "-DNDEBUG"],
+ "asm": [],
+ "c": ["--vla"],
+ "cxx": ["--guard_calls", "--no_static_destruction"],
+ "ld": ["--skip_dynamic_initialization", "--threaded_lib"]
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/source/http_response_builder.h Sat Oct 09 14:23:23 2021 +0000
@@ -0,0 +1,185 @@
+/*
+ * PackageLicenseDeclared: Apache-2.0
+ * Copyright (c) 2017 ARM Limited
+ *
+ * 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.
+ */
+
+#ifndef _MBED_HTTP_RESPONSE_BUILDER_
+#define _MBED_HTTP_RESPONSE_BUILDER_
+
+#include <string>
+#include <map>
+#include "http_parser.h"
+#include "http_parsed_url.h"
+
+static const char* get_http_status_string(uint16_t status_code) {
+ switch (status_code) {
+ case 100: return "Continue";
+ case 101: return "Switching Protocols";
+ case 102: return "Processing";
+ case 200: return "OK";
+ case 201: return "Created";
+ case 202: return "Accepted";
+ case 203: return "Non-Authoritative Information";
+ case 204: return "No Content";
+ case 205: return "Reset Content";
+ case 206: return "Partial Content";
+ case 207: return "Multi-Status";
+ case 208: return "Already Reported";
+ case 226: return "IM Used";
+ case 300: return "Multiple Choices";
+ case 301: return "Moved Permanently";
+ case 302: return "Found";
+ case 303: return "See Other";
+ case 304: return "Not Modified";
+ case 305: return "Use Proxy";
+ case 307: return "Temporary Redirect";
+ case 308: return "Permanent Redirect";
+ case 400: return "Bad Request";
+ case 401: return "Unauthorized";
+ case 402: return "Payment Required";
+ case 403: return "Forbidden";
+ case 404: return "Not Found";
+ case 405: return "Method Not Allowed";
+ case 406: return "Not Acceptable";
+ case 407: return "Proxy Authentication Required";
+ case 408: return "Request Timeout";
+ case 409: return "Conflict";
+ case 410: return "Gone";
+ case 411: return "Length Required";
+ case 412: return "Precondition Failed";
+ case 413: return "Payload Too Large";
+ case 414: return "URI Too Long";
+ case 415: return "Unsupported Media Type";
+ case 416: return "Range Not Satisfiable";
+ case 417: return "Expectation Failed";
+ case 421: return "Misdirected Request";
+ case 422: return "Unprocessable Entity";
+ case 423: return "Locked";
+ case 424: return "Failed Dependency";
+ case 426: return "Upgrade Required";
+ case 428: return "Precondition Required";
+ case 429: return "Too Many Requests";
+ case 431: return "Request Header Fields Too Large";
+ case 451: return "Unavailable For Legal Reasons";
+ case 500: return "Internal Server Error";
+ case 501: return "Not Implemented";
+ case 502: return "Bad Gateway";
+ case 503: return "Service Unavailable";
+ case 504: return "Gateway Timeout";
+ case 505: return "HTTP Version Not Supported";
+ case 506: return "Variant Also Negotiates";
+ case 507: return "Insufficient Storage";
+ case 508: return "Loop Detected";
+ case 510: return "Not Extended";
+ case 511: return "Network Authentication Required";
+ default : return "Unknown";
+ }
+}
+
+class HttpResponseBuilder {
+public:
+ HttpResponseBuilder(uint16_t a_status_code)
+ : status_code(a_status_code), status_message(get_http_status_string(a_status_code))
+ {
+ }
+
+ /**
+ * Set a header for the request
+ * If the key already exists, it will be overwritten...
+ */
+ void set_header(string key, string value) {
+ map<string, string>::iterator it = headers.find(key);
+
+ if (it != headers.end()) {
+ it->second = value;
+ }
+ else {
+ headers.insert(headers.end(), pair<string, string>(key, value));
+ }
+ }
+
+ char* build(const void* body, size_t body_size, size_t* size) {
+ char buffer[10];
+ snprintf(buffer, sizeof(buffer), "%d", body_size);
+ set_header("Content-Length", string(buffer));
+
+ char status_code_buffer[5];
+ snprintf(status_code_buffer, sizeof(status_code_buffer), "%d", status_code /* max 5 digits */);
+
+ *size = 0;
+
+ // first line is HTTP/1.1 200 OK\r\n
+ *size += 8 + 1 + strlen(status_code_buffer) + 1 + strlen(status_message) + 2;
+
+ // after that we'll do the headers
+ typedef map<string, string>::iterator it_type;
+ for(it_type it = headers.begin(); it != headers.end(); it++) {
+ // line is KEY: VALUE\r\n
+ *size += it->first.length() + 1 + 1 + it->second.length() + 2;
+ }
+
+ // then the body, first an extra newline
+ *size += 2;
+
+ // body
+ *size += body_size;
+
+ // Now let's print it
+ char* res = (char*)calloc(*size + 1, 1);
+ char* originalRes = res;
+
+ res += sprintf(res, "HTTP/1.1 %s %s\r\n", status_code_buffer, status_message);
+
+ typedef map<string, string>::iterator it_type;
+ for(it_type it = headers.begin(); it != headers.end(); it++) {
+ // line is KEY: VALUE\r\n
+ res += sprintf(res, "%s: %s\r\n", it->first.c_str(), it->second.c_str());
+ }
+
+ res += sprintf(res, "\r\n");
+
+ if (body_size > 0) {
+ memcpy(res, body, body_size);
+ }
+ res += body_size;
+
+ // Uncomment to debug...
+ // printf("----- BEGIN RESPONSE -----\n");
+ // printf("%s", originalRes);
+ // printf("----- END RESPONSE -----\n");
+
+ return originalRes;
+ }
+
+ nsapi_error_t send(TCPSocket* socket, const void* body, size_t body_size) {
+ if (!socket) return NSAPI_ERROR_NO_SOCKET;
+
+ size_t res_size;
+ char* response = build(body, body_size, &res_size);
+
+ nsapi_error_t r = socket->send(response, res_size);
+
+ free(response);
+
+ return r;
+ }
+
+private:
+ uint16_t status_code;
+ const char* status_message;
+ map<string, string> headers;
+};
+
+#endif // _MBED_HTTP_RESPONSE_BUILDER_
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/source/http_server.h Sat Oct 09 14:23:23 2021 +0000
@@ -0,0 +1,196 @@
+/*
+ * PackageLicenseDeclared: Apache-2.0
+ * Copyright (c) 2017 ARM Limited
+ *
+ * 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.
+ */
+
+#ifndef _HTTP_SERVER_
+#define _HTTP_SERVER_
+
+#include "mbed.h"
+#include "http_request_parser.h"
+#include "http_response.h"
+#include "http_response_builder.h"
+
+#ifndef HTTP_SERVER_MAX_CONCURRENT
+#define HTTP_SERVER_MAX_CONCURRENT 5
+#endif
+
+typedef HttpResponse ParsedHttpRequest;
+
+/**
+ * \brief HttpServer implements the logic for setting up an HTTP server.
+ */
+class HttpServer {
+public:
+ /**
+ * HttpRequest Constructor
+ *
+ * @param[in] network The network interface
+ */
+ HttpServer(NetworkInterface* network) {
+ _network = network;
+ }
+
+ ~HttpServer() {
+ for (size_t ix = 0; ix < HTTP_SERVER_MAX_CONCURRENT; ix++) {
+ if (socket_threads[ix]) {
+ delete socket_threads[ix];
+ }
+ }
+ }
+
+ /**
+ * Start running the server (it will run on it's own thread)
+ */
+ nsapi_error_t start(uint16_t port, Callback<void(ParsedHttpRequest* request, TCPSocket* socket)> a_handler) {
+ server = new TCPServer();
+
+ nsapi_error_t ret;
+
+ ret = server->open(_network);
+ if (ret != NSAPI_ERROR_OK) {
+ return ret;
+ }
+
+ ret = server->bind(port);
+ if (ret != NSAPI_ERROR_OK) {
+ return ret;
+ }
+
+ server->listen(HTTP_SERVER_MAX_CONCURRENT); // max. concurrent connections...
+
+ handler = a_handler;
+
+ main_thread.start(callback(this, &HttpServer::main));
+
+ return NSAPI_ERROR_OK;
+ }
+
+private:
+
+ void receive_data() {
+ // UNSAFE: should Mutex around it or something
+ TCPSocket* socket = sockets.back();
+
+ // needs to keep running until the socket gets closed
+ while (1) {
+
+ ParsedHttpRequest* response = new ParsedHttpRequest();
+ HttpParser* parser = new HttpParser(response, HTTP_REQUEST);
+
+ // Set up a receive buffer (on the heap)
+ uint8_t* recv_buffer = (uint8_t*)malloc(HTTP_RECEIVE_BUFFER_SIZE);
+
+ // TCPSocket::recv is called until we don't have any data anymore
+ nsapi_size_or_error_t recv_ret;
+ while ((recv_ret = socket->recv(recv_buffer, HTTP_RECEIVE_BUFFER_SIZE)) > 0) {
+ // Pass the chunk into the http_parser
+ size_t nparsed = parser->execute((const char*)recv_buffer, recv_ret);
+ if (nparsed != recv_ret) {
+ printf("Parsing failed... parsed %d bytes, received %d bytes\n", nparsed, recv_ret);
+ recv_ret = -2101;
+ break;
+ }
+
+ if (response->is_message_complete()) {
+ break;
+ }
+ }
+ // error?
+ if (recv_ret <= 0) {
+ if (recv_ret < 0) {
+ printf("Error reading from socket %d\n", recv_ret);
+ }
+
+ // error = recv_ret;
+ delete response;
+ delete parser;
+ free(recv_buffer);
+
+ // q; should we always break out of the thread or only if NO_SOCKET ?
+ // should we delete socket here? the socket seems already gone...
+ if (recv_ret < -3000 || recv_ret == 0) {
+ return;
+ }
+ else {
+ continue;
+ }
+ }
+
+ // When done, call parser.finish()
+ parser->finish();
+
+ // Free the receive buffer
+ free(recv_buffer);
+
+ // Let user application handle the request, if user needs a handle to response they need to memcpy themselves
+ if (recv_ret > 0) {
+ handler(response, socket);
+ }
+
+ // Free the response and parser
+ delete response;
+ delete parser;
+ }
+ }
+
+ void main() {
+ while (1) {
+ TCPSocket* clt_sock = new TCPSocket(); // Q: when should these be cleared? When not connected anymore?
+ SocketAddress clt_addr;
+
+ nsapi_error_t accept_res = server->accept(clt_sock, &clt_addr);
+ if (accept_res == NSAPI_ERROR_OK) {
+ sockets.push_back(clt_sock); // so we can clear our disconnected sockets
+
+ // and start listening for events there
+ Thread* t = new Thread(osPriorityNormal, 2048);
+ t->start(callback(this, &HttpServer::receive_data));
+
+ socket_thread_metadata_t* m = new socket_thread_metadata_t();
+ m->socket = clt_sock;
+ m->thread = t;
+ socket_threads.push_back(m);
+ }
+ else {
+ delete clt_sock;
+ }
+
+ for (size_t ix = 0; ix < socket_threads.size(); ix++) {
+ if (socket_threads[ix]->thread->get_state() == Thread::Deleted) {
+ socket_threads[ix]->thread->terminate();
+ delete socket_threads[ix]->thread;
+ delete socket_threads[ix]->socket; // does this work on esp8266?
+ socket_threads.erase(socket_threads.begin() + ix); // what if there are two?
+ }
+ }
+
+ }
+ }
+
+ typedef struct {
+ TCPSocket* socket;
+ Thread* thread;
+ } socket_thread_metadata_t;
+
+ TCPServer* server;
+ NetworkInterface* _network;
+ Thread main_thread;
+ vector<TCPSocket*> sockets;
+ vector<socket_thread_metadata_t*> socket_threads;
+ Callback<void(ParsedHttpRequest* request, TCPSocket* socket)> handler;
+};
+
+#endif // _HTTP_SERVER
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/source/main.cpp Sat Oct 09 14:23:23 2021 +0000
@@ -0,0 +1,207 @@
+/* WiFi Example
+ * Copyright (c) 2016 ARM Limited
+ *
+ * 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.
+ */
+
+#include "mbed.h"
+#include "http_server.h"
+#include "http_response_builder.h"
+
+#define BUFFER_SIZE 1024*2
+
+WiFiInterface *wifi;
+
+TCPSocket socket;
+
+char buf[BUFFER_SIZE];
+unsigned int bufIndex=0;;
+bool RxTriggred=false;
+
+/* Flash Control hoko*/
+void _eraseFlash(void)
+{
+ FLASH_EraseInitTypeDef erase;
+ erase.TypeErase = FLASH_TYPEERASE_SECTORS; /* Select sector */
+ erase.Sector = FLASH_SECTOR_23; /* Set sector 23 */
+ erase.Banks = FLASH_BANK_2; /* Erase bank =2 */
+ erase.NbSectors = 1; /* Erase sector num */
+ erase.VoltageRange = FLASH_VOLTAGE_RANGE_3; /* Driver 3.3V */
+
+ uint32_t pageError = 0;
+
+ HAL_FLASHEx_Erase(&erase, &pageError); /* Erase use by HAL_FLASHEx */
+}
+
+void writeFlash(uint32_t address, uint8_t *data, uint32_t size)
+{
+ HAL_FLASH_Unlock(); /* Unlock FLASH */
+ _eraseFlash(); /* Erase sector 23 */
+ do {
+ HAL_FLASH_Program(FLASH_TYPEPROGRAM_BYTE, address, *data);
+ } while (++address, ++data, --size);
+ HAL_FLASH_Lock(); /* Lock FLASH */
+}
+
+void loadFlash(uint32_t address, uint8_t *data, uint32_t size)
+{
+ memcpy(data, (uint8_t*)address, size);
+}
+
+typedef struct {
+ char wifi_ssid[20] = "WIFI_SSID" ;
+ char wifi_passwd[20] = "WIFI_PASSWORD" ;
+} NTRIP_Condition;
+
+NTRIP_Condition get_ntrip_condition (){
+
+ NTRIP_Condition ntrip_condition;
+
+ mbed_stats_sys_t stats;
+ mbed_stats_sys_get(&stats);
+
+ printf("\n ---- ODIN-W2 Status -----\r\n");
+ printf("Mbed OS version %d.%d.%d\r\n", MBED_MAJOR_VERSION, MBED_MINOR_VERSION, MBED_PATCH_VERSION);
+ printf("CPU ID: 0x%x\r\n", stats.cpu_id);
+ printf("Compiler ID: %d\r\n", stats.compiler_id);
+ printf("Compiler Version: %ld\r\n", stats.compiler_version);
+
+ const uint32_t address = 0x81E0000;
+
+ loadFlash(address, (uint8_t*)&ntrip_condition, sizeof(NTRIP_Condition));
+ printf (" -------------------------\r\n");
+ printf ("| Current C099 conditions |\r\n");
+ printf (" -------------------------\r\n");
+ printf ("WiFi SSID = %s \r\n" , ntrip_condition.wifi_ssid );
+ printf ("WiFi Password = %s \r\n" , ntrip_condition.wifi_passwd );
+
+ DigitalIn switch_in0(SW0); // PUSH = 0 ;
+ switch_in0.mode(PullUp);
+ int x = switch_in0;
+ int y = 1;
+
+ char buffer[4]="OK";
+
+ printf (" --------------------------------------------\r\n");
+ printf ("| If you release RESET while turning on SW0, |\r\n");
+ printf ("| ODIN will enter the condition change mode. |\r\n");
+ printf (" --------------------------------------------\r\n");
+
+ while( (strstr(buffer,"OK") == NULL) || ( x == 0 ) ) // if SW0 is PUSH -> Change Condition
+ {
+ x = 1 ;
+ y = 0 ;
+ printf ("Please Input Condition\r\n");
+
+ printf("\r\nWiFi SSID = ");
+ scanf ("%s" , ntrip_condition.wifi_ssid );
+ printf("%s\r\nWiFi Password = " ,ntrip_condition.wifi_ssid);
+ scanf ("%s" , ntrip_condition.wifi_passwd );
+ printf("%s\r\nNtrip Server (ex:rtk2go.com)= ",ntrip_condition.wifi_passwd);
+ printf (" -------------------------\r\n");
+ printf ("| Changed C099 conditions |\r\n");
+ printf (" -------------------------\r\n");
+ printf ("WiFi SSID = %s \r\n" , ntrip_condition.wifi_ssid );
+ printf ("WiFi Password = %s \r\n" , ntrip_condition.wifi_passwd );
+ printf (" -----------------------------------------------------------------------\r\n");
+ printf ("| If this is all right, enter \"OK\". If not, enter \"NG\". (and ENTER) |\r\n");
+ printf (" -----------------------------------------------------------------------\r\n");
+
+ scanf("%s", buffer);
+ printf("%s\r\n",buffer);
+ }
+
+ if ( y == 0 ) {
+ printf ("Write changed condition\r\n");
+ writeFlash(address, (uint8_t*)&ntrip_condition, sizeof(NTRIP_Condition));
+ }
+
+ return ntrip_condition;
+}
+
+
+// Requests come in here
+void request_handler(ParsedHttpRequest* request, TCPSocket* socket) {
+
+ DigitalOut led(LED1);
+
+ printf("Request came in: %s %s\n", http_method_str(request->get_method()), request->get_url().c_str());
+
+ if (request->get_method() == HTTP_GET && request->get_url() == "/") {
+ HttpResponseBuilder builder(200);
+ builder.set_header("Content-Type", "text/html; charset=utf-8");
+
+ char response[] = "<html><head><title>Hello from mbed</title></head>"
+ "<body>"
+ "<h1>mbed webserver</h1>"
+ "<button id=\"toggle\">Toggle LED</button>"
+ "<script>document.querySelector('#toggle').onclick = function() {"
+ "var x = new XMLHttpRequest(); x.open('POST', '/toggle'); x.send();"
+ "}</script>"
+ "</body></html>";
+
+ builder.send(socket, response, sizeof(response) - 1);
+ }
+ else if (request->get_method() == HTTP_POST && request->get_url() == "/toggle") {
+ printf("toggle LED called\n");
+ led = !led;
+
+ HttpResponseBuilder builder(200);
+ builder.send(socket, NULL, 0);
+ }
+ else {
+ HttpResponseBuilder builder(404);
+ builder.send(socket, NULL, 0);
+ }
+}
+
+int main() {
+
+ NTRIP_Condition ntrip_condition = get_ntrip_condition();
+
+ printf("Mbed OS version %d.%d.%d\r\n", MBED_MAJOR_VERSION, MBED_MINOR_VERSION, MBED_PATCH_VERSION);
+
+ wifi = WiFiInterface::get_default_instance();
+ int ret=1;
+ ret = wifi->connect(ntrip_condition.wifi_ssid, ntrip_condition.wifi_passwd, NSAPI_SECURITY_WPA_WPA2);
+ if (ret != 0) {
+ printf("\nConnection error: %d\n", ret);
+ }
+ else {
+ printf("Success\n\n");
+ printf("MAC: %s\n", wifi->get_mac_address());
+ printf("IP: %s\n", wifi->get_ip_address());
+ printf("Netmask: %s\n", wifi->get_netmask());
+ printf("Gateway: %s\n", wifi->get_gateway());
+ printf("RSSI: %d\n\n", wifi->get_rssi());
+ }
+
+ // Connect to the network (see mbed_app.json for the connectivity method used)
+ if (!wifi) {
+ printf("Cannot connect to the network, see serial output\n");
+ return 1;
+ }
+
+ HttpServer server(wifi);
+ nsapi_error_t res = server.start(8080, &request_handler);
+
+ if (res == NSAPI_ERROR_OK) {
+ printf("Server is listening at http://%s:8080\n", wifi->get_ip_address());
+ }
+ else {
+ printf("Server could not be started... %d\n", res);
+ }
+
+ wait(osWaitForever);
+
+}
\ No newline at end of file