├── .gitignore ├── HttpReponse.cpp ├── HttpReponse.h ├── HttpRequest.cpp ├── HttpRequest.h ├── LICENSE ├── README.md ├── SparkTime.cpp ├── SparkTime.h ├── http_parser.cpp ├── http_parser.h ├── slre.cpp ├── slre.h └── spark.json /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files 2 | *.slo 3 | *.lo 4 | *.o 5 | *.obj 6 | 7 | # Compiled Dynamic libraries 8 | *.so 9 | *.dylib 10 | *.dll 11 | 12 | # Compiled Static libraries 13 | *.lai 14 | *.la 15 | *.a 16 | *.lib 17 | 18 | # Executables 19 | *.exe 20 | *.out 21 | *.app 22 | -------------------------------------------------------------------------------- /HttpReponse.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * @copyright Copyright © 2014 by Marc Sibert 4 | * @author Marc Sibert 5 | * 6 | * This work is free. You can redistribute it and/or modify it under the 7 | * terms of the Do What The Fuck You Want To Public License, Version 2, 8 | * as published by Sam Hocevar. See the COPYING file or http://www.wtfpl.net/ 9 | * for more details. 10 | */ 11 | 12 | #include "HttpResponse.h" 13 | 14 | const char* const HttpResponse::DEFAULT_CONTENT_TYPE = "Content-Type: text/html; charset=utf-8"; 15 | 16 | void operator<<(Stream& aStream, const HttpResponse& aResponse) { 17 | aStream.print("http/1.1 "); 18 | aStream.print(aResponse.fStatus); 19 | switch (aResponse.fStatus) { 20 | case 200 : 21 | aStream.print(" OK\r\n"); 22 | break; 23 | case 404 : 24 | aStream.print(" Not Found\r\n"); 25 | break; 26 | default : 27 | aStream.print(" \r\n"); 28 | break; 29 | } 30 | if (aResponse.fContentType) { 31 | aStream.print("Content-Type: text/html; charset=utf-8\r\n"); 32 | aStream.print(aResponse.fContentType); 33 | aStream.print("\r\n"); 34 | } 35 | if (aResponse.fContentLength >= 0) { 36 | aStream.print("Content-Length:"); 37 | aStream.print(aResponse.fContentLength); 38 | aStream.print("\r\n"); 39 | } 40 | aStream.print("Server: MServer 0.1\r\n"); 41 | aStream.print("Connection: close\r\n"); 42 | aStream.print("\r\n"); 43 | 44 | aResponse.printBody(aStream); 45 | } 46 | 47 | /** 48 | * Global defined err204 : HTTP 204 No Content. 49 | * Note : using this status, "the client SHOULD NOT change its document view..." 50 | */ 51 | Err204 err204; 52 | -------------------------------------------------------------------------------- /HttpReponse.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * @copyright Copyright © 2014 by Marc Sibert 4 | * @author Marc Sibert 5 | * 6 | * This work is free. You can redistribute it and/or modify it under the 7 | * terms of the Do What The Fuck You Want To Public License, Version 2, 8 | * as published by Sam Hocevar. See the COPYING file or http://www.wtfpl.net/ 9 | * for more details. 10 | */ 11 | 12 | #ifndef __HTTP_RESPONSE_H__ 13 | #define __HTTP_RESPONSE_H__ 14 | 15 | #include 16 | 17 | /** 18 | * Virtual class to produce implementable ones. 19 | * @see printBody() MUST be define. 20 | */ 21 | class HttpResponse { 22 | private: 23 | /// HTTP status. 24 | unsigned fStatus; 25 | /// Content-Type header value. NULL means unknown. 26 | const char* fContentType; 27 | /// Content-Length header value. -1 means unknown. 28 | long fContentLength; 29 | 30 | protected: 31 | /** 32 | * Print the current body data to the défined stream (TCP Client). 33 | * This method MUST be redefined in each subclass. 34 | * @param aStream The stream where data are writen to. 35 | * @return The Stream after writing on. 36 | */ 37 | virtual Stream& printBody(Stream& aStream) const = 0; 38 | 39 | public: 40 | /** 41 | * Constructor setting all default values. 42 | * - HTTP Status set to 200 (OK) ; 43 | * - Content-Type set to a default value. 44 | * - Content-Length set as unknown (not sent). 45 | */ 46 | HttpResponse() : 47 | fStatus(200), 48 | fContentType(DEFAULT_CONTENT_TYPE), 49 | fContentLength(-1) { 50 | } 51 | 52 | /** 53 | * Let define a value for Content-Type header. 54 | * @param aVal A pointer to a string giving the new type. 55 | * @note The original string must exists until the end of header sending. 56 | * @return The used object to be cascaded. 57 | */ 58 | HttpResponse& contentType(const char *const aVal) { 59 | fContentType = aVal; 60 | return *this; 61 | } 62 | 63 | /** 64 | * Let define a value for Content-Length header. 65 | * @param aVal A long unsigned integer giving the new value. 66 | * @return The used object to be cascaded. 67 | */ 68 | HttpResponse& contentLength(const long unsigned& aVal) { 69 | fContentLength = aVal; 70 | return *this; 71 | } 72 | 73 | /** 74 | * Let define a value for HTTP Status. 75 | * @param aVal An unsigned integer giving the new value. 76 | * @return The used object to be cascaded. 77 | */ 78 | HttpResponse& status(const unsigned& aVal) { 79 | fStatus = aVal; 80 | return *this; 81 | } 82 | 83 | /** 84 | * operator<< defined as friend to let use the following : 85 | * @code stream << HttpResponse; 86 | */ 87 | friend void operator<<(Stream& aStream, const HttpResponse& aResponse); 88 | 89 | /** 90 | * A static string containing the default Content-Type value. 91 | */ 92 | static const char *const DEFAULT_CONTENT_TYPE; 93 | 94 | }; 95 | 96 | 97 | 98 | /** 99 | * Class used to produce static pages, like html ones or fix images. 100 | **/ 101 | class HttpResponseStatic : public HttpResponse { 102 | private: 103 | /// Reference on the body content. 104 | const void *const fBody; 105 | /// Length of the body content. 106 | const size_t fLength; 107 | 108 | protected: 109 | Stream& printBody(Stream& aStream) const { 110 | if (fBody && fLength) 111 | aStream.write(static_cast(fBody), fLength); 112 | return aStream; 113 | } 114 | 115 | public: 116 | HttpResponseStatic(const void *const aBody, const size_t aLength, const char *const aContentType = NULL ) : 117 | HttpResponse(), 118 | fBody(aBody), 119 | fLength(aLength) 120 | { 121 | if (aContentType) 122 | contentType(aContentType); 123 | } 124 | 125 | }; 126 | 127 | class Err204 : public HttpResponseStatic { 128 | public: 129 | Err204() : 130 | HttpResponseStatic(NULL, 0) { 131 | status(204); 132 | }; 133 | }; 134 | 135 | extern Err204 err204; 136 | 137 | #endif // __HTTP_RESPONSE_H__ 138 | -------------------------------------------------------------------------------- /HttpRequest.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * @copyright Copyright © 2014 by Marc Sibert 4 | * @author Marc Sibert 5 | * 6 | * This work is free. You can redistribute it and/or modify it under the 7 | * terms of the Do What The Fuck You Want To Public License, Version 2, 8 | * as published by Sam Hocevar. See the COPYING file or http://www.wtfpl.net/ 9 | * for more details. 10 | */ 11 | 12 | #include "HttpRequest.h" 13 | #include 14 | 15 | void HttpRequest::setURL(const char *const aURL) { 16 | strncpy(fURL, aURL, MAX_URL_LENGTH); 17 | } 18 | 19 | #ifdef USE_HEADERS 20 | void HttpRequest::setHeaderField(const char *const aField) { 21 | strncpy(fHeaderField, aField, MAX_HEADER_FIELD_LENGTH); 22 | } 23 | 24 | void HttpRequest::setHeaderValue(const char *const aValue) { 25 | 26 | char *const pH = static_cast(malloc(strlen(fHeaderField) + 1)); // len + '\0' 27 | memcpy(pH, fHeaderField, strlen(fHeaderField) + 1); 28 | 29 | char *const pV = static_cast(malloc(strlen(aValue) + 1)); // len + '\0' 30 | memcpy(pV, aValue, strlen(aValue) + 1); 31 | 32 | // fHeaders.insert(std::pair(pH, pV)); 33 | fHeaders[pH] = pV; 34 | } 35 | #endif 36 | 37 | HttpRequest::HttpRequest() { 38 | memset(&fSettings, 0, sizeof(fSettings)); // remplissage de 0s 39 | /* 40 | fSettings.on_message_begin = onMessageBegin; 41 | fSettings.on_headers_complete = onHeadersComplete; 42 | fSettings.on_message_complete = onMessageComplete; 43 | */ 44 | fSettings.on_url = onUrl; 45 | #ifdef USE_HEADERS 46 | fSettings.on_header_field = onHeaderField; 47 | fSettings.on_header_value = onHeaderValue; 48 | #endif 49 | http_parser_init(&fParser, HTTP_REQUEST); 50 | fParser.data = static_cast(this); 51 | } 52 | 53 | HttpRequest::~HttpRequest() { 54 | /// @todo vider les fHdeaders pour éviter les fuites mémoire !!! 55 | } 56 | 57 | void HttpRequest::parse(const char aChar) { 58 | if (1 != http_parser_execute(&fParser, &fSettings, &aChar, 1)) { 59 | Serial.print("Error in http_parser_execute, line "); 60 | Serial.print(__LINE__ - 2); 61 | abort(); 62 | } 63 | } 64 | 65 | const char* HttpRequest::URL() const { 66 | return fURL; 67 | } 68 | 69 | #ifdef USE_HEADERS 70 | void HttpRequest::printHeaders() const { 71 | // for (std::map::const_iterator it = fHeaders.begin(); it != fHeaders.end(); ++it) { 72 | for (auto it = fHeaders.begin(); it != fHeaders.end(); ++it) { 73 | Serial.print(it->first); 74 | Serial.print("="); 75 | Serial.println(it->second); 76 | } 77 | } 78 | #endif 79 | 80 | /* 81 | int HttpRequest::onMessageBegin(http_parser* parser) { 82 | // HttpRequest *const pHR = static_cast(parser->data); 83 | // pHR->init(); 84 | // Serial.println("***MESSAGE BEGIN***"); 85 | return 0; 86 | } 87 | 88 | int HttpRequest::onHeadersComplete(http_parser* parser) { 89 | // HttpRequest *const pHR = static_cast(parser->data); 90 | // Serial.println("***HEADER COMPLETE***"); 91 | return 0; 92 | } 93 | 94 | int HttpRequest::onMessageComplete(http_parser* parser) { 95 | // HttpRequest *const pHR = static_cast(parser->data); 96 | // Serial.println("***MESSAGE COMPLETE***"); 97 | return 0; 98 | } 99 | */ 100 | 101 | int HttpRequest::onUrl(http_parser* parser, const char *at, size_t len) { 102 | static char c[MAX_URL_LENGTH]; 103 | 104 | if (len) { 105 | if (strlen(c) + len > MAX_URL_LENGTH) 106 | return 1; // overflow 107 | strncat(c, at, len); 108 | return 0; 109 | } 110 | HttpRequest *const pHR = static_cast(parser->data); 111 | pHR->setURL(c); 112 | c[0] = '\0'; 113 | return 0; 114 | } 115 | 116 | #ifdef USE_HEADERS 117 | int HttpRequest::onHeaderField(http_parser* parser, const char *at, size_t len) { 118 | static char c[MAX_HEADER_FIELD_LENGTH]; 119 | 120 | if (len) { 121 | if (strlen(c) + len > MAX_HEADER_FIELD_LENGTH) 122 | return 1; // overflow 123 | strncat(c, at, len); 124 | return 0; 125 | } 126 | HttpRequest *const pHR = static_cast(parser->data); 127 | pHR->setHeaderField(c); 128 | c[0] = '\0'; 129 | return 0; 130 | } 131 | 132 | int HttpRequest::onHeaderValue(http_parser* parser, const char *at, size_t len) { 133 | static char c[MAX_HEADER_VALUE_LENGTH] = ""; 134 | 135 | if (len) { 136 | if (strlen(c) + len > MAX_HEADER_VALUE_LENGTH) 137 | return 1; // overflow 138 | strncat(c, at, len); 139 | return 0; 140 | } 141 | HttpRequest *const pHR = static_cast(parser->data); 142 | pHR->setHeaderValue(c); 143 | c[0] = '\0'; 144 | return 0; 145 | } 146 | #endif 147 | -------------------------------------------------------------------------------- /HttpRequest.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file 3 | * @copyright Copyright © 2014 by Marc Sibert 4 | * @author Marc Sibert 5 | * 6 | * This work is free. You can redistribute it and/or modify it under the 7 | * terms of the Do What The Fuck You Want To Public License, Version 2, 8 | * as published by Sam Hocevar. See the COPYING file or http://www.wtfpl.net/ 9 | * for more details. 10 | */ 11 | #ifndef __HTTP_REQUEST__ 12 | #define __HTTP_REQUEST__ 13 | 14 | /** 15 | * See @url https://github.com/joyent/http-parser 16 | **/ 17 | #include "http_parser.h" 18 | #include 19 | 20 | #define MAX_URL_LENGTH 128 21 | 22 | // #define USE_HEADERS 23 | #ifdef USE_HEADERS 24 | #define MAX_HEADER_FIELD_LENGTH 128 25 | #define MAX_HEADER_VALUE_LENGTH 128 26 | #endif 27 | 28 | class HttpRequest { 29 | private: 30 | http_parser fParser; 31 | http_parser_settings fSettings; 32 | 33 | char fURL[MAX_URL_LENGTH]; 34 | #ifdef USE_HEADERS 35 | char fHeaderField[MAX_HEADER_FIELD_LENGTH]; 36 | #endif 37 | struct ltstr 38 | { 39 | bool operator()(const char* s1, const char* s2) const 40 | { 41 | return strcmp(s1, s2) < 0; 42 | } 43 | }; 44 | 45 | #ifdef USE_HEADERS 46 | std::map fHeaders; 47 | #endif 48 | // static int onMessageBegin(http_parser* parser); 49 | // static int onHeadersComplete(http_parser* parser); 50 | // static int onMessageComplete(http_parser* parser); 51 | static int onUrl(http_parser* parser, const char *at, size_t len); 52 | #ifdef USE_HEADERS 53 | static int onHeaderField(http_parser* parser, const char* at, size_t len); 54 | static int onHeaderValue(http_parser* parser, const char *at, size_t len); 55 | #endif 56 | 57 | protected: 58 | void setURL(const char *const aURL); 59 | #ifdef USE_HEADERS 60 | void setHeaderField(const char *const aField); 61 | void setHeaderValue(const char *const aValue); 62 | #endif 63 | 64 | public: 65 | HttpRequest(); 66 | ~HttpRequest(); 67 | void parse(const char aChar); 68 | 69 | /** 70 | * Return the URL after parsing the http request. 71 | * @return the parsed URL. 72 | */ 73 | const char* URL() const; 74 | #ifdef USE_HEADERS 75 | void printHeaders() const; 76 | #endif 77 | 78 | }; 79 | 80 | #endif // __HTTP_REQUEST__ 81 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU AFFERO GENERAL PUBLIC LICENSE 2 | Version 3, 19 November 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU Affero General Public License is a free, copyleft license for 11 | software and other kinds of works, specifically designed to ensure 12 | cooperation with the community in the case of network server software. 13 | 14 | The licenses for most software and other practical works are designed 15 | to take away your freedom to share and change the works. By contrast, 16 | our General Public Licenses are intended to guarantee your freedom to 17 | share and change all versions of a program--to make sure it remains free 18 | software for all its users. 19 | 20 | When we speak of free software, we are referring to freedom, not 21 | price. Our General Public Licenses are designed to make sure that you 22 | have the freedom to distribute copies of free software (and charge for 23 | them if you wish), that you receive source code or can get it if you 24 | want it, that you can change the software or use pieces of it in new 25 | free programs, and that you know you can do these things. 26 | 27 | Developers that use our General Public Licenses protect your rights 28 | with two steps: (1) assert copyright on the software, and (2) offer 29 | you this License which gives you legal permission to copy, distribute 30 | and/or modify the software. 31 | 32 | A secondary benefit of defending all users' freedom is that 33 | improvements made in alternate versions of the program, if they 34 | receive widespread use, become available for other developers to 35 | incorporate. Many developers of free software are heartened and 36 | encouraged by the resulting cooperation. However, in the case of 37 | software used on network servers, this result may fail to come about. 38 | The GNU General Public License permits making a modified version and 39 | letting the public access it on a server without ever releasing its 40 | source code to the public. 41 | 42 | The GNU Affero General Public License is designed specifically to 43 | ensure that, in such cases, the modified source code becomes available 44 | to the community. It requires the operator of a network server to 45 | provide the source code of the modified version running there to the 46 | users of that server. Therefore, public use of a modified version, on 47 | a publicly accessible server, gives the public access to the source 48 | code of the modified version. 49 | 50 | An older license, called the Affero General Public License and 51 | published by Affero, was designed to accomplish similar goals. This is 52 | a different license, not a version of the Affero GPL, but Affero has 53 | released a new version of the Affero GPL which permits relicensing under 54 | this license. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | TERMS AND CONDITIONS 60 | 61 | 0. Definitions. 62 | 63 | "This License" refers to version 3 of the GNU Affero General Public License. 64 | 65 | "Copyright" also means copyright-like laws that apply to other kinds of 66 | works, such as semiconductor masks. 67 | 68 | "The Program" refers to any copyrightable work licensed under this 69 | License. Each licensee is addressed as "you". "Licensees" and 70 | "recipients" may be individuals or organizations. 71 | 72 | To "modify" a work means to copy from or adapt all or part of the work 73 | in a fashion requiring copyright permission, other than the making of an 74 | exact copy. The resulting work is called a "modified version" of the 75 | earlier work or a work "based on" the earlier work. 76 | 77 | A "covered work" means either the unmodified Program or a work based 78 | on the Program. 79 | 80 | To "propagate" a work means to do anything with it that, without 81 | permission, would make you directly or secondarily liable for 82 | infringement under applicable copyright law, except executing it on a 83 | computer or modifying a private copy. Propagation includes copying, 84 | distribution (with or without modification), making available to the 85 | public, and in some countries other activities as well. 86 | 87 | To "convey" a work means any kind of propagation that enables other 88 | parties to make or receive copies. Mere interaction with a user through 89 | a computer network, with no transfer of a copy, is not conveying. 90 | 91 | An interactive user interface displays "Appropriate Legal Notices" 92 | to the extent that it includes a convenient and prominently visible 93 | feature that (1) displays an appropriate copyright notice, and (2) 94 | tells the user that there is no warranty for the work (except to the 95 | extent that warranties are provided), that licensees may convey the 96 | work under this License, and how to view a copy of this License. If 97 | the interface presents a list of user commands or options, such as a 98 | menu, a prominent item in the list meets this criterion. 99 | 100 | 1. Source Code. 101 | 102 | The "source code" for a work means the preferred form of the work 103 | for making modifications to it. "Object code" means any non-source 104 | form of a work. 105 | 106 | A "Standard Interface" means an interface that either is an official 107 | standard defined by a recognized standards body, or, in the case of 108 | interfaces specified for a particular programming language, one that 109 | is widely used among developers working in that language. 110 | 111 | The "System Libraries" of an executable work include anything, other 112 | than the work as a whole, that (a) is included in the normal form of 113 | packaging a Major Component, but which is not part of that Major 114 | Component, and (b) serves only to enable use of the work with that 115 | Major Component, or to implement a Standard Interface for which an 116 | implementation is available to the public in source code form. A 117 | "Major Component", in this context, means a major essential component 118 | (kernel, window system, and so on) of the specific operating system 119 | (if any) on which the executable work runs, or a compiler used to 120 | produce the work, or an object code interpreter used to run it. 121 | 122 | The "Corresponding Source" for a work in object code form means all 123 | the source code needed to generate, install, and (for an executable 124 | work) run the object code and to modify the work, including scripts to 125 | control those activities. However, it does not include the work's 126 | System Libraries, or general-purpose tools or generally available free 127 | programs which are used unmodified in performing those activities but 128 | which are not part of the work. For example, Corresponding Source 129 | includes interface definition files associated with source files for 130 | the work, and the source code for shared libraries and dynamically 131 | linked subprograms that the work is specifically designed to require, 132 | such as by intimate data communication or control flow between those 133 | subprograms and other parts of the work. 134 | 135 | The Corresponding Source need not include anything that users 136 | can regenerate automatically from other parts of the Corresponding 137 | Source. 138 | 139 | The Corresponding Source for a work in source code form is that 140 | same work. 141 | 142 | 2. Basic Permissions. 143 | 144 | All rights granted under this License are granted for the term of 145 | copyright on the Program, and are irrevocable provided the stated 146 | conditions are met. This License explicitly affirms your unlimited 147 | permission to run the unmodified Program. The output from running a 148 | covered work is covered by this License only if the output, given its 149 | content, constitutes a covered work. This License acknowledges your 150 | rights of fair use or other equivalent, as provided by copyright law. 151 | 152 | You may make, run and propagate covered works that you do not 153 | convey, without conditions so long as your license otherwise remains 154 | in force. You may convey covered works to others for the sole purpose 155 | of having them make modifications exclusively for you, or provide you 156 | with facilities for running those works, provided that you comply with 157 | the terms of this License in conveying all material for which you do 158 | not control copyright. Those thus making or running the covered works 159 | for you must do so exclusively on your behalf, under your direction 160 | and control, on terms that prohibit them from making any copies of 161 | your copyrighted material outside their relationship with you. 162 | 163 | Conveying under any other circumstances is permitted solely under 164 | the conditions stated below. Sublicensing is not allowed; section 10 165 | makes it unnecessary. 166 | 167 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 168 | 169 | No covered work shall be deemed part of an effective technological 170 | measure under any applicable law fulfilling obligations under article 171 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 172 | similar laws prohibiting or restricting circumvention of such 173 | measures. 174 | 175 | When you convey a covered work, you waive any legal power to forbid 176 | circumvention of technological measures to the extent such circumvention 177 | is effected by exercising rights under this License with respect to 178 | the covered work, and you disclaim any intention to limit operation or 179 | modification of the work as a means of enforcing, against the work's 180 | users, your or third parties' legal rights to forbid circumvention of 181 | technological measures. 182 | 183 | 4. Conveying Verbatim Copies. 184 | 185 | You may convey verbatim copies of the Program's source code as you 186 | receive it, in any medium, provided that you conspicuously and 187 | appropriately publish on each copy an appropriate copyright notice; 188 | keep intact all notices stating that this License and any 189 | non-permissive terms added in accord with section 7 apply to the code; 190 | keep intact all notices of the absence of any warranty; and give all 191 | recipients a copy of this License along with the Program. 192 | 193 | You may charge any price or no price for each copy that you convey, 194 | and you may offer support or warranty protection for a fee. 195 | 196 | 5. Conveying Modified Source Versions. 197 | 198 | You may convey a work based on the Program, or the modifications to 199 | produce it from the Program, in the form of source code under the 200 | terms of section 4, provided that you also meet all of these conditions: 201 | 202 | a) The work must carry prominent notices stating that you modified 203 | it, and giving a relevant date. 204 | 205 | b) The work must carry prominent notices stating that it is 206 | released under this License and any conditions added under section 207 | 7. This requirement modifies the requirement in section 4 to 208 | "keep intact all notices". 209 | 210 | c) You must license the entire work, as a whole, under this 211 | License to anyone who comes into possession of a copy. This 212 | License will therefore apply, along with any applicable section 7 213 | additional terms, to the whole of the work, and all its parts, 214 | regardless of how they are packaged. This License gives no 215 | permission to license the work in any other way, but it does not 216 | invalidate such permission if you have separately received it. 217 | 218 | d) If the work has interactive user interfaces, each must display 219 | Appropriate Legal Notices; however, if the Program has interactive 220 | interfaces that do not display Appropriate Legal Notices, your 221 | work need not make them do so. 222 | 223 | A compilation of a covered work with other separate and independent 224 | works, which are not by their nature extensions of the covered work, 225 | and which are not combined with it such as to form a larger program, 226 | in or on a volume of a storage or distribution medium, is called an 227 | "aggregate" if the compilation and its resulting copyright are not 228 | used to limit the access or legal rights of the compilation's users 229 | beyond what the individual works permit. Inclusion of a covered work 230 | in an aggregate does not cause this License to apply to the other 231 | parts of the aggregate. 232 | 233 | 6. Conveying Non-Source Forms. 234 | 235 | You may convey a covered work in object code form under the terms 236 | of sections 4 and 5, provided that you also convey the 237 | machine-readable Corresponding Source under the terms of this License, 238 | in one of these ways: 239 | 240 | a) Convey the object code in, or embodied in, a physical product 241 | (including a physical distribution medium), accompanied by the 242 | Corresponding Source fixed on a durable physical medium 243 | customarily used for software interchange. 244 | 245 | b) Convey the object code in, or embodied in, a physical product 246 | (including a physical distribution medium), accompanied by a 247 | written offer, valid for at least three years and valid for as 248 | long as you offer spare parts or customer support for that product 249 | model, to give anyone who possesses the object code either (1) a 250 | copy of the Corresponding Source for all the software in the 251 | product that is covered by this License, on a durable physical 252 | medium customarily used for software interchange, for a price no 253 | more than your reasonable cost of physically performing this 254 | conveying of source, or (2) access to copy the 255 | Corresponding Source from a network server at no charge. 256 | 257 | c) Convey individual copies of the object code with a copy of the 258 | written offer to provide the Corresponding Source. This 259 | alternative is allowed only occasionally and noncommercially, and 260 | only if you received the object code with such an offer, in accord 261 | with subsection 6b. 262 | 263 | d) Convey the object code by offering access from a designated 264 | place (gratis or for a charge), and offer equivalent access to the 265 | Corresponding Source in the same way through the same place at no 266 | further charge. You need not require recipients to copy the 267 | Corresponding Source along with the object code. If the place to 268 | copy the object code is a network server, the Corresponding Source 269 | may be on a different server (operated by you or a third party) 270 | that supports equivalent copying facilities, provided you maintain 271 | clear directions next to the object code saying where to find the 272 | Corresponding Source. Regardless of what server hosts the 273 | Corresponding Source, you remain obligated to ensure that it is 274 | available for as long as needed to satisfy these requirements. 275 | 276 | e) Convey the object code using peer-to-peer transmission, provided 277 | you inform other peers where the object code and Corresponding 278 | Source of the work are being offered to the general public at no 279 | charge under subsection 6d. 280 | 281 | A separable portion of the object code, whose source code is excluded 282 | from the Corresponding Source as a System Library, need not be 283 | included in conveying the object code work. 284 | 285 | A "User Product" is either (1) a "consumer product", which means any 286 | tangible personal property which is normally used for personal, family, 287 | or household purposes, or (2) anything designed or sold for incorporation 288 | into a dwelling. In determining whether a product is a consumer product, 289 | doubtful cases shall be resolved in favor of coverage. For a particular 290 | product received by a particular user, "normally used" refers to a 291 | typical or common use of that class of product, regardless of the status 292 | of the particular user or of the way in which the particular user 293 | actually uses, or expects or is expected to use, the product. A product 294 | is a consumer product regardless of whether the product has substantial 295 | commercial, industrial or non-consumer uses, unless such uses represent 296 | the only significant mode of use of the product. 297 | 298 | "Installation Information" for a User Product means any methods, 299 | procedures, authorization keys, or other information required to install 300 | and execute modified versions of a covered work in that User Product from 301 | a modified version of its Corresponding Source. The information must 302 | suffice to ensure that the continued functioning of the modified object 303 | code is in no case prevented or interfered with solely because 304 | modification has been made. 305 | 306 | If you convey an object code work under this section in, or with, or 307 | specifically for use in, a User Product, and the conveying occurs as 308 | part of a transaction in which the right of possession and use of the 309 | User Product is transferred to the recipient in perpetuity or for a 310 | fixed term (regardless of how the transaction is characterized), the 311 | Corresponding Source conveyed under this section must be accompanied 312 | by the Installation Information. But this requirement does not apply 313 | if neither you nor any third party retains the ability to install 314 | modified object code on the User Product (for example, the work has 315 | been installed in ROM). 316 | 317 | The requirement to provide Installation Information does not include a 318 | requirement to continue to provide support service, warranty, or updates 319 | for a work that has been modified or installed by the recipient, or for 320 | the User Product in which it has been modified or installed. Access to a 321 | network may be denied when the modification itself materially and 322 | adversely affects the operation of the network or violates the rules and 323 | protocols for communication across the network. 324 | 325 | Corresponding Source conveyed, and Installation Information provided, 326 | in accord with this section must be in a format that is publicly 327 | documented (and with an implementation available to the public in 328 | source code form), and must require no special password or key for 329 | unpacking, reading or copying. 330 | 331 | 7. Additional Terms. 332 | 333 | "Additional permissions" are terms that supplement the terms of this 334 | License by making exceptions from one or more of its conditions. 335 | Additional permissions that are applicable to the entire Program shall 336 | be treated as though they were included in this License, to the extent 337 | that they are valid under applicable law. If additional permissions 338 | apply only to part of the Program, that part may be used separately 339 | under those permissions, but the entire Program remains governed by 340 | this License without regard to the additional permissions. 341 | 342 | When you convey a copy of a covered work, you may at your option 343 | remove any additional permissions from that copy, or from any part of 344 | it. (Additional permissions may be written to require their own 345 | removal in certain cases when you modify the work.) You may place 346 | additional permissions on material, added by you to a covered work, 347 | for which you have or can give appropriate copyright permission. 348 | 349 | Notwithstanding any other provision of this License, for material you 350 | add to a covered work, you may (if authorized by the copyright holders of 351 | that material) supplement the terms of this License with terms: 352 | 353 | a) Disclaiming warranty or limiting liability differently from the 354 | terms of sections 15 and 16 of this License; or 355 | 356 | b) Requiring preservation of specified reasonable legal notices or 357 | author attributions in that material or in the Appropriate Legal 358 | Notices displayed by works containing it; or 359 | 360 | c) Prohibiting misrepresentation of the origin of that material, or 361 | requiring that modified versions of such material be marked in 362 | reasonable ways as different from the original version; or 363 | 364 | d) Limiting the use for publicity purposes of names of licensors or 365 | authors of the material; or 366 | 367 | e) Declining to grant rights under trademark law for use of some 368 | trade names, trademarks, or service marks; or 369 | 370 | f) Requiring indemnification of licensors and authors of that 371 | material by anyone who conveys the material (or modified versions of 372 | it) with contractual assumptions of liability to the recipient, for 373 | any liability that these contractual assumptions directly impose on 374 | those licensors and authors. 375 | 376 | All other non-permissive additional terms are considered "further 377 | restrictions" within the meaning of section 10. If the Program as you 378 | received it, or any part of it, contains a notice stating that it is 379 | governed by this License along with a term that is a further 380 | restriction, you may remove that term. If a license document contains 381 | a further restriction but permits relicensing or conveying under this 382 | License, you may add to a covered work material governed by the terms 383 | of that license document, provided that the further restriction does 384 | not survive such relicensing or conveying. 385 | 386 | If you add terms to a covered work in accord with this section, you 387 | must place, in the relevant source files, a statement of the 388 | additional terms that apply to those files, or a notice indicating 389 | where to find the applicable terms. 390 | 391 | Additional terms, permissive or non-permissive, may be stated in the 392 | form of a separately written license, or stated as exceptions; 393 | the above requirements apply either way. 394 | 395 | 8. Termination. 396 | 397 | You may not propagate or modify a covered work except as expressly 398 | provided under this License. Any attempt otherwise to propagate or 399 | modify it is void, and will automatically terminate your rights under 400 | this License (including any patent licenses granted under the third 401 | paragraph of section 11). 402 | 403 | However, if you cease all violation of this License, then your 404 | license from a particular copyright holder is reinstated (a) 405 | provisionally, unless and until the copyright holder explicitly and 406 | finally terminates your license, and (b) permanently, if the copyright 407 | holder fails to notify you of the violation by some reasonable means 408 | prior to 60 days after the cessation. 409 | 410 | Moreover, your license from a particular copyright holder is 411 | reinstated permanently if the copyright holder notifies you of the 412 | violation by some reasonable means, this is the first time you have 413 | received notice of violation of this License (for any work) from that 414 | copyright holder, and you cure the violation prior to 30 days after 415 | your receipt of the notice. 416 | 417 | Termination of your rights under this section does not terminate the 418 | licenses of parties who have received copies or rights from you under 419 | this License. If your rights have been terminated and not permanently 420 | reinstated, you do not qualify to receive new licenses for the same 421 | material under section 10. 422 | 423 | 9. Acceptance Not Required for Having Copies. 424 | 425 | You are not required to accept this License in order to receive or 426 | run a copy of the Program. Ancillary propagation of a covered work 427 | occurring solely as a consequence of using peer-to-peer transmission 428 | to receive a copy likewise does not require acceptance. However, 429 | nothing other than this License grants you permission to propagate or 430 | modify any covered work. These actions infringe copyright if you do 431 | not accept this License. Therefore, by modifying or propagating a 432 | covered work, you indicate your acceptance of this License to do so. 433 | 434 | 10. Automatic Licensing of Downstream Recipients. 435 | 436 | Each time you convey a covered work, the recipient automatically 437 | receives a license from the original licensors, to run, modify and 438 | propagate that work, subject to this License. You are not responsible 439 | for enforcing compliance by third parties with this License. 440 | 441 | An "entity transaction" is a transaction transferring control of an 442 | organization, or substantially all assets of one, or subdividing an 443 | organization, or merging organizations. If propagation of a covered 444 | work results from an entity transaction, each party to that 445 | transaction who receives a copy of the work also receives whatever 446 | licenses to the work the party's predecessor in interest had or could 447 | give under the previous paragraph, plus a right to possession of the 448 | Corresponding Source of the work from the predecessor in interest, if 449 | the predecessor has it or can get it with reasonable efforts. 450 | 451 | You may not impose any further restrictions on the exercise of the 452 | rights granted or affirmed under this License. For example, you may 453 | not impose a license fee, royalty, or other charge for exercise of 454 | rights granted under this License, and you may not initiate litigation 455 | (including a cross-claim or counterclaim in a lawsuit) alleging that 456 | any patent claim is infringed by making, using, selling, offering for 457 | sale, or importing the Program or any portion of it. 458 | 459 | 11. Patents. 460 | 461 | A "contributor" is a copyright holder who authorizes use under this 462 | License of the Program or a work on which the Program is based. The 463 | work thus licensed is called the contributor's "contributor version". 464 | 465 | A contributor's "essential patent claims" are all patent claims 466 | owned or controlled by the contributor, whether already acquired or 467 | hereafter acquired, that would be infringed by some manner, permitted 468 | by this License, of making, using, or selling its contributor version, 469 | but do not include claims that would be infringed only as a 470 | consequence of further modification of the contributor version. For 471 | purposes of this definition, "control" includes the right to grant 472 | patent sublicenses in a manner consistent with the requirements of 473 | this License. 474 | 475 | Each contributor grants you a non-exclusive, worldwide, royalty-free 476 | patent license under the contributor's essential patent claims, to 477 | make, use, sell, offer for sale, import and otherwise run, modify and 478 | propagate the contents of its contributor version. 479 | 480 | In the following three paragraphs, a "patent license" is any express 481 | agreement or commitment, however denominated, not to enforce a patent 482 | (such as an express permission to practice a patent or covenant not to 483 | sue for patent infringement). To "grant" such a patent license to a 484 | party means to make such an agreement or commitment not to enforce a 485 | patent against the party. 486 | 487 | If you convey a covered work, knowingly relying on a patent license, 488 | and the Corresponding Source of the work is not available for anyone 489 | to copy, free of charge and under the terms of this License, through a 490 | publicly available network server or other readily accessible means, 491 | then you must either (1) cause the Corresponding Source to be so 492 | available, or (2) arrange to deprive yourself of the benefit of the 493 | patent license for this particular work, or (3) arrange, in a manner 494 | consistent with the requirements of this License, to extend the patent 495 | license to downstream recipients. "Knowingly relying" means you have 496 | actual knowledge that, but for the patent license, your conveying the 497 | covered work in a country, or your recipient's use of the covered work 498 | in a country, would infringe one or more identifiable patents in that 499 | country that you have reason to believe are valid. 500 | 501 | If, pursuant to or in connection with a single transaction or 502 | arrangement, you convey, or propagate by procuring conveyance of, a 503 | covered work, and grant a patent license to some of the parties 504 | receiving the covered work authorizing them to use, propagate, modify 505 | or convey a specific copy of the covered work, then the patent license 506 | you grant is automatically extended to all recipients of the covered 507 | work and works based on it. 508 | 509 | A patent license is "discriminatory" if it does not include within 510 | the scope of its coverage, prohibits the exercise of, or is 511 | conditioned on the non-exercise of one or more of the rights that are 512 | specifically granted under this License. You may not convey a covered 513 | work if you are a party to an arrangement with a third party that is 514 | in the business of distributing software, under which you make payment 515 | to the third party based on the extent of your activity of conveying 516 | the work, and under which the third party grants, to any of the 517 | parties who would receive the covered work from you, a discriminatory 518 | patent license (a) in connection with copies of the covered work 519 | conveyed by you (or copies made from those copies), or (b) primarily 520 | for and in connection with specific products or compilations that 521 | contain the covered work, unless you entered into that arrangement, 522 | or that patent license was granted, prior to 28 March 2007. 523 | 524 | Nothing in this License shall be construed as excluding or limiting 525 | any implied license or other defenses to infringement that may 526 | otherwise be available to you under applicable patent law. 527 | 528 | 12. No Surrender of Others' Freedom. 529 | 530 | If conditions are imposed on you (whether by court order, agreement or 531 | otherwise) that contradict the conditions of this License, they do not 532 | excuse you from the conditions of this License. If you cannot convey a 533 | covered work so as to satisfy simultaneously your obligations under this 534 | License and any other pertinent obligations, then as a consequence you may 535 | not convey it at all. For example, if you agree to terms that obligate you 536 | to collect a royalty for further conveying from those to whom you convey 537 | the Program, the only way you could satisfy both those terms and this 538 | License would be to refrain entirely from conveying the Program. 539 | 540 | 13. Remote Network Interaction; Use with the GNU General Public License. 541 | 542 | Notwithstanding any other provision of this License, if you modify the 543 | Program, your modified version must prominently offer all users 544 | interacting with it remotely through a computer network (if your version 545 | supports such interaction) an opportunity to receive the Corresponding 546 | Source of your version by providing access to the Corresponding Source 547 | from a network server at no charge, through some standard or customary 548 | means of facilitating copying of software. This Corresponding Source 549 | shall include the Corresponding Source for any work covered by version 3 550 | of the GNU General Public License that is incorporated pursuant to the 551 | following paragraph. 552 | 553 | Notwithstanding any other provision of this License, you have 554 | permission to link or combine any covered work with a work licensed 555 | under version 3 of the GNU General Public License into a single 556 | combined work, and to convey the resulting work. The terms of this 557 | License will continue to apply to the part which is the covered work, 558 | but the work with which it is combined will remain governed by version 559 | 3 of the GNU General Public License. 560 | 561 | 14. Revised Versions of this License. 562 | 563 | The Free Software Foundation may publish revised and/or new versions of 564 | the GNU Affero General Public License from time to time. Such new versions 565 | will be similar in spirit to the present version, but may differ in detail to 566 | address new problems or concerns. 567 | 568 | Each version is given a distinguishing version number. If the 569 | Program specifies that a certain numbered version of the GNU Affero General 570 | Public License "or any later version" applies to it, you have the 571 | option of following the terms and conditions either of that numbered 572 | version or of any later version published by the Free Software 573 | Foundation. If the Program does not specify a version number of the 574 | GNU Affero General Public License, you may choose any version ever published 575 | by the Free Software Foundation. 576 | 577 | If the Program specifies that a proxy can decide which future 578 | versions of the GNU Affero General Public License can be used, that proxy's 579 | public statement of acceptance of a version permanently authorizes you 580 | to choose that version for the Program. 581 | 582 | Later license versions may give you additional or different 583 | permissions. However, no additional obligations are imposed on any 584 | author or copyright holder as a result of your choosing to follow a 585 | later version. 586 | 587 | 15. Disclaimer of Warranty. 588 | 589 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 590 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 591 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 592 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 593 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 594 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 595 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 596 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 597 | 598 | 16. Limitation of Liability. 599 | 600 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 601 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 602 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 603 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 604 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 605 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 606 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 607 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 608 | SUCH DAMAGES. 609 | 610 | 17. Interpretation of Sections 15 and 16. 611 | 612 | If the disclaimer of warranty and limitation of liability provided 613 | above cannot be given local legal effect according to their terms, 614 | reviewing courts shall apply local law that most closely approximates 615 | an absolute waiver of all civil liability in connection with the 616 | Program, unless a warranty or assumption of liability accompanies a 617 | copy of the Program in return for a fee. 618 | 619 | END OF TERMS AND CONDITIONS 620 | 621 | How to Apply These Terms to Your New Programs 622 | 623 | If you develop a new program, and you want it to be of the greatest 624 | possible use to the public, the best way to achieve this is to make it 625 | free software which everyone can redistribute and change under these terms. 626 | 627 | To do so, attach the following notices to the program. It is safest 628 | to attach them to the start of each source file to most effectively 629 | state the exclusion of warranty; and each file should have at least 630 | the "copyright" line and a pointer to where the full notice is found. 631 | 632 | 633 | Copyright (C) 634 | 635 | This program is free software: you can redistribute it and/or modify 636 | it under the terms of the GNU Affero General Public License as published by 637 | the Free Software Foundation, either version 3 of the License, or 638 | (at your option) any later version. 639 | 640 | This program is distributed in the hope that it will be useful, 641 | but WITHOUT ANY WARRANTY; without even the implied warranty of 642 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 643 | GNU Affero General Public License for more details. 644 | 645 | You should have received a copy of the GNU Affero General Public License 646 | along with this program. If not, see . 647 | 648 | Also add information on how to contact you by electronic and paper mail. 649 | 650 | If your software can interact with users remotely through a computer 651 | network, you should also make sure that it provides a way for users to 652 | get its source. For example, if your program is a web application, its 653 | interface could display a "Source" link that leads users to an archive 654 | of the code. There are many ways you could offer source, and different 655 | solutions will be better for different programs; see section 13 for the 656 | specific requirements. 657 | 658 | You should also get your employer (if you work as a programmer) or school, 659 | if any, to sign a "copyright disclaimer" for the program, if necessary. 660 | For more information on this, and how to apply and follow the GNU AGPL, see 661 | . 662 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # HTTP server (REST & JSON) on [Spark core](https://www.spark.io/) 2 | ![alt text](https://s3.amazonaws.com/spark-website/spark.png "") 3 | ## EN : Local HTTP server (REST & JSON) for Sparkcore 4 | 5 | This project implement a Http server on Sparkcore itself. Once compiled into the [Web IDE](https://www.spark.io/build), the server will be available at http://IP-SPARKCORE (port 80 by default). 6 | 7 | ### Common files of projects : 8 | - slre.h 9 | - slre.cpp 10 | - HttpResponse.h 11 | - HttpResponse.cpp 12 | - HttpRequest.h 13 | - HttpRequest.cpp 14 | - http_parser.h 15 | - http_parser.cpp 16 | - SparkTime.h 17 | - SparkTime.cpp 18 | 19 | Thanks to : 20 | - [SLRE: Super Light Regular Expression library](https://github.com/cesanta/slre) 21 | - [Marcussacapuces91 Github / SparkCore-RESTserver ](https://github.com/Marcussacapuces91/SparkCore-RESTserver) 22 | - [Joyent Github/ http-parser](https://github.com/joyent/http-parser) 23 | 24 | ### Projet "spark-Web-Tinker.ino" 25 | 26 | - RESTFull URL : http://IP-Sparkcore/D0/on|off 27 | - Digital JSON URL : http://IP-Sparkcore/json/dig/ 28 | - Return : {"TS":20140802091229,"D0":0,"D1":0,"D2":0,"D3":0,"D4":0,"D5":0,"D6":0,"D7":1} 29 | - Analog JSON URL : http://IP-Sparkcore/json/ana/ 30 | - Return : {"TS":20140802091252,"A0":2127,"A1":2117,"A2":2097,"A3":2098,"A4":2107,"A5":2114,"A6":2083,"A7":2085} 31 | 32 | ### Installation 33 | - [Connect the Spark Core to your wifi](https://www.spark.io/start) 34 | - [Deploy the firmware using the Spark Cloud](https://www.spark.io/build/) 35 | - How add files in project : 36 | 37 | ![Add file](http://domotique-info.fr/wp-content/uploads/2014/04/domotique-info-webideaddlib.gif) 38 | 39 | ## FR : Serveur HTTP REST et JSON pour Spark Core. 40 | 41 | Ce projet permet de mettre en oeuvre un serveur sur le Spark core lui-même. 42 | Un fois compilé dans le [Web IDE](https://www.spark.io/build), le serveur sera accessible à l'adresse http://IP-SPARKCORE (port :80 par défaut). Ce projet est un exemple permettant d'envisager d'autres projet. 43 | 44 | Fichiers communs des projets (Rappel .h = fichier d'entête, .cpp = fichier de définition [Sources]) : 45 | - slre.h 46 | - slre.cpp 47 | - HttpResponse.h 48 | - HttpResponse.cpp 49 | - HttpRequest.h 50 | - HttpRequest.cpp 51 | - http_parser.h 52 | - http_parser.cpp 53 | - SparkTime.h 54 | - SparkTime.cpp 55 | 56 | Remerciements à : 57 | - [SLRE: Super Light Regular Expression library](https://github.com/cesanta/slre) 58 | - [Marcussacapuces91 Github / SparkCore-RESTserver ](https://github.com/Marcussacapuces91/SparkCore-RESTserver) 59 | - [Joyent Github/ http-parser](https://github.com/joyent/http-parser) 60 | 61 | ### Projet "spark-Web-Tinker.ino" 62 | 63 | - RESTFull URL : http://IP-Sparkcore/D0/on|off. 64 | - Digital JSON URL : http://IP-Sparkcore/json/dig/ 65 | - Retour : {"TS":20140802091229,"D0":0,"D1":0,"D2":0,"D3":0,"D4":0,"D5":0,"D6":0,"D7":1} 66 | - Analog JSON URL : http://IP-Sparkcore/json/ana/ 67 | - Retour : {"TS":20140802091252,"A0":2127,"A1":2117,"A2":2097,"A3":2098,"A4":2107,"A5":2114,"A6":2083,"A7":2085} 68 | 69 | ### Installation 70 | - [Connecter le Spark Core à votre Wifi](https://www.spark.io/start) 71 | - [Déployer le firmware depuis le Web IDE Spark Cloud](https://www.spark.io/build/) 72 | - Comment ajouter les fichiers dans le projet : 73 | 74 | ![Add file](http://domotique-info.fr/wp-content/uploads/2014/04/domotique-info-webideaddlib.gif) 75 | 76 | Old GUI version : 77 | 78 | Aller sur Youtube 81 | 82 | Aller sur Youtube 85 | 86 | [Youtube video 1](https://www.youtube.com/watch?v=ifYkC7UdMVA) 87 | [Youtube video 2](http://youtu.be/bbSlwt5ZI3Q) 88 | 89 | -------------------------------------------------------------------------------- /SparkTime.cpp: -------------------------------------------------------------------------------- 1 | /* Spark Time by Brian Ogilvie 2 | Inspired by Arduino Time by Michael Margolis 3 | Copyright (c) 2014 Brian Ogilvie. All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | - Redistributions of source code must retain the above copyright notice, 9 | this list of conditions and the following disclaimer. 10 | - Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 18 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 19 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 20 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 21 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 22 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 23 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 24 | POSSIBILITY OF SUCH DAMAGE. 25 | 26 | TODO: Fractional second handling for NTP and millis(); 27 | Translation for other languages 28 | 29 | */ 30 | 31 | #include "SparkTime.h" 32 | 33 | SparkTime::SparkTime() 34 | { 35 | _UDPClient = NULL; 36 | _timezone = -5; 37 | _useDST = true; 38 | _useEuroDSTRule = false; 39 | _syncedOnce = false; 40 | _interval = 60UL * 60UL; 41 | _localPort = 2390; 42 | } 43 | 44 | void SparkTime::begin(UDP * UDPClient, const char * NTPServer) { 45 | _UDPClient = UDPClient; 46 | memcpy(_serverName, NTPServer, strlen(NTPServer)+1); 47 | } 48 | 49 | void SparkTime::begin(UDP * UDPClient) { 50 | _UDPClient = UDPClient; 51 | const char NTPServer[] = "pool.ntp.org"; 52 | memcpy(_serverName, NTPServer, strlen(NTPServer)+1); 53 | } 54 | 55 | bool SparkTime::hasSynced() { 56 | return _syncedOnce; 57 | } 58 | 59 | void SparkTime::setUseDST(bool value) { 60 | _useDST = value; 61 | } 62 | 63 | void SparkTime::setUseEuroDSTRule(bool value) { 64 | _useEuroDSTRule = value; 65 | } 66 | 67 | uint8_t SparkTime::hour(uint32_t tnow) { 68 | uint8_t hTemp = ((tnow+timeZoneDSTOffset(tnow)) % 86400UL)/3600UL; 69 | return hTemp; 70 | } 71 | 72 | 73 | uint8_t SparkTime::minute(uint32_t tnow) { 74 | return (((tnow+timeZoneDSTOffset(tnow)) % 3600) / 60); 75 | } 76 | 77 | 78 | uint8_t SparkTime::second(uint32_t tnow) { 79 | return ((tnow+timeZoneDSTOffset(tnow)) % 60); 80 | } 81 | 82 | 83 | uint8_t SparkTime::dayOfWeek(uint32_t tnow) { 84 | uint32_t dayNum = (tnow + timeZoneDSTOffset(tnow)-SPARKTIMEEPOCHSTART)/SPARKTIMESECPERDAY; 85 | //Unix epoch day 0 was a thursday 86 | return ((dayNum+4)%7); 87 | } 88 | 89 | 90 | uint8_t SparkTime::day(uint32_t tnow) { 91 | uint32_t dayNum = (tnow+timeZoneDSTOffset(tnow)-SPARKTIMEBASESTART)/SPARKTIMESECPERDAY; 92 | uint32_t tempYear = SPARKTIMEBASEYEAR; 93 | uint8_t tempMonth = 0; 94 | 95 | while(dayNum >= YEARSIZE(tempYear)) { 96 | dayNum -= YEARSIZE(tempYear); 97 | tempYear++; 98 | } 99 | 100 | while(dayNum >= _monthLength[LEAPYEAR(tempYear)][tempMonth]) { 101 | dayNum -= _monthLength[LEAPYEAR(tempYear)][tempMonth]; 102 | tempMonth++; 103 | } 104 | dayNum++; // correct for zero-base 105 | return (uint8_t)dayNum; 106 | } 107 | 108 | uint8_t SparkTime::month(uint32_t tnow) { 109 | uint32_t dayNum = (tnow+timeZoneDSTOffset(tnow)-SPARKTIMEBASESTART)/SPARKTIMESECPERDAY; 110 | uint32_t tempYear = SPARKTIMEBASEYEAR; 111 | uint8_t tempMonth = 0; 112 | 113 | while(dayNum >= YEARSIZE(tempYear)) { 114 | dayNum -= YEARSIZE(tempYear); 115 | tempYear++; 116 | } 117 | 118 | while(dayNum >= _monthLength[LEAPYEAR(tempYear)][tempMonth]) { 119 | dayNum -= _monthLength[LEAPYEAR(tempYear)][tempMonth]; 120 | tempMonth++; 121 | } 122 | tempMonth++; 123 | return tempMonth; 124 | } 125 | 126 | 127 | uint32_t SparkTime::year(uint32_t tnow) { 128 | uint32_t dayNum = (tnow+timeZoneDSTOffset(tnow)-SPARKTIMEBASESTART)/SPARKTIMESECPERDAY; 129 | uint32_t tempYear = SPARKTIMEBASEYEAR; 130 | 131 | while(dayNum >= YEARSIZE(tempYear)) { 132 | dayNum -= YEARSIZE(tempYear); 133 | tempYear++; 134 | } 135 | return tempYear; 136 | } 137 | 138 | String SparkTime::hourString(uint32_t tnow) { 139 | return String(_digits[hour(tnow)]); 140 | } 141 | 142 | String SparkTime::hour12String(uint32_t tnow) { 143 | uint8_t tempHour = hour(tnow); 144 | if (tempHour>12) { 145 | tempHour -= 12; 146 | } 147 | if (tempHour == 0) { 148 | tempHour = 12; 149 | } 150 | return String(_digits[tempHour]); 151 | } 152 | 153 | 154 | String SparkTime::minuteString(uint32_t tnow) { 155 | return String(_digits[minute(tnow)]); 156 | } 157 | 158 | String SparkTime::secondString(uint32_t tnow) { 159 | return String(_digits[second(tnow)]); 160 | } 161 | 162 | String SparkTime::AMPMString(uint32_t tnow) { 163 | uint8_t tempHour = hour(tnow); 164 | if (tempHour<12) { 165 | return String("AM"); 166 | } else { 167 | return String("PM"); 168 | } 169 | } 170 | 171 | String SparkTime::dayOfWeekShortString(uint32_t tnow) { 172 | return String(_days_short[dayOfWeek(tnow)]); 173 | } 174 | 175 | String SparkTime::dayOfWeekString(uint32_t tnow) { 176 | return String(_days[dayOfWeek(tnow)]); 177 | } 178 | 179 | String SparkTime::dayString(uint32_t tnow) { 180 | return String(_digits[day(tnow)]); 181 | } 182 | 183 | String SparkTime::monthString(uint32_t tnow) { 184 | return String(_digits[month(tnow)]); 185 | } 186 | 187 | String SparkTime::monthNameShortString(uint32_t tnow) { 188 | return String(_months_short[month(tnow)-1]); 189 | } 190 | 191 | String SparkTime::monthNameString(uint32_t tnow) { 192 | return String(_months[month(tnow)-1]); 193 | } 194 | 195 | String SparkTime::yearShortString(uint32_t tnow) { 196 | uint32_t tempYear = year(tnow)%100; 197 | if (tempYear<10) { 198 | return String(_digits[tempYear]); 199 | } else { 200 | return String(tempYear); 201 | } 202 | } 203 | 204 | String SparkTime::yearString(uint32_t tnow) { 205 | return String(year(tnow)); 206 | } 207 | 208 | String SparkTime::ISODateString(uint32_t tnow) { 209 | String ISOString; 210 | ISOString += yearString(tnow); 211 | ISOString += "-"; 212 | ISOString += monthString(tnow); 213 | ISOString += "-"; 214 | ISOString += dayString(tnow); 215 | ISOString += "T"; 216 | ISOString += hourString(tnow); 217 | ISOString += ":"; 218 | ISOString += minuteString(tnow); 219 | ISOString += ":"; 220 | ISOString += secondString(tnow); 221 | 222 | int32_t offset = timeZoneDSTOffset(tnow)/3600L; 223 | // Guard against timezone problems 224 | if (offset>-24 && offset<24) { 225 | if (offset < 0) { 226 | ISOString = ISOString + "-" + _digits[-offset] + "00"; 227 | } else { 228 | ISOString = ISOString + "+" + _digits[offset] + "00"; 229 | } 230 | } 231 | return ISOString; 232 | } 233 | 234 | String SparkTime::ISODateUTCString(uint32_t tnow) { 235 | uint32_t savedTimeZone = _timezone; 236 | bool savedUseDST = _useDST; 237 | _timezone = 0; 238 | _useDST = false; 239 | 240 | String ISOString; 241 | ISOString += yearString(tnow); 242 | ISOString += "-"; 243 | ISOString += monthString(tnow); 244 | ISOString += "-"; 245 | ISOString += dayString(tnow); 246 | ISOString += "T"; 247 | ISOString += hourString(tnow); 248 | ISOString += ":"; 249 | ISOString += minuteString(tnow); 250 | ISOString += ":"; 251 | ISOString += secondString(tnow); 252 | ISOString += "Z"; 253 | 254 | _timezone = savedTimeZone; 255 | _useDST = savedUseDST; 256 | 257 | return ISOString; 258 | } 259 | 260 | uint32_t SparkTime::now() { 261 | if (!_syncedOnce && !_isSyncing) { 262 | updateNTPTime(); 263 | } 264 | if (!_syncedOnce) { // fail! 265 | return SPARKTIMEBASEYEAR; // Jan 1, 2014 266 | } 267 | return nowNoUpdate(); 268 | } 269 | 270 | uint32_t SparkTime::nowNoUpdate() { 271 | uint32_t mTime = millis(); 272 | //unsigned long mFracSec = (mTime%1000); // 0 to 999 miliseconds 273 | //unsigned long nowFrac = _lastSyncNTPFrac + mFracSec*2^22/1000; 274 | uint32_t nowSec = _lastSyncNTPTime + ((mTime - _lastSyncMillisTime)/1000); 275 | if (_lastSyncMillisTime>mTime) { // has wrapped 276 | nowSec = nowSec + SPARKTIMEWRAPSECS; 277 | } 278 | if (nowSec >= (_lastSyncNTPTime + _interval)) { 279 | updateNTPTime(); 280 | } 281 | return nowSec; 282 | } 283 | 284 | uint32_t SparkTime::nowEpoch() { 285 | return (now()-SPARKTIMEEPOCHSTART); 286 | } 287 | 288 | uint32_t SparkTime::lastNTPTime() { 289 | return _lastSyncNTPTime; 290 | } 291 | 292 | void SparkTime::setNTPInvterval(uint32_t intervalMinutes) { 293 | const uint32_t fiveMinutes = 5UL * 60UL; 294 | uint32_t interval = intervalMinutes * 60UL; 295 | _interval = max(fiveMinutes, interval); 296 | } 297 | 298 | void SparkTime::setTimeZone(int32_t hoursOffset) { 299 | _timezone = hoursOffset; 300 | } 301 | 302 | bool SparkTime::isUSDST(uint32_t tnow) { 303 | // 2am 2nd Sunday in March to 2am 1st Sunday in November 304 | // can't use offset here 305 | bool result = false; 306 | uint32_t dayNum = (tnow+_timezone*3600UL-SPARKTIMEBASESTART)/SPARKTIMESECPERDAY; 307 | uint32_t tempYear = SPARKTIMEBASEYEAR; 308 | uint8_t tempMonth = 0; 309 | uint8_t tempHour = ((tnow+_timezone*3600UL) % 86400UL)/3600UL; 310 | 311 | while(dayNum >= YEARSIZE(tempYear)) { 312 | dayNum -= YEARSIZE(tempYear); 313 | tempYear++; 314 | } 315 | 316 | while(dayNum >= _monthLength[LEAPYEAR(tempYear)][tempMonth]) { 317 | dayNum -= _monthLength[LEAPYEAR(tempYear)][tempMonth]; 318 | tempMonth++; 319 | } 320 | tempMonth++; 321 | dayNum++; // correct for zero-base 322 | 323 | if (tempMonth>3 && tempMonth<11) { 324 | result = true; 325 | } else if (tempMonth == 3) { 326 | if ((dayNum == _usDSTStart[tempYear-SPARKTIMEBASEYEAR] && tempHour >=2) || 327 | (dayNum > _usDSTStart[tempYear-SPARKTIMEBASEYEAR])) { 328 | result = true; 329 | } 330 | } else if (tempMonth == 11) { 331 | if (!((dayNum == _usDSTEnd[tempYear-SPARKTIMEBASEYEAR] && tempHour >=2) || 332 | (dayNum > _usDSTEnd[tempYear-SPARKTIMEBASEYEAR]))) { 333 | result = true; 334 | } 335 | } 336 | return result; 337 | } 338 | 339 | bool SparkTime::isEuroDST(uint32_t tnow) { 340 | // 1am last Sunday in March to 1am on last Sunday October 341 | // can't use offset here 342 | bool result = false; 343 | uint32_t dayNum = (tnow+_timezone*3600UL-SPARKTIMEBASESTART)/SPARKTIMESECPERDAY; 344 | uint32_t tempYear = SPARKTIMEBASEYEAR; 345 | uint8_t tempMonth = 0; 346 | uint8_t tempHour = ((tnow+_timezone*3600UL) % 86400UL)/3600UL; 347 | 348 | while(dayNum >= YEARSIZE(tempYear)) { 349 | dayNum -= YEARSIZE(tempYear); 350 | tempYear++; 351 | } 352 | 353 | while(dayNum >= _monthLength[LEAPYEAR(tempYear)][tempMonth]) { 354 | dayNum -= _monthLength[LEAPYEAR(tempYear)][tempMonth]; 355 | tempMonth++; 356 | } 357 | tempMonth++; 358 | dayNum++; // correct for zero-base 359 | 360 | if (tempMonth>3 && tempMonth<10) { 361 | result = true; 362 | } else if (tempMonth == 3) { 363 | if ((dayNum == _EuDSTStart[tempYear-SPARKTIMEBASEYEAR] && tempHour >=1) || 364 | (dayNum > _EuDSTStart[tempYear-SPARKTIMEBASEYEAR])) { 365 | result = true; 366 | } 367 | } else if (tempMonth == 10) { 368 | if (!((dayNum == _EuDSTEnd[tempYear-SPARKTIMEBASEYEAR] && tempHour >=1) || 369 | (dayNum > _EuDSTEnd[tempYear-SPARKTIMEBASEYEAR]))) { 370 | result = true; 371 | } 372 | } 373 | return result; 374 | } 375 | 376 | void SparkTime::updateNTPTime() { 377 | //digitalWrite(D7,HIGH); 378 | _isSyncing = true; 379 | _UDPClient->begin(_localPort); 380 | memset(_packetBuffer, 0, SPARKTIMENTPSIZE); 381 | _packetBuffer[0] = 0b11100011; // LI, Version, Mode 382 | _packetBuffer[1] = 0; // Stratum, or type of clock 383 | _packetBuffer[2] = 6; // Polling Interval 384 | _packetBuffer[3] = 0xEC; // Peer Clock Precision 385 | // 4-11 are zero 386 | _packetBuffer[12] = 49; 387 | _packetBuffer[13] = 0x4E; 388 | _packetBuffer[14] = 49; 389 | _packetBuffer[15] = 52; 390 | 391 | _UDPClient->beginPacket(_serverName, 123); //NTP requests are to port 123 392 | _UDPClient->write(_packetBuffer,SPARKTIMENTPSIZE); 393 | _UDPClient->endPacket(); 394 | //gather the local offset close to the send time 395 | uint32_t localmsec = millis(); 396 | int32_t retries = 0; 397 | int32_t bytesrecv = _UDPClient->parsePacket(); 398 | while(bytesrecv == 0 && retries < 1000) { 399 | bytesrecv = _UDPClient->parsePacket(); 400 | retries++; 401 | } 402 | 403 | if (bytesrecv>0) { 404 | _UDPClient->read(_packetBuffer,SPARKTIMENTPSIZE); 405 | // Handle Kiss-of-death 406 | if (_packetBuffer[1]==0) { 407 | _interval = max(_interval * 2, 24UL*60UL*60UL); 408 | } 409 | _lastSyncNTPTime = _packetBuffer[40] << 24 | _packetBuffer[41] << 16 | _packetBuffer[42] << 8 | _packetBuffer[43]; 410 | _lastSyncNTPFrac = _packetBuffer[44] << 24 | _packetBuffer[45] << 16 | _packetBuffer[46] << 8 | _packetBuffer[47]; 411 | _lastSyncMillisTime = localmsec; 412 | _syncedOnce = true; 413 | } 414 | //digitalWrite(D7,LOW); 415 | _UDPClient->stop(); 416 | _isSyncing = false; 417 | } 418 | 419 | int32_t SparkTime::timeZoneDSTOffset(uint32_t tnow) { 420 | int32_t result = _timezone*3600UL; 421 | if ((_useDST && (!_useEuroDSTRule && isUSDST(tnow))) || 422 | (_useDST && (_useEuroDSTRule && isEuroDST(tnow)))) { 423 | result += 3600UL; 424 | } 425 | return result; 426 | } 427 | -------------------------------------------------------------------------------- /SparkTime.h: -------------------------------------------------------------------------------- 1 | #ifndef _SPARKTIME 2 | #define _SPARKTIME 3 | 4 | #include "application.h" 5 | 6 | #define SPARKTIMENTPSIZE 48 7 | #define SPARKTIMEHOSTNAMESIZE 64 8 | #define SPARKTIMEYEARZERO 1900 9 | #define SPARKTIMEEPOCHSTART 2208988800UL 10 | #define SPARKTIMESECPERDAY 86400UL 11 | #define SPARKTIMEBASESTART 3597523200UL 12 | #define SPARKTIMEBASEYEAR 2014UL 13 | #define LEAPYEAR(year) (!((year) % 4) && (((year) % 100) || !((year) % 400))) 14 | #define YEARSIZE(year) (LEAPYEAR(year) ? 366 : 365) 15 | #define SPARKTIMEWRAPSECS 4294967UL 16 | 17 | static const char _digits[60][3] = {"00","01","02","03","04","05","06","07","08","09", 18 | "10","11","12","13","14","15","16","17","18","19", 19 | "20","21","22","23","24","25","26","27","28","29", 20 | "30","31","32","33","34","35","36","37","38","39", 21 | "40","41","42","43","44","45","46","47","48","49", 22 | "50","51","52","53","54","55","56","57","58","59"}; /* for speed! */ 23 | static const char _days[7][10] = {"Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"}; 24 | static const char _days_short[7][4] = {"Sun","Mon","Tue","Wed","Thu","Fri","Sat"}; 25 | static const char _months[12][10] = {"January","February","March","April","May","June","July","August","September","October","November","December"}; 26 | static const char _months_short[12][4] = {"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"}; 27 | static const uint8_t _monthLength[2][12] = { 28 | {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, 29 | {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}}; // Leap year 30 | // for years 2014-2036 Day in March or November 31 | // 2014 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 32 | static const uint8_t _usDSTStart[23] = { 9, 8,13,12,11,10, 8,14,13,12,10, 9, 8,14,12,11,10, 9,14,13,12,11, 9}; 33 | static const uint8_t _usDSTEnd[23] = { 2, 1, 6, 5, 4, 3, 1, 7, 6, 5, 3, 2, 1, 7, 5, 4, 3, 2, 7, 6, 5, 4, 2}; 34 | static const uint8_t _EuDSTStart[23] = {30,29,27,26,25,31,29,28,27,26,31,30,29,28,26,25,31,30,28,27,26,25,30}; 35 | static const uint8_t _EuDSTEnd[23] = {26,25,30,29,28,27,25,31,30,29,27,26,25,31,29,28,27,26,31,30,29,28,26}; 36 | 37 | class SparkTime { 38 | public: 39 | SparkTime(); 40 | void begin(UDP * UPDClient); 41 | void begin(UDP * UPDClient, const char * NTPServer); 42 | bool hasSynced(); 43 | void setNTPInvterval(uint32_t intervalMinutes); 44 | void setTimeZone(int32_t hoursOffset); 45 | void setUseDST(bool value); 46 | void setUseEuroDSTRule(bool value); 47 | 48 | uint32_t now(); 49 | uint32_t nowNoUpdate(); 50 | uint32_t nowEpoch(); 51 | uint32_t lastNTPTime(); 52 | 53 | uint8_t hour(uint32_t tnow); 54 | uint8_t minute(uint32_t tnow); 55 | uint8_t second(uint32_t tnow); 56 | uint8_t dayOfWeek(uint32_t tnow); 57 | uint8_t day(uint32_t tnow); 58 | uint8_t month(uint32_t tnow); 59 | uint32_t year(uint32_t tnow); 60 | 61 | bool isUSDST(uint32_t tnow); //2nd Sun in Mar to 1st Sun in Nov 62 | bool isEuroDST(uint32_t tnow); //Last Sun in Mar to last Sun in Oct 63 | 64 | String hourString(uint32_t tnow); 65 | String hour12String(uint32_t tnow); 66 | String minuteString(uint32_t tnow); 67 | String secondString(uint32_t tnow); 68 | String AMPMString(uint32_t tnow); 69 | String dayOfWeekShortString(uint32_t tnow); 70 | String dayOfWeekString(uint32_t tnow); 71 | String dayString(uint32_t tnow); 72 | String monthString(uint32_t tnow); 73 | String monthNameShortString(uint32_t tnow); 74 | String monthNameString(uint32_t tnow); 75 | String yearShortString(uint32_t tnow); 76 | String yearString(uint32_t tnow); 77 | String ISODateString(uint32_t tnow); 78 | String ISODateUTCString(uint32_t tnow); 79 | 80 | private: 81 | UDP * _UDPClient; 82 | char _serverName[SPARKTIMEHOSTNAMESIZE]; 83 | bool _syncedOnce = false; 84 | bool _isSyncing = false; 85 | uint32_t _lastSyncMillisTime; 86 | uint32_t _lastSyncNTPTime; 87 | uint32_t _lastSyncNTPFrac; 88 | uint32_t _interval; 89 | int32_t _timezone; 90 | bool _useDST; 91 | bool _useEuroDSTRule; 92 | uint32_t _localPort; 93 | uint8_t _packetBuffer[SPARKTIMENTPSIZE]; 94 | void updateNTPTime(); 95 | int32_t timeZoneDSTOffset(uint32_t tnow); 96 | }; 97 | 98 | #endif 99 | -------------------------------------------------------------------------------- /http_parser.cpp: -------------------------------------------------------------------------------- 1 | /* Based on src/http/ngx_http_parse.c from NGINX copyright Igor Sysoev 2 | * 3 | * Additional changes are licensed under the same terms as NGINX and 4 | * copyright Joyent, Inc. and other Node contributors. All rights reserved. 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to 8 | * deal in the Software without restriction, including without limitation the 9 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 | * sell copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 22 | * IN THE SOFTWARE. 23 | */ 24 | #include "http_parser.h" 25 | #if defined (__arm__) 26 | #define NDEBUG 27 | #endif 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | 35 | #ifndef ULLONG_MAX 36 | # define ULLONG_MAX ((uint64_t) -1) /* 2^64-1 */ 37 | #endif 38 | 39 | #ifndef MIN 40 | # define MIN(a,b) ((a) < (b) ? (a) : (b)) 41 | #endif 42 | 43 | #ifndef ARRAY_SIZE 44 | # define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) 45 | #endif 46 | 47 | #ifndef BIT_AT 48 | # define BIT_AT(a, i) \ 49 | (!!((unsigned int) (a)[(unsigned int) (i) >> 3] & \ 50 | (1 << ((unsigned int) (i) & 7)))) 51 | #endif 52 | 53 | #ifndef ELEM_AT 54 | # define ELEM_AT(a, i, v) ((unsigned int) (i) < ARRAY_SIZE(a) ? (a)[(i)] : (v)) 55 | #endif 56 | 57 | #define SET_ERRNO(e) \ 58 | do { \ 59 | parser->http_errno = (e); \ 60 | } while(0) 61 | 62 | 63 | /* Run the notify callback FOR, returning ER if it fails */ 64 | #define CALLBACK_NOTIFY_(FOR, ER) \ 65 | do { \ 66 | assert(HTTP_PARSER_ERRNO(parser) == HPE_OK); \ 67 | \ 68 | if (settings->on_##FOR) { \ 69 | if (0 != settings->on_##FOR(parser)) { \ 70 | SET_ERRNO(HPE_CB_##FOR); \ 71 | } \ 72 | \ 73 | /* We either errored above or got paused; get out */ \ 74 | if (HTTP_PARSER_ERRNO(parser) != HPE_OK) { \ 75 | return (ER); \ 76 | } \ 77 | } \ 78 | } while (0) 79 | 80 | /* Run the notify callback FOR and consume the current byte */ 81 | #define CALLBACK_NOTIFY(FOR) CALLBACK_NOTIFY_(FOR, p - data + 1) 82 | 83 | /* Run the notify callback FOR and don't consume the current byte */ 84 | #define CALLBACK_NOTIFY_NOADVANCE(FOR) CALLBACK_NOTIFY_(FOR, p - data) 85 | 86 | /* Run data callback FOR with LEN bytes, returning ER if it fails */ 87 | #define CALLBACK_DATA_(FOR, LEN, ER) \ 88 | do { \ 89 | assert(HTTP_PARSER_ERRNO(parser) == HPE_OK); \ 90 | \ 91 | if (FOR##_mark) { \ 92 | if (settings->on_##FOR) { \ 93 | if (0 != settings->on_##FOR(parser, FOR##_mark, (LEN))) { \ 94 | SET_ERRNO(HPE_CB_##FOR); \ 95 | } \ 96 | \ 97 | /* We either errored above or got paused; get out */ \ 98 | if (HTTP_PARSER_ERRNO(parser) != HPE_OK) { \ 99 | return (ER); \ 100 | } \ 101 | } \ 102 | FOR##_mark = NULL; \ 103 | } \ 104 | } while (0) 105 | 106 | /* Run the data callback FOR and consume the current byte */ 107 | #define CALLBACK_DATA(FOR) \ 108 | CALLBACK_DATA_(FOR, p - FOR##_mark, p - data + 1) 109 | 110 | /* Run the data callback FOR and don't consume the current byte */ 111 | #define CALLBACK_DATA_NOADVANCE(FOR) \ 112 | CALLBACK_DATA_(FOR, p - FOR##_mark, p - data) 113 | 114 | /* Set the mark FOR; non-destructive if mark is already set */ 115 | #define MARK(FOR) \ 116 | do { \ 117 | if (!FOR##_mark) { \ 118 | FOR##_mark = p; \ 119 | } \ 120 | } while (0) 121 | 122 | 123 | #define PROXY_CONNECTION "proxy-connection" 124 | #define CONNECTION "connection" 125 | #define CONTENT_LENGTH "content-length" 126 | #define TRANSFER_ENCODING "transfer-encoding" 127 | #define UPGRADE "upgrade" 128 | #define CHUNKED "chunked" 129 | #define KEEP_ALIVE "keep-alive" 130 | #define CLOSE "close" 131 | 132 | 133 | static const char *method_strings[] = 134 | { 135 | #define XX(num, name, string) #string, 136 | HTTP_METHOD_MAP(XX) 137 | #undef XX 138 | }; 139 | 140 | 141 | /* Tokens as defined by rfc 2616. Also lowercases them. 142 | * token = 1* 143 | * separators = "(" | ")" | "<" | ">" | "@" 144 | * | "," | ";" | ":" | "\" | <"> 145 | * | "/" | "[" | "]" | "?" | "=" 146 | * | "{" | "}" | SP | HT 147 | */ 148 | static const char tokens[256] = { 149 | /* 0 nul 1 soh 2 stx 3 etx 4 eot 5 enq 6 ack 7 bel */ 150 | 0, 0, 0, 0, 0, 0, 0, 0, 151 | /* 8 bs 9 ht 10 nl 11 vt 12 np 13 cr 14 so 15 si */ 152 | 0, 0, 0, 0, 0, 0, 0, 0, 153 | /* 16 dle 17 dc1 18 dc2 19 dc3 20 dc4 21 nak 22 syn 23 etb */ 154 | 0, 0, 0, 0, 0, 0, 0, 0, 155 | /* 24 can 25 em 26 sub 27 esc 28 fs 29 gs 30 rs 31 us */ 156 | 0, 0, 0, 0, 0, 0, 0, 0, 157 | /* 32 sp 33 ! 34 " 35 # 36 $ 37 % 38 & 39 ' */ 158 | 0, '!', 0, '#', '$', '%', '&', '\'', 159 | /* 40 ( 41 ) 42 * 43 + 44 , 45 - 46 . 47 / */ 160 | 0, 0, '*', '+', 0, '-', '.', 0, 161 | /* 48 0 49 1 50 2 51 3 52 4 53 5 54 6 55 7 */ 162 | '0', '1', '2', '3', '4', '5', '6', '7', 163 | /* 56 8 57 9 58 : 59 ; 60 < 61 = 62 > 63 ? */ 164 | '8', '9', 0, 0, 0, 0, 0, 0, 165 | /* 64 @ 65 A 66 B 67 C 68 D 69 E 70 F 71 G */ 166 | 0, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 167 | /* 72 H 73 I 74 J 75 K 76 L 77 M 78 N 79 O */ 168 | 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 169 | /* 80 P 81 Q 82 R 83 S 84 T 85 U 86 V 87 W */ 170 | 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 171 | /* 88 X 89 Y 90 Z 91 [ 92 \ 93 ] 94 ^ 95 _ */ 172 | 'x', 'y', 'z', 0, 0, 0, '^', '_', 173 | /* 96 ` 97 a 98 b 99 c 100 d 101 e 102 f 103 g */ 174 | '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 175 | /* 104 h 105 i 106 j 107 k 108 l 109 m 110 n 111 o */ 176 | 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 177 | /* 112 p 113 q 114 r 115 s 116 t 117 u 118 v 119 w */ 178 | 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 179 | /* 120 x 121 y 122 z 123 { 124 | 125 } 126 ~ 127 del */ 180 | 'x', 'y', 'z', 0, '|', 0, '~', 0 }; 181 | 182 | 183 | static const int8_t unhex[256] = 184 | {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 185 | ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 186 | ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 187 | , 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1 188 | ,-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1 189 | ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 190 | ,-1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1 191 | ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 192 | }; 193 | 194 | 195 | #if HTTP_PARSER_STRICT 196 | # define T(v) 0 197 | #else 198 | # define T(v) v 199 | #endif 200 | 201 | 202 | static const uint8_t normal_url_char[32] = { 203 | /* 0 nul 1 soh 2 stx 3 etx 4 eot 5 enq 6 ack 7 bel */ 204 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0, 205 | /* 8 bs 9 ht 10 nl 11 vt 12 np 13 cr 14 so 15 si */ 206 | 0 | T(2) | 0 | 0 | T(16) | 0 | 0 | 0, 207 | /* 16 dle 17 dc1 18 dc2 19 dc3 20 dc4 21 nak 22 syn 23 etb */ 208 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0, 209 | /* 24 can 25 em 26 sub 27 esc 28 fs 29 gs 30 rs 31 us */ 210 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0, 211 | /* 32 sp 33 ! 34 " 35 # 36 $ 37 % 38 & 39 ' */ 212 | 0 | 2 | 4 | 0 | 16 | 32 | 64 | 128, 213 | /* 40 ( 41 ) 42 * 43 + 44 , 45 - 46 . 47 / */ 214 | 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, 215 | /* 48 0 49 1 50 2 51 3 52 4 53 5 54 6 55 7 */ 216 | 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, 217 | /* 56 8 57 9 58 : 59 ; 60 < 61 = 62 > 63 ? */ 218 | 1 | 2 | 4 | 8 | 16 | 32 | 64 | 0, 219 | /* 64 @ 65 A 66 B 67 C 68 D 69 E 70 F 71 G */ 220 | 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, 221 | /* 72 H 73 I 74 J 75 K 76 L 77 M 78 N 79 O */ 222 | 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, 223 | /* 80 P 81 Q 82 R 83 S 84 T 85 U 86 V 87 W */ 224 | 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, 225 | /* 88 X 89 Y 90 Z 91 [ 92 \ 93 ] 94 ^ 95 _ */ 226 | 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, 227 | /* 96 ` 97 a 98 b 99 c 100 d 101 e 102 f 103 g */ 228 | 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, 229 | /* 104 h 105 i 106 j 107 k 108 l 109 m 110 n 111 o */ 230 | 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, 231 | /* 112 p 113 q 114 r 115 s 116 t 117 u 118 v 119 w */ 232 | 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128, 233 | /* 120 x 121 y 122 z 123 { 124 | 125 } 126 ~ 127 del */ 234 | 1 | 2 | 4 | 8 | 16 | 32 | 64 | 0, }; 235 | 236 | #undef T 237 | 238 | enum state 239 | { s_dead = 1 /* important that this is > 0 */ 240 | 241 | , s_start_req_or_res 242 | , s_res_or_resp_H 243 | , s_start_res 244 | , s_res_H 245 | , s_res_HT 246 | , s_res_HTT 247 | , s_res_HTTP 248 | , s_res_first_http_major 249 | , s_res_http_major 250 | , s_res_first_http_minor 251 | , s_res_http_minor 252 | , s_res_first_status_code 253 | , s_res_status_code 254 | , s_res_status_start 255 | , s_res_status 256 | , s_res_line_almost_done 257 | 258 | , s_start_req 259 | 260 | , s_req_method 261 | , s_req_spaces_before_url 262 | , s_req_schema 263 | , s_req_schema_slash 264 | , s_req_schema_slash_slash 265 | , s_req_server_start 266 | , s_req_server 267 | , s_req_server_with_at 268 | , s_req_path 269 | , s_req_query_string_start 270 | , s_req_query_string 271 | , s_req_fragment_start 272 | , s_req_fragment 273 | , s_req_http_start 274 | , s_req_http_H 275 | , s_req_http_HT 276 | , s_req_http_HTT 277 | , s_req_http_HTTP 278 | , s_req_first_http_major 279 | , s_req_http_major 280 | , s_req_first_http_minor 281 | , s_req_http_minor 282 | , s_req_line_almost_done 283 | 284 | , s_header_field_start 285 | , s_header_field 286 | , s_header_value_start 287 | , s_header_value 288 | , s_header_value_lws 289 | 290 | , s_header_almost_done 291 | 292 | , s_chunk_size_start 293 | , s_chunk_size 294 | , s_chunk_parameters 295 | , s_chunk_size_almost_done 296 | 297 | , s_headers_almost_done 298 | , s_headers_done 299 | 300 | /* Important: 's_headers_done' must be the last 'header' state. All 301 | * states beyond this must be 'body' states. It is used for overflow 302 | * checking. See the PARSING_HEADER() macro. 303 | */ 304 | 305 | , s_chunk_data 306 | , s_chunk_data_almost_done 307 | , s_chunk_data_done 308 | 309 | , s_body_identity 310 | , s_body_identity_eof 311 | 312 | , s_message_done 313 | }; 314 | 315 | 316 | #define PARSING_HEADER(state) (state <= s_headers_done) 317 | 318 | 319 | enum header_states 320 | { h_general = 0 321 | , h_C 322 | , h_CO 323 | , h_CON 324 | 325 | , h_matching_connection 326 | , h_matching_proxy_connection 327 | , h_matching_content_length 328 | , h_matching_transfer_encoding 329 | , h_matching_upgrade 330 | 331 | , h_connection 332 | , h_content_length 333 | , h_transfer_encoding 334 | , h_upgrade 335 | 336 | , h_matching_transfer_encoding_chunked 337 | , h_matching_connection_keep_alive 338 | , h_matching_connection_close 339 | 340 | , h_transfer_encoding_chunked 341 | , h_connection_keep_alive 342 | , h_connection_close 343 | }; 344 | 345 | enum http_host_state 346 | { 347 | s_http_host_dead = 1 348 | , s_http_userinfo_start 349 | , s_http_userinfo 350 | , s_http_host_start 351 | , s_http_host_v6_start 352 | , s_http_host 353 | , s_http_host_v6 354 | , s_http_host_v6_end 355 | , s_http_host_port_start 356 | , s_http_host_port 357 | }; 358 | 359 | /* Macros for character classes; depends on strict-mode */ 360 | #define CR '\r' 361 | #define LF '\n' 362 | #define LOWER(c) (unsigned char)(c | 0x20) 363 | #define IS_ALPHA(c) (LOWER(c) >= 'a' && LOWER(c) <= 'z') 364 | #define IS_NUM(c) ((c) >= '0' && (c) <= '9') 365 | #define IS_ALPHANUM(c) (IS_ALPHA(c) || IS_NUM(c)) 366 | #define IS_HEX(c) (IS_NUM(c) || (LOWER(c) >= 'a' && LOWER(c) <= 'f')) 367 | #define IS_MARK(c) ((c) == '-' || (c) == '_' || (c) == '.' || \ 368 | (c) == '!' || (c) == '~' || (c) == '*' || (c) == '\'' || (c) == '(' || \ 369 | (c) == ')') 370 | #define IS_USERINFO_CHAR(c) (IS_ALPHANUM(c) || IS_MARK(c) || (c) == '%' || \ 371 | (c) == ';' || (c) == ':' || (c) == '&' || (c) == '=' || (c) == '+' || \ 372 | (c) == '$' || (c) == ',') 373 | 374 | #if HTTP_PARSER_STRICT 375 | #define TOKEN(c) (tokens[(unsigned char)c]) 376 | #define IS_URL_CHAR(c) (BIT_AT(normal_url_char, (unsigned char)c)) 377 | #define IS_HOST_CHAR(c) (IS_ALPHANUM(c) || (c) == '.' || (c) == '-') 378 | #else 379 | #define TOKEN(c) ((c == ' ') ? ' ' : tokens[(unsigned char)c]) 380 | #define IS_URL_CHAR(c) \ 381 | (BIT_AT(normal_url_char, (unsigned char)c) || ((c) & 0x80)) 382 | #define IS_HOST_CHAR(c) \ 383 | (IS_ALPHANUM(c) || (c) == '.' || (c) == '-' || (c) == '_') 384 | #endif 385 | 386 | 387 | #define start_state (parser->type == HTTP_REQUEST ? s_start_req : s_start_res) 388 | 389 | 390 | #if HTTP_PARSER_STRICT 391 | # define STRICT_CHECK(cond) \ 392 | do { \ 393 | if (cond) { \ 394 | SET_ERRNO(HPE_STRICT); \ 395 | goto error; \ 396 | } \ 397 | } while (0) 398 | # define NEW_MESSAGE() (http_should_keep_alive(parser) ? start_state : s_dead) 399 | #else 400 | # define STRICT_CHECK(cond) 401 | # define NEW_MESSAGE() start_state 402 | #endif 403 | 404 | 405 | /* Map errno values to strings for human-readable output */ 406 | #define HTTP_STRERROR_GEN(n, s) { "HPE_" #n, s }, 407 | static struct { 408 | const char *name; 409 | const char *description; 410 | } http_strerror_tab[] = { 411 | HTTP_ERRNO_MAP(HTTP_STRERROR_GEN) 412 | }; 413 | #undef HTTP_STRERROR_GEN 414 | 415 | int http_message_needs_eof(const http_parser *parser); 416 | 417 | /* Our URL parser. 418 | * 419 | * This is designed to be shared by http_parser_execute() for URL validation, 420 | * hence it has a state transition + byte-for-byte interface. In addition, it 421 | * is meant to be embedded in http_parser_parse_url(), which does the dirty 422 | * work of turning state transitions URL components for its API. 423 | * 424 | * This function should only be invoked with non-space characters. It is 425 | * assumed that the caller cares about (and can detect) the transition between 426 | * URL and non-URL states by looking for these. 427 | */ 428 | static enum state 429 | parse_url_char(enum state s, const char ch) 430 | { 431 | if (ch == ' ' || ch == '\r' || ch == '\n') { 432 | return s_dead; 433 | } 434 | 435 | #if HTTP_PARSER_STRICT 436 | if (ch == '\t' || ch == '\f') { 437 | return s_dead; 438 | } 439 | #endif 440 | 441 | switch (s) { 442 | case s_req_spaces_before_url: 443 | /* Proxied requests are followed by scheme of an absolute URI (alpha). 444 | * All methods except CONNECT are followed by '/' or '*'. 445 | */ 446 | 447 | if (ch == '/' || ch == '*') { 448 | return s_req_path; 449 | } 450 | 451 | if (IS_ALPHA(ch)) { 452 | return s_req_schema; 453 | } 454 | 455 | break; 456 | 457 | case s_req_schema: 458 | if (IS_ALPHA(ch)) { 459 | return s; 460 | } 461 | 462 | if (ch == ':') { 463 | return s_req_schema_slash; 464 | } 465 | 466 | break; 467 | 468 | case s_req_schema_slash: 469 | if (ch == '/') { 470 | return s_req_schema_slash_slash; 471 | } 472 | 473 | break; 474 | 475 | case s_req_schema_slash_slash: 476 | if (ch == '/') { 477 | return s_req_server_start; 478 | } 479 | 480 | break; 481 | 482 | case s_req_server_with_at: 483 | if (ch == '@') { 484 | return s_dead; 485 | } 486 | 487 | /* FALLTHROUGH */ 488 | case s_req_server_start: 489 | case s_req_server: 490 | if (ch == '/') { 491 | return s_req_path; 492 | } 493 | 494 | if (ch == '?') { 495 | return s_req_query_string_start; 496 | } 497 | 498 | if (ch == '@') { 499 | return s_req_server_with_at; 500 | } 501 | 502 | if (IS_USERINFO_CHAR(ch) || ch == '[' || ch == ']') { 503 | return s_req_server; 504 | } 505 | 506 | break; 507 | 508 | case s_req_path: 509 | if (IS_URL_CHAR(ch)) { 510 | return s; 511 | } 512 | 513 | switch (ch) { 514 | case '?': 515 | return s_req_query_string_start; 516 | 517 | case '#': 518 | return s_req_fragment_start; 519 | } 520 | 521 | break; 522 | 523 | case s_req_query_string_start: 524 | case s_req_query_string: 525 | if (IS_URL_CHAR(ch)) { 526 | return s_req_query_string; 527 | } 528 | 529 | switch (ch) { 530 | case '?': 531 | /* allow extra '?' in query string */ 532 | return s_req_query_string; 533 | 534 | case '#': 535 | return s_req_fragment_start; 536 | } 537 | 538 | break; 539 | 540 | case s_req_fragment_start: 541 | if (IS_URL_CHAR(ch)) { 542 | return s_req_fragment; 543 | } 544 | 545 | switch (ch) { 546 | case '?': 547 | return s_req_fragment; 548 | 549 | case '#': 550 | return s; 551 | } 552 | 553 | break; 554 | 555 | case s_req_fragment: 556 | if (IS_URL_CHAR(ch)) { 557 | return s; 558 | } 559 | 560 | switch (ch) { 561 | case '?': 562 | case '#': 563 | return s; 564 | } 565 | 566 | break; 567 | 568 | default: 569 | break; 570 | } 571 | 572 | /* We should never fall out of the switch above unless there's an error */ 573 | return s_dead; 574 | } 575 | 576 | size_t http_parser_execute (http_parser *parser, 577 | const http_parser_settings *settings, 578 | const char *data, 579 | size_t len) 580 | { 581 | char c, ch; 582 | int8_t unhex_val; 583 | const char *p = data; 584 | const char *header_field_mark = 0; 585 | const char *header_value_mark = 0; 586 | const char *url_mark = 0; 587 | const char *body_mark = 0; 588 | const char *status_mark = 0; 589 | 590 | /* We're in an error state. Don't bother doing anything. */ 591 | if (HTTP_PARSER_ERRNO(parser) != HPE_OK) { 592 | return 0; 593 | } 594 | 595 | if (len == 0) { 596 | switch (parser->state) { 597 | case s_body_identity_eof: 598 | /* Use of CALLBACK_NOTIFY() here would erroneously return 1 byte read if 599 | * we got paused. 600 | */ 601 | CALLBACK_NOTIFY_NOADVANCE(message_complete); 602 | return 0; 603 | 604 | case s_dead: 605 | case s_start_req_or_res: 606 | case s_start_res: 607 | case s_start_req: 608 | return 0; 609 | 610 | default: 611 | SET_ERRNO(HPE_INVALID_EOF_STATE); 612 | return 1; 613 | } 614 | } 615 | 616 | 617 | if (parser->state == s_header_field) 618 | header_field_mark = data; 619 | if (parser->state == s_header_value) 620 | header_value_mark = data; 621 | switch (parser->state) { 622 | case s_req_path: 623 | case s_req_schema: 624 | case s_req_schema_slash: 625 | case s_req_schema_slash_slash: 626 | case s_req_server_start: 627 | case s_req_server: 628 | case s_req_server_with_at: 629 | case s_req_query_string_start: 630 | case s_req_query_string: 631 | case s_req_fragment_start: 632 | case s_req_fragment: 633 | url_mark = data; 634 | break; 635 | case s_res_status: 636 | status_mark = data; 637 | break; 638 | } 639 | 640 | for (p=data; p != data + len; p++) { 641 | ch = *p; 642 | 643 | if (PARSING_HEADER(parser->state)) { 644 | ++parser->nread; 645 | /* Don't allow the total size of the HTTP headers (including the status 646 | * line) to exceed HTTP_MAX_HEADER_SIZE. This check is here to protect 647 | * embedders against denial-of-service attacks where the attacker feeds 648 | * us a never-ending header that the embedder keeps buffering. 649 | * 650 | * This check is arguably the responsibility of embedders but we're doing 651 | * it on the embedder's behalf because most won't bother and this way we 652 | * make the web a little safer. HTTP_MAX_HEADER_SIZE is still far bigger 653 | * than any reasonable request or response so this should never affect 654 | * day-to-day operation. 655 | */ 656 | if (parser->nread > HTTP_MAX_HEADER_SIZE) { 657 | SET_ERRNO(HPE_HEADER_OVERFLOW); 658 | goto error; 659 | } 660 | } 661 | 662 | reexecute_byte: 663 | switch (parser->state) { 664 | 665 | case s_dead: 666 | /* this state is used after a 'Connection: close' message 667 | * the parser will error out if it reads another message 668 | */ 669 | if (ch == CR || ch == LF) 670 | break; 671 | 672 | SET_ERRNO(HPE_CLOSED_CONNECTION); 673 | goto error; 674 | 675 | case s_start_req_or_res: 676 | { 677 | if (ch == CR || ch == LF) 678 | break; 679 | parser->flags = 0; 680 | parser->content_length = ULLONG_MAX; 681 | 682 | if (ch == 'H') { 683 | parser->state = s_res_or_resp_H; 684 | 685 | CALLBACK_NOTIFY(message_begin); 686 | } else { 687 | parser->type = HTTP_REQUEST; 688 | parser->state = s_start_req; 689 | goto reexecute_byte; 690 | } 691 | 692 | break; 693 | } 694 | 695 | case s_res_or_resp_H: 696 | if (ch == 'T') { 697 | parser->type = HTTP_RESPONSE; 698 | parser->state = s_res_HT; 699 | } else { 700 | if (ch != 'E') { 701 | SET_ERRNO(HPE_INVALID_CONSTANT); 702 | goto error; 703 | } 704 | 705 | parser->type = HTTP_REQUEST; 706 | parser->method = HTTP_HEAD; 707 | parser->index = 2; 708 | parser->state = s_req_method; 709 | } 710 | break; 711 | 712 | case s_start_res: 713 | { 714 | parser->flags = 0; 715 | parser->content_length = ULLONG_MAX; 716 | 717 | switch (ch) { 718 | case 'H': 719 | parser->state = s_res_H; 720 | break; 721 | 722 | case CR: 723 | case LF: 724 | break; 725 | 726 | default: 727 | SET_ERRNO(HPE_INVALID_CONSTANT); 728 | goto error; 729 | } 730 | 731 | CALLBACK_NOTIFY(message_begin); 732 | break; 733 | } 734 | 735 | case s_res_H: 736 | STRICT_CHECK(ch != 'T'); 737 | parser->state = s_res_HT; 738 | break; 739 | 740 | case s_res_HT: 741 | STRICT_CHECK(ch != 'T'); 742 | parser->state = s_res_HTT; 743 | break; 744 | 745 | case s_res_HTT: 746 | STRICT_CHECK(ch != 'P'); 747 | parser->state = s_res_HTTP; 748 | break; 749 | 750 | case s_res_HTTP: 751 | STRICT_CHECK(ch != '/'); 752 | parser->state = s_res_first_http_major; 753 | break; 754 | 755 | case s_res_first_http_major: 756 | if (ch < '0' || ch > '9') { 757 | SET_ERRNO(HPE_INVALID_VERSION); 758 | goto error; 759 | } 760 | 761 | parser->http_major = ch - '0'; 762 | parser->state = s_res_http_major; 763 | break; 764 | 765 | /* major HTTP version or dot */ 766 | case s_res_http_major: 767 | { 768 | if (ch == '.') { 769 | parser->state = s_res_first_http_minor; 770 | break; 771 | } 772 | 773 | if (!IS_NUM(ch)) { 774 | SET_ERRNO(HPE_INVALID_VERSION); 775 | goto error; 776 | } 777 | 778 | parser->http_major *= 10; 779 | parser->http_major += ch - '0'; 780 | 781 | if (parser->http_major > 999) { 782 | SET_ERRNO(HPE_INVALID_VERSION); 783 | goto error; 784 | } 785 | 786 | break; 787 | } 788 | 789 | /* first digit of minor HTTP version */ 790 | case s_res_first_http_minor: 791 | if (!IS_NUM(ch)) { 792 | SET_ERRNO(HPE_INVALID_VERSION); 793 | goto error; 794 | } 795 | 796 | parser->http_minor = ch - '0'; 797 | parser->state = s_res_http_minor; 798 | break; 799 | 800 | /* minor HTTP version or end of request line */ 801 | case s_res_http_minor: 802 | { 803 | if (ch == ' ') { 804 | parser->state = s_res_first_status_code; 805 | break; 806 | } 807 | 808 | if (!IS_NUM(ch)) { 809 | SET_ERRNO(HPE_INVALID_VERSION); 810 | goto error; 811 | } 812 | 813 | parser->http_minor *= 10; 814 | parser->http_minor += ch - '0'; 815 | 816 | if (parser->http_minor > 999) { 817 | SET_ERRNO(HPE_INVALID_VERSION); 818 | goto error; 819 | } 820 | 821 | break; 822 | } 823 | 824 | case s_res_first_status_code: 825 | { 826 | if (!IS_NUM(ch)) { 827 | if (ch == ' ') { 828 | break; 829 | } 830 | 831 | SET_ERRNO(HPE_INVALID_STATUS); 832 | goto error; 833 | } 834 | parser->status_code = ch - '0'; 835 | parser->state = s_res_status_code; 836 | break; 837 | } 838 | 839 | case s_res_status_code: 840 | { 841 | if (!IS_NUM(ch)) { 842 | switch (ch) { 843 | case ' ': 844 | parser->state = s_res_status_start; 845 | break; 846 | case CR: 847 | parser->state = s_res_line_almost_done; 848 | break; 849 | case LF: 850 | parser->state = s_header_field_start; 851 | break; 852 | default: 853 | SET_ERRNO(HPE_INVALID_STATUS); 854 | goto error; 855 | } 856 | break; 857 | } 858 | 859 | parser->status_code *= 10; 860 | parser->status_code += ch - '0'; 861 | 862 | if (parser->status_code > 999) { 863 | SET_ERRNO(HPE_INVALID_STATUS); 864 | goto error; 865 | } 866 | 867 | break; 868 | } 869 | 870 | case s_res_status_start: 871 | { 872 | if (ch == CR) { 873 | parser->state = s_res_line_almost_done; 874 | break; 875 | } 876 | 877 | if (ch == LF) { 878 | parser->state = s_header_field_start; 879 | break; 880 | } 881 | 882 | MARK(status); 883 | parser->state = s_res_status; 884 | parser->index = 0; 885 | break; 886 | } 887 | 888 | case s_res_status: 889 | if (ch == CR) { 890 | parser->state = s_res_line_almost_done; 891 | CALLBACK_DATA(status); 892 | break; 893 | } 894 | 895 | if (ch == LF) { 896 | parser->state = s_header_field_start; 897 | CALLBACK_DATA(status); 898 | break; 899 | } 900 | 901 | break; 902 | 903 | case s_res_line_almost_done: 904 | STRICT_CHECK(ch != LF); 905 | parser->state = s_header_field_start; 906 | break; 907 | 908 | case s_start_req: 909 | { 910 | if (ch == CR || ch == LF) 911 | break; 912 | parser->flags = 0; 913 | parser->content_length = ULLONG_MAX; 914 | 915 | if (!IS_ALPHA(ch)) { 916 | SET_ERRNO(HPE_INVALID_METHOD); 917 | goto error; 918 | } 919 | 920 | parser->method = (enum http_method) 0; 921 | parser->index = 1; 922 | switch (ch) { 923 | case 'C': parser->method = HTTP_CONNECT; /* or COPY, CHECKOUT */ break; 924 | case 'D': parser->method = HTTP_DELETE; break; 925 | case 'G': parser->method = HTTP_GET; break; 926 | case 'H': parser->method = HTTP_HEAD; break; 927 | case 'L': parser->method = HTTP_LOCK; break; 928 | case 'M': parser->method = HTTP_MKCOL; /* or MOVE, MKACTIVITY, MERGE, M-SEARCH */ break; 929 | case 'N': parser->method = HTTP_NOTIFY; break; 930 | case 'O': parser->method = HTTP_OPTIONS; break; 931 | case 'P': parser->method = HTTP_POST; 932 | /* or PROPFIND|PROPPATCH|PUT|PATCH|PURGE */ 933 | break; 934 | case 'R': parser->method = HTTP_REPORT; break; 935 | case 'S': parser->method = HTTP_SUBSCRIBE; /* or SEARCH */ break; 936 | case 'T': parser->method = HTTP_TRACE; break; 937 | case 'U': parser->method = HTTP_UNLOCK; /* or UNSUBSCRIBE */ break; 938 | default: 939 | SET_ERRNO(HPE_INVALID_METHOD); 940 | goto error; 941 | } 942 | parser->state = s_req_method; 943 | 944 | CALLBACK_NOTIFY(message_begin); 945 | 946 | break; 947 | } 948 | 949 | case s_req_method: 950 | { 951 | const char *matcher; 952 | if (ch == '\0') { 953 | SET_ERRNO(HPE_INVALID_METHOD); 954 | goto error; 955 | } 956 | 957 | matcher = method_strings[parser->method]; 958 | if (ch == ' ' && matcher[parser->index] == '\0') { 959 | parser->state = s_req_spaces_before_url; 960 | } else if (ch == matcher[parser->index]) { 961 | ; /* nada */ 962 | } else if (parser->method == HTTP_CONNECT) { 963 | if (parser->index == 1 && ch == 'H') { 964 | parser->method = HTTP_CHECKOUT; 965 | } else if (parser->index == 2 && ch == 'P') { 966 | parser->method = HTTP_COPY; 967 | } else { 968 | SET_ERRNO(HPE_INVALID_METHOD); 969 | goto error; 970 | } 971 | } else if (parser->method == HTTP_MKCOL) { 972 | if (parser->index == 1 && ch == 'O') { 973 | parser->method = HTTP_MOVE; 974 | } else if (parser->index == 1 && ch == 'E') { 975 | parser->method = HTTP_MERGE; 976 | } else if (parser->index == 1 && ch == '-') { 977 | parser->method = HTTP_MSEARCH; 978 | } else if (parser->index == 2 && ch == 'A') { 979 | parser->method = HTTP_MKACTIVITY; 980 | } else { 981 | SET_ERRNO(HPE_INVALID_METHOD); 982 | goto error; 983 | } 984 | } else if (parser->method == HTTP_SUBSCRIBE) { 985 | if (parser->index == 1 && ch == 'E') { 986 | parser->method = HTTP_SEARCH; 987 | } else { 988 | SET_ERRNO(HPE_INVALID_METHOD); 989 | goto error; 990 | } 991 | } else if (parser->index == 1 && parser->method == HTTP_POST) { 992 | if (ch == 'R') { 993 | parser->method = HTTP_PROPFIND; /* or HTTP_PROPPATCH */ 994 | } else if (ch == 'U') { 995 | parser->method = HTTP_PUT; /* or HTTP_PURGE */ 996 | } else if (ch == 'A') { 997 | parser->method = HTTP_PATCH; 998 | } else { 999 | SET_ERRNO(HPE_INVALID_METHOD); 1000 | goto error; 1001 | } 1002 | } else if (parser->index == 2) { 1003 | if (parser->method == HTTP_PUT) { 1004 | if (ch == 'R') { 1005 | parser->method = HTTP_PURGE; 1006 | } else { 1007 | SET_ERRNO(HPE_INVALID_METHOD); 1008 | goto error; 1009 | } 1010 | } else if (parser->method == HTTP_UNLOCK) { 1011 | if (ch == 'S') { 1012 | parser->method = HTTP_UNSUBSCRIBE; 1013 | } else { 1014 | SET_ERRNO(HPE_INVALID_METHOD); 1015 | goto error; 1016 | } 1017 | } else { 1018 | SET_ERRNO(HPE_INVALID_METHOD); 1019 | goto error; 1020 | } 1021 | } else if (parser->index == 4 && parser->method == HTTP_PROPFIND && ch == 'P') { 1022 | parser->method = HTTP_PROPPATCH; 1023 | } else { 1024 | SET_ERRNO(HPE_INVALID_METHOD); 1025 | goto error; 1026 | } 1027 | 1028 | ++parser->index; 1029 | break; 1030 | } 1031 | 1032 | case s_req_spaces_before_url: 1033 | { 1034 | if (ch == ' ') break; 1035 | 1036 | MARK(url); 1037 | if (parser->method == HTTP_CONNECT) { 1038 | parser->state = s_req_server_start; 1039 | } 1040 | 1041 | parser->state = parse_url_char((enum state)parser->state, ch); 1042 | if (parser->state == s_dead) { 1043 | SET_ERRNO(HPE_INVALID_URL); 1044 | goto error; 1045 | } 1046 | 1047 | break; 1048 | } 1049 | 1050 | case s_req_schema: 1051 | case s_req_schema_slash: 1052 | case s_req_schema_slash_slash: 1053 | case s_req_server_start: 1054 | { 1055 | switch (ch) { 1056 | /* No whitespace allowed here */ 1057 | case ' ': 1058 | case CR: 1059 | case LF: 1060 | SET_ERRNO(HPE_INVALID_URL); 1061 | goto error; 1062 | default: 1063 | parser->state = parse_url_char((enum state)parser->state, ch); 1064 | if (parser->state == s_dead) { 1065 | SET_ERRNO(HPE_INVALID_URL); 1066 | goto error; 1067 | } 1068 | } 1069 | 1070 | break; 1071 | } 1072 | 1073 | case s_req_server: 1074 | case s_req_server_with_at: 1075 | case s_req_path: 1076 | case s_req_query_string_start: 1077 | case s_req_query_string: 1078 | case s_req_fragment_start: 1079 | case s_req_fragment: 1080 | { 1081 | switch (ch) { 1082 | case ' ': 1083 | parser->state = s_req_http_start; 1084 | CALLBACK_DATA(url); 1085 | break; 1086 | case CR: 1087 | case LF: 1088 | parser->http_major = 0; 1089 | parser->http_minor = 9; 1090 | parser->state = (ch == CR) ? 1091 | s_req_line_almost_done : 1092 | s_header_field_start; 1093 | CALLBACK_DATA(url); 1094 | break; 1095 | default: 1096 | parser->state = parse_url_char((enum state)parser->state, ch); 1097 | if (parser->state == s_dead) { 1098 | SET_ERRNO(HPE_INVALID_URL); 1099 | goto error; 1100 | } 1101 | } 1102 | break; 1103 | } 1104 | 1105 | case s_req_http_start: 1106 | switch (ch) { 1107 | case 'H': 1108 | parser->state = s_req_http_H; 1109 | break; 1110 | case ' ': 1111 | break; 1112 | default: 1113 | SET_ERRNO(HPE_INVALID_CONSTANT); 1114 | goto error; 1115 | } 1116 | break; 1117 | 1118 | case s_req_http_H: 1119 | STRICT_CHECK(ch != 'T'); 1120 | parser->state = s_req_http_HT; 1121 | break; 1122 | 1123 | case s_req_http_HT: 1124 | STRICT_CHECK(ch != 'T'); 1125 | parser->state = s_req_http_HTT; 1126 | break; 1127 | 1128 | case s_req_http_HTT: 1129 | STRICT_CHECK(ch != 'P'); 1130 | parser->state = s_req_http_HTTP; 1131 | break; 1132 | 1133 | case s_req_http_HTTP: 1134 | STRICT_CHECK(ch != '/'); 1135 | parser->state = s_req_first_http_major; 1136 | break; 1137 | 1138 | /* first digit of major HTTP version */ 1139 | case s_req_first_http_major: 1140 | if (ch < '1' || ch > '9') { 1141 | SET_ERRNO(HPE_INVALID_VERSION); 1142 | goto error; 1143 | } 1144 | 1145 | parser->http_major = ch - '0'; 1146 | parser->state = s_req_http_major; 1147 | break; 1148 | 1149 | /* major HTTP version or dot */ 1150 | case s_req_http_major: 1151 | { 1152 | if (ch == '.') { 1153 | parser->state = s_req_first_http_minor; 1154 | break; 1155 | } 1156 | 1157 | if (!IS_NUM(ch)) { 1158 | SET_ERRNO(HPE_INVALID_VERSION); 1159 | goto error; 1160 | } 1161 | 1162 | parser->http_major *= 10; 1163 | parser->http_major += ch - '0'; 1164 | 1165 | if (parser->http_major > 999) { 1166 | SET_ERRNO(HPE_INVALID_VERSION); 1167 | goto error; 1168 | } 1169 | 1170 | break; 1171 | } 1172 | 1173 | /* first digit of minor HTTP version */ 1174 | case s_req_first_http_minor: 1175 | if (!IS_NUM(ch)) { 1176 | SET_ERRNO(HPE_INVALID_VERSION); 1177 | goto error; 1178 | } 1179 | 1180 | parser->http_minor = ch - '0'; 1181 | parser->state = s_req_http_minor; 1182 | break; 1183 | 1184 | /* minor HTTP version or end of request line */ 1185 | case s_req_http_minor: 1186 | { 1187 | if (ch == CR) { 1188 | parser->state = s_req_line_almost_done; 1189 | break; 1190 | } 1191 | 1192 | if (ch == LF) { 1193 | parser->state = s_header_field_start; 1194 | break; 1195 | } 1196 | 1197 | /* XXX allow spaces after digit? */ 1198 | 1199 | if (!IS_NUM(ch)) { 1200 | SET_ERRNO(HPE_INVALID_VERSION); 1201 | goto error; 1202 | } 1203 | 1204 | parser->http_minor *= 10; 1205 | parser->http_minor += ch - '0'; 1206 | 1207 | if (parser->http_minor > 999) { 1208 | SET_ERRNO(HPE_INVALID_VERSION); 1209 | goto error; 1210 | } 1211 | 1212 | break; 1213 | } 1214 | 1215 | /* end of request line */ 1216 | case s_req_line_almost_done: 1217 | { 1218 | if (ch != LF) { 1219 | SET_ERRNO(HPE_LF_EXPECTED); 1220 | goto error; 1221 | } 1222 | 1223 | parser->state = s_header_field_start; 1224 | break; 1225 | } 1226 | 1227 | case s_header_field_start: 1228 | { 1229 | if (ch == CR) { 1230 | parser->state = s_headers_almost_done; 1231 | break; 1232 | } 1233 | 1234 | if (ch == LF) { 1235 | /* they might be just sending \n instead of \r\n so this would be 1236 | * the second \n to denote the end of headers*/ 1237 | parser->state = s_headers_almost_done; 1238 | goto reexecute_byte; 1239 | } 1240 | 1241 | c = TOKEN(ch); 1242 | 1243 | if (!c) { 1244 | SET_ERRNO(HPE_INVALID_HEADER_TOKEN); 1245 | goto error; 1246 | } 1247 | 1248 | MARK(header_field); 1249 | 1250 | parser->index = 0; 1251 | parser->state = s_header_field; 1252 | 1253 | switch (c) { 1254 | case 'c': 1255 | parser->header_state = h_C; 1256 | break; 1257 | 1258 | case 'p': 1259 | parser->header_state = h_matching_proxy_connection; 1260 | break; 1261 | 1262 | case 't': 1263 | parser->header_state = h_matching_transfer_encoding; 1264 | break; 1265 | 1266 | case 'u': 1267 | parser->header_state = h_matching_upgrade; 1268 | break; 1269 | 1270 | default: 1271 | parser->header_state = h_general; 1272 | break; 1273 | } 1274 | break; 1275 | } 1276 | 1277 | case s_header_field: 1278 | { 1279 | c = TOKEN(ch); 1280 | 1281 | if (c) { 1282 | switch (parser->header_state) { 1283 | case h_general: 1284 | break; 1285 | 1286 | case h_C: 1287 | parser->index++; 1288 | parser->header_state = (c == 'o' ? h_CO : h_general); 1289 | break; 1290 | 1291 | case h_CO: 1292 | parser->index++; 1293 | parser->header_state = (c == 'n' ? h_CON : h_general); 1294 | break; 1295 | 1296 | case h_CON: 1297 | parser->index++; 1298 | switch (c) { 1299 | case 'n': 1300 | parser->header_state = h_matching_connection; 1301 | break; 1302 | case 't': 1303 | parser->header_state = h_matching_content_length; 1304 | break; 1305 | default: 1306 | parser->header_state = h_general; 1307 | break; 1308 | } 1309 | break; 1310 | 1311 | /* connection */ 1312 | 1313 | case h_matching_connection: 1314 | parser->index++; 1315 | if (parser->index > sizeof(CONNECTION)-1 1316 | || c != CONNECTION[parser->index]) { 1317 | parser->header_state = h_general; 1318 | } else if (parser->index == sizeof(CONNECTION)-2) { 1319 | parser->header_state = h_connection; 1320 | } 1321 | break; 1322 | 1323 | /* proxy-connection */ 1324 | 1325 | case h_matching_proxy_connection: 1326 | parser->index++; 1327 | if (parser->index > sizeof(PROXY_CONNECTION)-1 1328 | || c != PROXY_CONNECTION[parser->index]) { 1329 | parser->header_state = h_general; 1330 | } else if (parser->index == sizeof(PROXY_CONNECTION)-2) { 1331 | parser->header_state = h_connection; 1332 | } 1333 | break; 1334 | 1335 | /* content-length */ 1336 | 1337 | case h_matching_content_length: 1338 | parser->index++; 1339 | if (parser->index > sizeof(CONTENT_LENGTH)-1 1340 | || c != CONTENT_LENGTH[parser->index]) { 1341 | parser->header_state = h_general; 1342 | } else if (parser->index == sizeof(CONTENT_LENGTH)-2) { 1343 | parser->header_state = h_content_length; 1344 | } 1345 | break; 1346 | 1347 | /* transfer-encoding */ 1348 | 1349 | case h_matching_transfer_encoding: 1350 | parser->index++; 1351 | if (parser->index > sizeof(TRANSFER_ENCODING)-1 1352 | || c != TRANSFER_ENCODING[parser->index]) { 1353 | parser->header_state = h_general; 1354 | } else if (parser->index == sizeof(TRANSFER_ENCODING)-2) { 1355 | parser->header_state = h_transfer_encoding; 1356 | } 1357 | break; 1358 | 1359 | /* upgrade */ 1360 | 1361 | case h_matching_upgrade: 1362 | parser->index++; 1363 | if (parser->index > sizeof(UPGRADE)-1 1364 | || c != UPGRADE[parser->index]) { 1365 | parser->header_state = h_general; 1366 | } else if (parser->index == sizeof(UPGRADE)-2) { 1367 | parser->header_state = h_upgrade; 1368 | } 1369 | break; 1370 | 1371 | case h_connection: 1372 | case h_content_length: 1373 | case h_transfer_encoding: 1374 | case h_upgrade: 1375 | if (ch != ' ') parser->header_state = h_general; 1376 | break; 1377 | 1378 | default: 1379 | assert(0 && "Unknown header_state"); 1380 | break; 1381 | } 1382 | break; 1383 | } 1384 | 1385 | if (ch == ':') { 1386 | parser->state = s_header_value_start; 1387 | CALLBACK_DATA(header_field); 1388 | break; 1389 | } 1390 | 1391 | if (ch == CR) { 1392 | parser->state = s_header_almost_done; 1393 | CALLBACK_DATA(header_field); 1394 | break; 1395 | } 1396 | 1397 | if (ch == LF) { 1398 | parser->state = s_header_field_start; 1399 | CALLBACK_DATA(header_field); 1400 | break; 1401 | } 1402 | 1403 | SET_ERRNO(HPE_INVALID_HEADER_TOKEN); 1404 | goto error; 1405 | } 1406 | 1407 | case s_header_value_start: 1408 | { 1409 | if (ch == ' ' || ch == '\t') break; 1410 | 1411 | MARK(header_value); 1412 | 1413 | parser->state = s_header_value; 1414 | parser->index = 0; 1415 | 1416 | if (ch == CR) { 1417 | parser->header_state = h_general; 1418 | parser->state = s_header_almost_done; 1419 | CALLBACK_DATA(header_value); 1420 | break; 1421 | } 1422 | 1423 | if (ch == LF) { 1424 | parser->state = s_header_field_start; 1425 | CALLBACK_DATA(header_value); 1426 | break; 1427 | } 1428 | 1429 | c = LOWER(ch); 1430 | 1431 | switch (parser->header_state) { 1432 | case h_upgrade: 1433 | parser->flags |= F_UPGRADE; 1434 | parser->header_state = h_general; 1435 | break; 1436 | 1437 | case h_transfer_encoding: 1438 | /* looking for 'Transfer-Encoding: chunked' */ 1439 | if ('c' == c) { 1440 | parser->header_state = h_matching_transfer_encoding_chunked; 1441 | } else { 1442 | parser->header_state = h_general; 1443 | } 1444 | break; 1445 | 1446 | case h_content_length: 1447 | if (!IS_NUM(ch)) { 1448 | SET_ERRNO(HPE_INVALID_CONTENT_LENGTH); 1449 | goto error; 1450 | } 1451 | 1452 | parser->content_length = ch - '0'; 1453 | break; 1454 | 1455 | case h_connection: 1456 | /* looking for 'Connection: keep-alive' */ 1457 | if (c == 'k') { 1458 | parser->header_state = h_matching_connection_keep_alive; 1459 | /* looking for 'Connection: close' */ 1460 | } else if (c == 'c') { 1461 | parser->header_state = h_matching_connection_close; 1462 | } else { 1463 | parser->header_state = h_general; 1464 | } 1465 | break; 1466 | 1467 | default: 1468 | parser->header_state = h_general; 1469 | break; 1470 | } 1471 | break; 1472 | } 1473 | 1474 | case s_header_value: 1475 | { 1476 | 1477 | if (ch == CR) { 1478 | parser->state = s_header_almost_done; 1479 | CALLBACK_DATA(header_value); 1480 | break; 1481 | } 1482 | 1483 | if (ch == LF) { 1484 | parser->state = s_header_almost_done; 1485 | CALLBACK_DATA_NOADVANCE(header_value); 1486 | goto reexecute_byte; 1487 | } 1488 | 1489 | c = LOWER(ch); 1490 | 1491 | switch (parser->header_state) { 1492 | case h_general: 1493 | break; 1494 | 1495 | case h_connection: 1496 | case h_transfer_encoding: 1497 | assert(0 && "Shouldn't get here."); 1498 | break; 1499 | 1500 | case h_content_length: 1501 | { 1502 | uint64_t t; 1503 | 1504 | if (ch == ' ') break; 1505 | 1506 | if (!IS_NUM(ch)) { 1507 | SET_ERRNO(HPE_INVALID_CONTENT_LENGTH); 1508 | goto error; 1509 | } 1510 | 1511 | t = parser->content_length; 1512 | t *= 10; 1513 | t += ch - '0'; 1514 | 1515 | /* Overflow? Test against a conservative limit for simplicity. */ 1516 | if ((ULLONG_MAX - 10) / 10 < parser->content_length) { 1517 | SET_ERRNO(HPE_INVALID_CONTENT_LENGTH); 1518 | goto error; 1519 | } 1520 | 1521 | parser->content_length = t; 1522 | break; 1523 | } 1524 | 1525 | /* Transfer-Encoding: chunked */ 1526 | case h_matching_transfer_encoding_chunked: 1527 | parser->index++; 1528 | if (parser->index > sizeof(CHUNKED)-1 1529 | || c != CHUNKED[parser->index]) { 1530 | parser->header_state = h_general; 1531 | } else if (parser->index == sizeof(CHUNKED)-2) { 1532 | parser->header_state = h_transfer_encoding_chunked; 1533 | } 1534 | break; 1535 | 1536 | /* looking for 'Connection: keep-alive' */ 1537 | case h_matching_connection_keep_alive: 1538 | parser->index++; 1539 | if (parser->index > sizeof(KEEP_ALIVE)-1 1540 | || c != KEEP_ALIVE[parser->index]) { 1541 | parser->header_state = h_general; 1542 | } else if (parser->index == sizeof(KEEP_ALIVE)-2) { 1543 | parser->header_state = h_connection_keep_alive; 1544 | } 1545 | break; 1546 | 1547 | /* looking for 'Connection: close' */ 1548 | case h_matching_connection_close: 1549 | parser->index++; 1550 | if (parser->index > sizeof(CLOSE)-1 || c != CLOSE[parser->index]) { 1551 | parser->header_state = h_general; 1552 | } else if (parser->index == sizeof(CLOSE)-2) { 1553 | parser->header_state = h_connection_close; 1554 | } 1555 | break; 1556 | 1557 | case h_transfer_encoding_chunked: 1558 | case h_connection_keep_alive: 1559 | case h_connection_close: 1560 | if (ch != ' ') parser->header_state = h_general; 1561 | break; 1562 | 1563 | default: 1564 | parser->state = s_header_value; 1565 | parser->header_state = h_general; 1566 | break; 1567 | } 1568 | break; 1569 | } 1570 | 1571 | case s_header_almost_done: 1572 | { 1573 | STRICT_CHECK(ch != LF); 1574 | 1575 | parser->state = s_header_value_lws; 1576 | 1577 | switch (parser->header_state) { 1578 | case h_connection_keep_alive: 1579 | parser->flags |= F_CONNECTION_KEEP_ALIVE; 1580 | break; 1581 | case h_connection_close: 1582 | parser->flags |= F_CONNECTION_CLOSE; 1583 | break; 1584 | case h_transfer_encoding_chunked: 1585 | parser->flags |= F_CHUNKED; 1586 | break; 1587 | default: 1588 | break; 1589 | } 1590 | 1591 | break; 1592 | } 1593 | 1594 | case s_header_value_lws: 1595 | { 1596 | if (ch == ' ' || ch == '\t') 1597 | parser->state = s_header_value_start; 1598 | else 1599 | { 1600 | parser->state = s_header_field_start; 1601 | goto reexecute_byte; 1602 | } 1603 | break; 1604 | } 1605 | 1606 | case s_headers_almost_done: 1607 | { 1608 | STRICT_CHECK(ch != LF); 1609 | 1610 | if (parser->flags & F_TRAILING) { 1611 | /* End of a chunked request */ 1612 | parser->state = NEW_MESSAGE(); 1613 | CALLBACK_NOTIFY(message_complete); 1614 | break; 1615 | } 1616 | 1617 | parser->state = s_headers_done; 1618 | 1619 | /* Set this here so that on_headers_complete() callbacks can see it */ 1620 | parser->upgrade = 1621 | (parser->flags & F_UPGRADE || parser->method == HTTP_CONNECT); 1622 | 1623 | /* Here we call the headers_complete callback. This is somewhat 1624 | * different than other callbacks because if the user returns 1, we 1625 | * will interpret that as saying that this message has no body. This 1626 | * is needed for the annoying case of recieving a response to a HEAD 1627 | * request. 1628 | * 1629 | * We'd like to use CALLBACK_NOTIFY_NOADVANCE() here but we cannot, so 1630 | * we have to simulate it by handling a change in errno below. 1631 | */ 1632 | if (settings->on_headers_complete) { 1633 | switch (settings->on_headers_complete(parser)) { 1634 | case 0: 1635 | break; 1636 | 1637 | case 1: 1638 | parser->flags |= F_SKIPBODY; 1639 | break; 1640 | 1641 | default: 1642 | SET_ERRNO(HPE_CB_headers_complete); 1643 | return p - data; /* Error */ 1644 | } 1645 | } 1646 | 1647 | if (HTTP_PARSER_ERRNO(parser) != HPE_OK) { 1648 | return p - data; 1649 | } 1650 | 1651 | goto reexecute_byte; 1652 | } 1653 | 1654 | case s_headers_done: 1655 | { 1656 | STRICT_CHECK(ch != LF); 1657 | 1658 | parser->nread = 0; 1659 | 1660 | /* Exit, the rest of the connect is in a different protocol. */ 1661 | if (parser->upgrade) { 1662 | parser->state = NEW_MESSAGE(); 1663 | CALLBACK_NOTIFY(message_complete); 1664 | return (p - data) + 1; 1665 | } 1666 | 1667 | if (parser->flags & F_SKIPBODY) { 1668 | parser->state = NEW_MESSAGE(); 1669 | CALLBACK_NOTIFY(message_complete); 1670 | } else if (parser->flags & F_CHUNKED) { 1671 | /* chunked encoding - ignore Content-Length header */ 1672 | parser->state = s_chunk_size_start; 1673 | } else { 1674 | if (parser->content_length == 0) { 1675 | /* Content-Length header given but zero: Content-Length: 0\r\n */ 1676 | parser->state = NEW_MESSAGE(); 1677 | CALLBACK_NOTIFY(message_complete); 1678 | } else if (parser->content_length != ULLONG_MAX) { 1679 | /* Content-Length header given and non-zero */ 1680 | parser->state = s_body_identity; 1681 | } else { 1682 | if (parser->type == HTTP_REQUEST || 1683 | !http_message_needs_eof(parser)) { 1684 | /* Assume content-length 0 - read the next */ 1685 | parser->state = NEW_MESSAGE(); 1686 | CALLBACK_NOTIFY(message_complete); 1687 | } else { 1688 | /* Read body until EOF */ 1689 | parser->state = s_body_identity_eof; 1690 | } 1691 | } 1692 | } 1693 | 1694 | break; 1695 | } 1696 | 1697 | case s_body_identity: 1698 | { 1699 | uint64_t to_read = MIN(parser->content_length, 1700 | (uint64_t) ((data + len) - p)); 1701 | 1702 | assert(parser->content_length != 0 1703 | && parser->content_length != ULLONG_MAX); 1704 | 1705 | /* The difference between advancing content_length and p is because 1706 | * the latter will automaticaly advance on the next loop iteration. 1707 | * Further, if content_length ends up at 0, we want to see the last 1708 | * byte again for our message complete callback. 1709 | */ 1710 | MARK(body); 1711 | parser->content_length -= to_read; 1712 | p += to_read - 1; 1713 | 1714 | if (parser->content_length == 0) { 1715 | parser->state = s_message_done; 1716 | 1717 | /* Mimic CALLBACK_DATA_NOADVANCE() but with one extra byte. 1718 | * 1719 | * The alternative to doing this is to wait for the next byte to 1720 | * trigger the data callback, just as in every other case. The 1721 | * problem with this is that this makes it difficult for the test 1722 | * harness to distinguish between complete-on-EOF and 1723 | * complete-on-length. It's not clear that this distinction is 1724 | * important for applications, but let's keep it for now. 1725 | */ 1726 | CALLBACK_DATA_(body, p - body_mark + 1, p - data); 1727 | goto reexecute_byte; 1728 | } 1729 | 1730 | break; 1731 | } 1732 | 1733 | /* read until EOF */ 1734 | case s_body_identity_eof: 1735 | MARK(body); 1736 | p = data + len - 1; 1737 | 1738 | break; 1739 | 1740 | case s_message_done: 1741 | parser->state = NEW_MESSAGE(); 1742 | CALLBACK_NOTIFY(message_complete); 1743 | break; 1744 | 1745 | case s_chunk_size_start: 1746 | { 1747 | assert(parser->nread == 1); 1748 | assert(parser->flags & F_CHUNKED); 1749 | 1750 | unhex_val = unhex[(unsigned char)ch]; 1751 | if (unhex_val == -1) { 1752 | SET_ERRNO(HPE_INVALID_CHUNK_SIZE); 1753 | goto error; 1754 | } 1755 | 1756 | parser->content_length = unhex_val; 1757 | parser->state = s_chunk_size; 1758 | break; 1759 | } 1760 | 1761 | case s_chunk_size: 1762 | { 1763 | uint64_t t; 1764 | 1765 | assert(parser->flags & F_CHUNKED); 1766 | 1767 | if (ch == CR) { 1768 | parser->state = s_chunk_size_almost_done; 1769 | break; 1770 | } 1771 | 1772 | unhex_val = unhex[(unsigned char)ch]; 1773 | 1774 | if (unhex_val == -1) { 1775 | if (ch == ';' || ch == ' ') { 1776 | parser->state = s_chunk_parameters; 1777 | break; 1778 | } 1779 | 1780 | SET_ERRNO(HPE_INVALID_CHUNK_SIZE); 1781 | goto error; 1782 | } 1783 | 1784 | t = parser->content_length; 1785 | t *= 16; 1786 | t += unhex_val; 1787 | 1788 | /* Overflow? Test against a conservative limit for simplicity. */ 1789 | if ((ULLONG_MAX - 16) / 16 < parser->content_length) { 1790 | SET_ERRNO(HPE_INVALID_CONTENT_LENGTH); 1791 | goto error; 1792 | } 1793 | 1794 | parser->content_length = t; 1795 | break; 1796 | } 1797 | 1798 | case s_chunk_parameters: 1799 | { 1800 | assert(parser->flags & F_CHUNKED); 1801 | /* just ignore this shit. TODO check for overflow */ 1802 | if (ch == CR) { 1803 | parser->state = s_chunk_size_almost_done; 1804 | break; 1805 | } 1806 | break; 1807 | } 1808 | 1809 | case s_chunk_size_almost_done: 1810 | { 1811 | assert(parser->flags & F_CHUNKED); 1812 | STRICT_CHECK(ch != LF); 1813 | 1814 | parser->nread = 0; 1815 | 1816 | if (parser->content_length == 0) { 1817 | parser->flags |= F_TRAILING; 1818 | parser->state = s_header_field_start; 1819 | } else { 1820 | parser->state = s_chunk_data; 1821 | } 1822 | break; 1823 | } 1824 | 1825 | case s_chunk_data: 1826 | { 1827 | uint64_t to_read = MIN(parser->content_length, 1828 | (uint64_t) ((data + len) - p)); 1829 | 1830 | assert(parser->flags & F_CHUNKED); 1831 | assert(parser->content_length != 0 1832 | && parser->content_length != ULLONG_MAX); 1833 | 1834 | /* See the explanation in s_body_identity for why the content 1835 | * length and data pointers are managed this way. 1836 | */ 1837 | MARK(body); 1838 | parser->content_length -= to_read; 1839 | p += to_read - 1; 1840 | 1841 | if (parser->content_length == 0) { 1842 | parser->state = s_chunk_data_almost_done; 1843 | } 1844 | 1845 | break; 1846 | } 1847 | 1848 | case s_chunk_data_almost_done: 1849 | assert(parser->flags & F_CHUNKED); 1850 | assert(parser->content_length == 0); 1851 | STRICT_CHECK(ch != CR); 1852 | parser->state = s_chunk_data_done; 1853 | CALLBACK_DATA(body); 1854 | break; 1855 | 1856 | case s_chunk_data_done: 1857 | assert(parser->flags & F_CHUNKED); 1858 | STRICT_CHECK(ch != LF); 1859 | parser->nread = 0; 1860 | parser->state = s_chunk_size_start; 1861 | break; 1862 | 1863 | default: 1864 | assert(0 && "unhandled state"); 1865 | SET_ERRNO(HPE_INVALID_INTERNAL_STATE); 1866 | goto error; 1867 | } 1868 | } 1869 | 1870 | /* Run callbacks for any marks that we have leftover after we ran our of 1871 | * bytes. There should be at most one of these set, so it's OK to invoke 1872 | * them in series (unset marks will not result in callbacks). 1873 | * 1874 | * We use the NOADVANCE() variety of callbacks here because 'p' has already 1875 | * overflowed 'data' and this allows us to correct for the off-by-one that 1876 | * we'd otherwise have (since CALLBACK_DATA() is meant to be run with a 'p' 1877 | * value that's in-bounds). 1878 | */ 1879 | 1880 | assert(((header_field_mark ? 1 : 0) + 1881 | (header_value_mark ? 1 : 0) + 1882 | (url_mark ? 1 : 0) + 1883 | (body_mark ? 1 : 0) + 1884 | (status_mark ? 1 : 0)) <= 1); 1885 | 1886 | CALLBACK_DATA_NOADVANCE(header_field); 1887 | CALLBACK_DATA_NOADVANCE(header_value); 1888 | CALLBACK_DATA_NOADVANCE(url); 1889 | CALLBACK_DATA_NOADVANCE(body); 1890 | CALLBACK_DATA_NOADVANCE(status); 1891 | 1892 | return len; 1893 | 1894 | error: 1895 | if (HTTP_PARSER_ERRNO(parser) == HPE_OK) { 1896 | SET_ERRNO(HPE_UNKNOWN); 1897 | } 1898 | 1899 | return (p - data); 1900 | } 1901 | 1902 | 1903 | /* Does the parser need to see an EOF to find the end of the message? */ 1904 | int 1905 | http_message_needs_eof (const http_parser *parser) 1906 | { 1907 | if (parser->type == HTTP_REQUEST) { 1908 | return 0; 1909 | } 1910 | 1911 | /* See RFC 2616 section 4.4 */ 1912 | if (parser->status_code / 100 == 1 || /* 1xx e.g. Continue */ 1913 | parser->status_code == 204 || /* No Content */ 1914 | parser->status_code == 304 || /* Not Modified */ 1915 | parser->flags & F_SKIPBODY) { /* response to a HEAD request */ 1916 | return 0; 1917 | } 1918 | 1919 | if ((parser->flags & F_CHUNKED) || parser->content_length != ULLONG_MAX) { 1920 | return 0; 1921 | } 1922 | 1923 | return 1; 1924 | } 1925 | 1926 | 1927 | int 1928 | http_should_keep_alive (const http_parser *parser) 1929 | { 1930 | if (parser->http_major > 0 && parser->http_minor > 0) { 1931 | /* HTTP/1.1 */ 1932 | if (parser->flags & F_CONNECTION_CLOSE) { 1933 | return 0; 1934 | } 1935 | } else { 1936 | /* HTTP/1.0 or earlier */ 1937 | if (!(parser->flags & F_CONNECTION_KEEP_ALIVE)) { 1938 | return 0; 1939 | } 1940 | } 1941 | 1942 | return !http_message_needs_eof(parser); 1943 | } 1944 | 1945 | 1946 | const char * 1947 | http_method_str (enum http_method m) 1948 | { 1949 | return ELEM_AT(method_strings, m, ""); 1950 | } 1951 | 1952 | 1953 | void 1954 | http_parser_init (http_parser *parser, enum http_parser_type t) 1955 | { 1956 | void *data = parser->data; /* preserve application data */ 1957 | memset(parser, 0, sizeof(*parser)); 1958 | parser->data = data; 1959 | parser->type = t; 1960 | parser->state = (t == HTTP_REQUEST ? s_start_req : (t == HTTP_RESPONSE ? s_start_res : s_start_req_or_res)); 1961 | parser->http_errno = HPE_OK; 1962 | } 1963 | 1964 | const char * 1965 | http_errno_name(enum http_errno err) { 1966 | assert(err < (sizeof(http_strerror_tab)/sizeof(http_strerror_tab[0]))); 1967 | return http_strerror_tab[err].name; 1968 | } 1969 | 1970 | const char * 1971 | http_errno_description(enum http_errno err) { 1972 | assert(err < (sizeof(http_strerror_tab)/sizeof(http_strerror_tab[0]))); 1973 | return http_strerror_tab[err].description; 1974 | } 1975 | 1976 | static enum http_host_state 1977 | http_parse_host_char(enum http_host_state s, const char ch) { 1978 | switch(s) { 1979 | case s_http_userinfo: 1980 | case s_http_userinfo_start: 1981 | if (ch == '@') { 1982 | return s_http_host_start; 1983 | } 1984 | 1985 | if (IS_USERINFO_CHAR(ch)) { 1986 | return s_http_userinfo; 1987 | } 1988 | break; 1989 | 1990 | case s_http_host_start: 1991 | if (ch == '[') { 1992 | return s_http_host_v6_start; 1993 | } 1994 | 1995 | if (IS_HOST_CHAR(ch)) { 1996 | return s_http_host; 1997 | } 1998 | 1999 | break; 2000 | 2001 | case s_http_host: 2002 | if (IS_HOST_CHAR(ch)) { 2003 | return s_http_host; 2004 | } 2005 | 2006 | /* FALLTHROUGH */ 2007 | case s_http_host_v6_end: 2008 | if (ch == ':') { 2009 | return s_http_host_port_start; 2010 | } 2011 | 2012 | break; 2013 | 2014 | case s_http_host_v6: 2015 | if (ch == ']') { 2016 | return s_http_host_v6_end; 2017 | } 2018 | 2019 | /* FALLTHROUGH */ 2020 | case s_http_host_v6_start: 2021 | if (IS_HEX(ch) || ch == ':' || ch == '.') { 2022 | return s_http_host_v6; 2023 | } 2024 | 2025 | break; 2026 | 2027 | case s_http_host_port: 2028 | case s_http_host_port_start: 2029 | if (IS_NUM(ch)) { 2030 | return s_http_host_port; 2031 | } 2032 | 2033 | break; 2034 | 2035 | default: 2036 | break; 2037 | } 2038 | return s_http_host_dead; 2039 | } 2040 | 2041 | static int 2042 | http_parse_host(const char * buf, struct http_parser_url *u, int found_at) { 2043 | enum http_host_state s; 2044 | 2045 | const char *p; 2046 | size_t buflen = u->field_data[UF_HOST].off + u->field_data[UF_HOST].len; 2047 | 2048 | u->field_data[UF_HOST].len = 0; 2049 | 2050 | s = found_at ? s_http_userinfo_start : s_http_host_start; 2051 | 2052 | for (p = buf + u->field_data[UF_HOST].off; p < buf + buflen; p++) { 2053 | enum http_host_state new_s = http_parse_host_char(s, *p); 2054 | 2055 | if (new_s == s_http_host_dead) { 2056 | return 1; 2057 | } 2058 | 2059 | switch(new_s) { 2060 | case s_http_host: 2061 | if (s != s_http_host) { 2062 | u->field_data[UF_HOST].off = p - buf; 2063 | } 2064 | u->field_data[UF_HOST].len++; 2065 | break; 2066 | 2067 | case s_http_host_v6: 2068 | if (s != s_http_host_v6) { 2069 | u->field_data[UF_HOST].off = p - buf; 2070 | } 2071 | u->field_data[UF_HOST].len++; 2072 | break; 2073 | 2074 | case s_http_host_port: 2075 | if (s != s_http_host_port) { 2076 | u->field_data[UF_PORT].off = p - buf; 2077 | u->field_data[UF_PORT].len = 0; 2078 | u->field_set |= (1 << UF_PORT); 2079 | } 2080 | u->field_data[UF_PORT].len++; 2081 | break; 2082 | 2083 | case s_http_userinfo: 2084 | if (s != s_http_userinfo) { 2085 | u->field_data[UF_USERINFO].off = p - buf ; 2086 | u->field_data[UF_USERINFO].len = 0; 2087 | u->field_set |= (1 << UF_USERINFO); 2088 | } 2089 | u->field_data[UF_USERINFO].len++; 2090 | break; 2091 | 2092 | default: 2093 | break; 2094 | } 2095 | s = new_s; 2096 | } 2097 | 2098 | /* Make sure we don't end somewhere unexpected */ 2099 | switch (s) { 2100 | case s_http_host_start: 2101 | case s_http_host_v6_start: 2102 | case s_http_host_v6: 2103 | case s_http_host_port_start: 2104 | case s_http_userinfo: 2105 | case s_http_userinfo_start: 2106 | return 1; 2107 | default: 2108 | break; 2109 | } 2110 | 2111 | return 0; 2112 | } 2113 | 2114 | int 2115 | http_parser_parse_url(const char *buf, size_t buflen, int is_connect, 2116 | struct http_parser_url *u) 2117 | { 2118 | enum state s; 2119 | const char *p; 2120 | enum http_parser_url_fields uf, old_uf; 2121 | int found_at = 0; 2122 | 2123 | u->port = u->field_set = 0; 2124 | s = is_connect ? s_req_server_start : s_req_spaces_before_url; 2125 | uf = old_uf = UF_MAX; 2126 | 2127 | for (p = buf; p < buf + buflen; p++) { 2128 | s = parse_url_char(s, *p); 2129 | 2130 | /* Figure out the next field that we're operating on */ 2131 | switch (s) { 2132 | case s_dead: 2133 | return 1; 2134 | 2135 | /* Skip delimeters */ 2136 | case s_req_schema_slash: 2137 | case s_req_schema_slash_slash: 2138 | case s_req_server_start: 2139 | case s_req_query_string_start: 2140 | case s_req_fragment_start: 2141 | continue; 2142 | 2143 | case s_req_schema: 2144 | uf = UF_SCHEMA; 2145 | break; 2146 | 2147 | case s_req_server_with_at: 2148 | found_at = 1; 2149 | 2150 | /* FALLTROUGH */ 2151 | case s_req_server: 2152 | uf = UF_HOST; 2153 | break; 2154 | 2155 | case s_req_path: 2156 | uf = UF_PATH; 2157 | break; 2158 | 2159 | case s_req_query_string: 2160 | uf = UF_QUERY; 2161 | break; 2162 | 2163 | case s_req_fragment: 2164 | uf = UF_FRAGMENT; 2165 | break; 2166 | 2167 | default: 2168 | assert(!"Unexpected state"); 2169 | return 1; 2170 | } 2171 | 2172 | /* Nothing's changed; soldier on */ 2173 | if (uf == old_uf) { 2174 | u->field_data[uf].len++; 2175 | continue; 2176 | } 2177 | 2178 | u->field_data[uf].off = p - buf; 2179 | u->field_data[uf].len = 1; 2180 | 2181 | u->field_set |= (1 << uf); 2182 | old_uf = uf; 2183 | } 2184 | 2185 | /* host must be present if there is a schema */ 2186 | /* parsing http:///toto will fail */ 2187 | if ((u->field_set & ((1 << UF_SCHEMA) | (1 << UF_HOST))) != 0) { 2188 | if (http_parse_host(buf, u, found_at) != 0) { 2189 | return 1; 2190 | } 2191 | } 2192 | 2193 | /* CONNECT requests can only contain "hostname:port" */ 2194 | if (is_connect && u->field_set != ((1 << UF_HOST)|(1 << UF_PORT))) { 2195 | return 1; 2196 | } 2197 | 2198 | if (u->field_set & (1 << UF_PORT)) { 2199 | /* Don't bother with endp; we've already validated the string */ 2200 | unsigned long v = strtoul(buf + u->field_data[UF_PORT].off, NULL, 10); 2201 | 2202 | /* Ports have a max value of 2^16 */ 2203 | if (v > 0xffff) { 2204 | return 1; 2205 | } 2206 | 2207 | u->port = (uint16_t) v; 2208 | } 2209 | 2210 | return 0; 2211 | } 2212 | 2213 | void 2214 | http_parser_pause(http_parser *parser, int paused) { 2215 | /* Users should only be pausing/unpausing a parser that is not in an error 2216 | * state. In non-debug builds, there's not much that we can do about this 2217 | * other than ignore it. 2218 | */ 2219 | if (HTTP_PARSER_ERRNO(parser) == HPE_OK || 2220 | HTTP_PARSER_ERRNO(parser) == HPE_PAUSED) { 2221 | SET_ERRNO((paused) ? HPE_PAUSED : HPE_OK); 2222 | } else { 2223 | assert(0 && "Attempting to pause parser in error state"); 2224 | } 2225 | } 2226 | 2227 | int 2228 | http_body_is_final(const struct http_parser *parser) { 2229 | return parser->state == s_message_done; 2230 | } 2231 | 2232 | unsigned long 2233 | http_parser_version(void) { 2234 | return HTTP_PARSER_VERSION_MAJOR * 0x10000 | 2235 | HTTP_PARSER_VERSION_MINOR * 0x00100 | 2236 | HTTP_PARSER_VERSION_PATCH * 0x00001; 2237 | } 2238 | -------------------------------------------------------------------------------- /http_parser.h: -------------------------------------------------------------------------------- 1 | /* Copyright Joyent, Inc. and other Node contributors. All rights reserved. 2 | * 3 | * Permission is hereby granted, free of charge, to any person obtaining a copy 4 | * of this software and associated documentation files (the "Software"), to 5 | * deal in the Software without restriction, including without limitation the 6 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 7 | * sell copies of the Software, and to permit persons to whom the Software is 8 | * furnished to do so, subject to the following conditions: 9 | * 10 | * The above copyright notice and this permission notice shall be included in 11 | * all copies or substantial portions of the Software. 12 | * 13 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 19 | * IN THE SOFTWARE. 20 | */ 21 | #ifndef http_parser_h 22 | #define http_parser_h 23 | #ifdef __cplusplus 24 | extern "C" { 25 | #endif 26 | 27 | /* Also update SONAME in the Makefile whenever you change these. */ 28 | #define HTTP_PARSER_VERSION_MAJOR 2 29 | #define HTTP_PARSER_VERSION_MINOR 2 30 | #define HTTP_PARSER_VERSION_PATCH 1 31 | 32 | //#include 33 | #include 34 | #if defined(_WIN32) && !defined(__MINGW32__) && (!defined(_MSC_VER) || _MSC_VER<1600) 35 | #include 36 | #include 37 | typedef __int8 int8_t; 38 | typedef unsigned __int8 uint8_t; 39 | typedef __int16 int16_t; 40 | typedef unsigned __int16 uint16_t; 41 | typedef __int32 int32_t; 42 | typedef unsigned __int32 uint32_t; 43 | typedef __int64 int64_t; 44 | typedef unsigned __int64 uint64_t; 45 | #else 46 | #include 47 | #endif 48 | 49 | /* Compile with -DHTTP_PARSER_STRICT=0 to make less checks, but run 50 | * faster 51 | */ 52 | #ifndef HTTP_PARSER_STRICT 53 | # define HTTP_PARSER_STRICT 1 54 | #endif 55 | 56 | /* Maximium header size allowed */ 57 | #define HTTP_MAX_HEADER_SIZE (80*1024) 58 | 59 | 60 | typedef struct http_parser http_parser; 61 | typedef struct http_parser_settings http_parser_settings; 62 | 63 | 64 | /* Callbacks should return non-zero to indicate an error. The parser will 65 | * then halt execution. 66 | * 67 | * The one exception is on_headers_complete. In a HTTP_RESPONSE parser 68 | * returning '1' from on_headers_complete will tell the parser that it 69 | * should not expect a body. This is used when receiving a response to a 70 | * HEAD request which may contain 'Content-Length' or 'Transfer-Encoding: 71 | * chunked' headers that indicate the presence of a body. 72 | * 73 | * http_data_cb does not return data chunks. It will be call arbitrarally 74 | * many times for each string. E.G. you might get 10 callbacks for "on_url" 75 | * each providing just a few characters more data. 76 | */ 77 | typedef int (*http_data_cb) (http_parser*, const char *at, size_t length); 78 | typedef int (*http_cb) (http_parser*); 79 | 80 | 81 | /* Request Methods */ 82 | #define HTTP_METHOD_MAP(XX) \ 83 | XX(0, DELETE, DELETE) \ 84 | XX(1, GET, GET) \ 85 | XX(2, HEAD, HEAD) \ 86 | XX(3, POST, POST) \ 87 | XX(4, PUT, PUT) \ 88 | /* pathological */ \ 89 | XX(5, CONNECT, CONNECT) \ 90 | XX(6, OPTIONS, OPTIONS) \ 91 | XX(7, TRACE, TRACE) \ 92 | /* webdav */ \ 93 | XX(8, COPY, COPY) \ 94 | XX(9, LOCK, LOCK) \ 95 | XX(10, MKCOL, MKCOL) \ 96 | XX(11, MOVE, MOVE) \ 97 | XX(12, PROPFIND, PROPFIND) \ 98 | XX(13, PROPPATCH, PROPPATCH) \ 99 | XX(14, SEARCH, SEARCH) \ 100 | XX(15, UNLOCK, UNLOCK) \ 101 | /* subversion */ \ 102 | XX(16, REPORT, REPORT) \ 103 | XX(17, MKACTIVITY, MKACTIVITY) \ 104 | XX(18, CHECKOUT, CHECKOUT) \ 105 | XX(19, MERGE, MERGE) \ 106 | /* upnp */ \ 107 | XX(20, MSEARCH, M-SEARCH) \ 108 | XX(21, NOTIFY, NOTIFY) \ 109 | XX(22, SUBSCRIBE, SUBSCRIBE) \ 110 | XX(23, UNSUBSCRIBE, UNSUBSCRIBE) \ 111 | /* RFC-5789 */ \ 112 | XX(24, PATCH, PATCH) \ 113 | XX(25, PURGE, PURGE) \ 114 | 115 | enum http_method 116 | { 117 | #define XX(num, name, string) HTTP_##name = num, 118 | HTTP_METHOD_MAP(XX) 119 | #undef XX 120 | }; 121 | 122 | 123 | enum http_parser_type { HTTP_REQUEST, HTTP_RESPONSE, HTTP_BOTH }; 124 | 125 | 126 | /* Flag values for http_parser.flags field */ 127 | enum flags 128 | { F_CHUNKED = 1 << 0 129 | , F_CONNECTION_KEEP_ALIVE = 1 << 1 130 | , F_CONNECTION_CLOSE = 1 << 2 131 | , F_TRAILING = 1 << 3 132 | , F_UPGRADE = 1 << 4 133 | , F_SKIPBODY = 1 << 5 134 | }; 135 | 136 | 137 | /* Map for errno-related constants 138 | * 139 | * The provided argument should be a macro that takes 2 arguments. 140 | */ 141 | #define HTTP_ERRNO_MAP(XX) \ 142 | /* No error */ \ 143 | XX(OK, "success") \ 144 | \ 145 | /* Callback-related errors */ \ 146 | XX(CB_message_begin, "the on_message_begin callback failed") \ 147 | XX(CB_url, "the on_url callback failed") \ 148 | XX(CB_header_field, "the on_header_field callback failed") \ 149 | XX(CB_header_value, "the on_header_value callback failed") \ 150 | XX(CB_headers_complete, "the on_headers_complete callback failed") \ 151 | XX(CB_body, "the on_body callback failed") \ 152 | XX(CB_message_complete, "the on_message_complete callback failed") \ 153 | XX(CB_status, "the on_status callback failed") \ 154 | \ 155 | /* Parsing-related errors */ \ 156 | XX(INVALID_EOF_STATE, "stream ended at an unexpected time") \ 157 | XX(HEADER_OVERFLOW, \ 158 | "too many header bytes seen; overflow detected") \ 159 | XX(CLOSED_CONNECTION, \ 160 | "data received after completed connection: close message") \ 161 | XX(INVALID_VERSION, "invalid HTTP version") \ 162 | XX(INVALID_STATUS, "invalid HTTP status code") \ 163 | XX(INVALID_METHOD, "invalid HTTP method") \ 164 | XX(INVALID_URL, "invalid URL") \ 165 | XX(INVALID_HOST, "invalid host") \ 166 | XX(INVALID_PORT, "invalid port") \ 167 | XX(INVALID_PATH, "invalid path") \ 168 | XX(INVALID_QUERY_STRING, "invalid query string") \ 169 | XX(INVALID_FRAGMENT, "invalid fragment") \ 170 | XX(LF_EXPECTED, "LF character expected") \ 171 | XX(INVALID_HEADER_TOKEN, "invalid character in header") \ 172 | XX(INVALID_CONTENT_LENGTH, \ 173 | "invalid character in content-length header") \ 174 | XX(INVALID_CHUNK_SIZE, \ 175 | "invalid character in chunk size header") \ 176 | XX(INVALID_CONSTANT, "invalid constant string") \ 177 | XX(INVALID_INTERNAL_STATE, "encountered unexpected internal state")\ 178 | XX(STRICT, "strict mode assertion failed") \ 179 | XX(PAUSED, "parser is paused") \ 180 | XX(UNKNOWN, "an unknown error occurred") 181 | 182 | 183 | /* Define HPE_* values for each errno value above */ 184 | #define HTTP_ERRNO_GEN(n, s) HPE_##n, 185 | enum http_errno { 186 | HTTP_ERRNO_MAP(HTTP_ERRNO_GEN) 187 | }; 188 | #undef HTTP_ERRNO_GEN 189 | 190 | 191 | /* Get an http_errno value from an http_parser */ 192 | #define HTTP_PARSER_ERRNO(p) ((enum http_errno) (p)->http_errno) 193 | 194 | 195 | struct http_parser { 196 | /** PRIVATE **/ 197 | unsigned int type : 2; /* enum http_parser_type */ 198 | unsigned int flags : 6; /* F_* values from 'flags' enum; semi-public */ 199 | unsigned int state : 8; /* enum state from http_parser.c */ 200 | unsigned int header_state : 8; /* enum header_state from http_parser.c */ 201 | unsigned int index : 8; /* index into current matcher */ 202 | 203 | uint32_t nread; /* # bytes read in various scenarios */ 204 | uint64_t content_length; /* # bytes in body (0 if no Content-Length header) */ 205 | 206 | /** READ-ONLY **/ 207 | unsigned short http_major; 208 | unsigned short http_minor; 209 | unsigned int status_code : 16; /* responses only */ 210 | unsigned int method : 8; /* requests only */ 211 | unsigned int http_errno : 7; 212 | 213 | /* 1 = Upgrade header was present and the parser has exited because of that. 214 | * 0 = No upgrade header present. 215 | * Should be checked when http_parser_execute() returns in addition to 216 | * error checking. 217 | */ 218 | unsigned int upgrade : 1; 219 | 220 | /** PUBLIC **/ 221 | void *data; /* A pointer to get hook to the "connection" or "socket" object */ 222 | }; 223 | 224 | 225 | struct http_parser_settings { 226 | http_cb on_message_begin; 227 | http_data_cb on_url; 228 | http_data_cb on_status; 229 | http_data_cb on_header_field; 230 | http_data_cb on_header_value; 231 | http_cb on_headers_complete; 232 | http_data_cb on_body; 233 | http_cb on_message_complete; 234 | }; 235 | 236 | 237 | enum http_parser_url_fields 238 | { UF_SCHEMA = 0 239 | , UF_HOST = 1 240 | , UF_PORT = 2 241 | , UF_PATH = 3 242 | , UF_QUERY = 4 243 | , UF_FRAGMENT = 5 244 | , UF_USERINFO = 6 245 | , UF_MAX = 7 246 | }; 247 | 248 | 249 | /* Result structure for http_parser_parse_url(). 250 | * 251 | * Callers should index into field_data[] with UF_* values iff field_set 252 | * has the relevant (1 << UF_*) bit set. As a courtesy to clients (and 253 | * because we probably have padding left over), we convert any port to 254 | * a uint16_t. 255 | */ 256 | struct http_parser_url { 257 | uint16_t field_set; /* Bitmask of (1 << UF_*) values */ 258 | uint16_t port; /* Converted UF_PORT string */ 259 | 260 | struct { 261 | uint16_t off; /* Offset into buffer in which field starts */ 262 | uint16_t len; /* Length of run in buffer */ 263 | } field_data[UF_MAX]; 264 | }; 265 | 266 | 267 | /* Returns the library version. Bits 16-23 contain the major version number, 268 | * bits 8-15 the minor version number and bits 0-7 the patch level. 269 | * Usage example: 270 | * 271 | * unsigned long version = http_parser_version(); 272 | * unsigned major = (version >> 16) & 255; 273 | * unsigned minor = (version >> 8) & 255; 274 | * unsigned patch = version & 255; 275 | * printf("http_parser v%u.%u.%u\n", major, minor, version); 276 | */ 277 | unsigned long http_parser_version(void); 278 | 279 | void http_parser_init(http_parser *parser, enum http_parser_type type); 280 | 281 | 282 | size_t http_parser_execute(http_parser *parser, 283 | const http_parser_settings *settings, 284 | const char *data, 285 | size_t len); 286 | 287 | 288 | /* If http_should_keep_alive() in the on_headers_complete or 289 | * on_message_complete callback returns 0, then this should be 290 | * the last message on the connection. 291 | * If you are the server, respond with the "Connection: close" header. 292 | * If you are the client, close the connection. 293 | */ 294 | int http_should_keep_alive(const http_parser *parser); 295 | 296 | /* Returns a string version of the HTTP method. */ 297 | const char *http_method_str(enum http_method m); 298 | 299 | /* Return a string name of the given error */ 300 | const char *http_errno_name(enum http_errno err); 301 | 302 | /* Return a string description of the given error */ 303 | const char *http_errno_description(enum http_errno err); 304 | 305 | /* Parse a URL; return nonzero on failure */ 306 | int http_parser_parse_url(const char *buf, size_t buflen, 307 | int is_connect, 308 | struct http_parser_url *u); 309 | 310 | /* Pause or un-pause the parser; a nonzero value pauses */ 311 | void http_parser_pause(http_parser *parser, int paused); 312 | 313 | /* Checks if this is the final chunk of the body. */ 314 | int http_body_is_final(const http_parser *parser); 315 | 316 | #ifdef __cplusplus 317 | } 318 | #endif 319 | #endif 320 | -------------------------------------------------------------------------------- /slre.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2004-2013 Sergey Lyubka 3 | * Copyright (c) 2013 Cesanta Software Limited 4 | * All rights reserved 5 | * 6 | * This library is dual-licensed: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License version 2 as 8 | * published by the Free Software Foundation. For the terms of this 9 | * license, see . 10 | * 11 | * You are free to use this library under the terms of the GNU General 12 | * Public License, but WITHOUT ANY WARRANTY; without even the implied 13 | * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 14 | * See the GNU General Public License for more details. 15 | * 16 | * Alternatively, you can license this library under a commercial 17 | * license, as set out in . 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | 24 | #include "slre.h" 25 | 26 | #define MAX_BRANCHES 100 27 | #define MAX_BRACKETS 100 28 | #define ARRAY_SIZE(ar) (int) (sizeof(ar) / sizeof((ar)[0])) 29 | #define FAIL_IF(condition, error_code) if (condition) return (error_code) 30 | 31 | #ifdef SLRE_DEBUG 32 | #define DBG(x) printf x 33 | #else 34 | #define DBG(x) 35 | #endif 36 | 37 | struct bracket_pair { 38 | const char *ptr; /* Points to the first char after '(' in regex */ 39 | int len; /* Length of the text between '(' and ')' */ 40 | int branches; /* Index in the branches array for this pair */ 41 | int num_branches; /* Number of '|' in this bracket pair */ 42 | }; 43 | 44 | struct branch { 45 | int bracket_index; /* index for 'struct bracket_pair brackets' */ 46 | /* array defined below */ 47 | const char *schlong; /* points to the '|' character in the regex */ 48 | }; 49 | 50 | struct regex_info { 51 | /* 52 | * Describes all bracket pairs in the regular expression. 53 | * First entry is always present, and grabs the whole regex. 54 | */ 55 | struct bracket_pair brackets[MAX_BRACKETS]; 56 | int num_brackets; 57 | 58 | /* 59 | * Describes alternations ('|' operators) in the regular expression. 60 | * Each branch falls into a specific branch pair. 61 | */ 62 | struct branch branches[MAX_BRANCHES]; 63 | int num_branches; 64 | 65 | /* Array of captures provided by the user */ 66 | struct slre_cap *caps; 67 | int num_caps; 68 | 69 | /* E.g. IGNORE_CASE. See enum below */ 70 | int flags; 71 | }; 72 | enum { IGNORE_CASE = 1 }; 73 | 74 | static int is_metacharacter(const unsigned char *s) { 75 | static const char *metacharacters = "^$().[]*+?|\\Ssd"; 76 | return strchr(metacharacters, *s) != NULL; 77 | } 78 | 79 | static int op_len(const char *re) { 80 | return re[0] == '\\' && re[1] == 'x' ? 4 : re[0] == '\\' ? 2 : 1; 81 | } 82 | 83 | static int set_len(const char *re, int re_len) { 84 | int len = 0; 85 | 86 | while (len < re_len && re[len] != ']') { 87 | len += op_len(re + len); 88 | } 89 | 90 | return len <= re_len ? len + 1 : -1; 91 | } 92 | 93 | static int get_op_len(const char *re, int re_len) { 94 | return re[0] == '[' ? set_len(re + 1, re_len - 1) + 1 : op_len(re); 95 | } 96 | 97 | static int is_quantifier(const char *re) { 98 | return re[0] == '*' || re[0] == '+' || re[0] == '?'; 99 | } 100 | 101 | static int toi(int x) { 102 | return isdigit(x) ? x - '0' : x - 'W'; 103 | } 104 | 105 | static int hextoi(const unsigned char *s) { 106 | return (toi(tolower(s[0])) << 4) | toi(tolower(s[1])); 107 | } 108 | 109 | static int match_op(const unsigned char *re, const unsigned char *s, 110 | struct regex_info *info) { 111 | int result = 0; 112 | switch (*re) { 113 | case '\\': 114 | /* Metacharacters */ 115 | switch (re[1]) { 116 | case 'S': 117 | FAIL_IF(isspace(*s), SLRE_NO_MATCH); 118 | result++; 119 | break; 120 | 121 | case 's': 122 | FAIL_IF(!isspace(*s), SLRE_NO_MATCH); 123 | result++; 124 | break; 125 | 126 | case 'd': 127 | FAIL_IF(!isdigit(*s), SLRE_NO_MATCH); 128 | result++; 129 | break; 130 | 131 | case 'x': 132 | /* Match byte, \xHH where HH is hexadecimal byte representaion */ 133 | FAIL_IF(hextoi(re + 2) != *s, SLRE_NO_MATCH); 134 | result++; 135 | break; 136 | 137 | default: 138 | /* Valid metacharacter check is done in bar() */ 139 | FAIL_IF(re[1] != s[0], SLRE_NO_MATCH); 140 | result++; 141 | break; 142 | } 143 | break; 144 | 145 | case '|': FAIL_IF(1, SLRE_INTERNAL_ERROR); break; 146 | case '$': FAIL_IF(1, SLRE_NO_MATCH); break; 147 | case '.': result++; break; 148 | 149 | default: 150 | if (info->flags & IGNORE_CASE) { 151 | FAIL_IF(tolower(*re) != tolower(*s), SLRE_NO_MATCH); 152 | } else { 153 | FAIL_IF(*re != *s, SLRE_NO_MATCH); 154 | } 155 | result++; 156 | break; 157 | } 158 | 159 | return result; 160 | } 161 | 162 | static int match_set(const char *re, int re_len, const char *s, 163 | struct regex_info *info) { 164 | int len = 0, result = -1, invert = re[0] == '^'; 165 | 166 | if (invert) re++, re_len--; 167 | 168 | while (len <= re_len && re[len] != ']' && result <= 0) { 169 | /* Support character range */ 170 | if (re[len] != '-' && re[len + 1] == '-' && re[len + 2] != ']' && 171 | re[len + 2] != '\0') { 172 | result = info->flags && IGNORE_CASE ? 173 | *s >= re[len] && *s <= re[len + 2] : 174 | tolower(*s) >= tolower(re[len]) && tolower(*s) <= tolower(re[len + 2]); 175 | len += 3; 176 | } else { 177 | result = match_op((unsigned char *) re + len, (unsigned char *) s, info); 178 | len += op_len(re + len); 179 | } 180 | } 181 | return (!invert && result > 0) || (invert && result <= 0) ? 1 : -1; 182 | } 183 | 184 | static int doh(const char *s, int s_len, struct regex_info *info, int bi); 185 | 186 | static int bar(const char *re, int re_len, const char *s, int s_len, 187 | struct regex_info *info, int bi) { 188 | /* i is offset in re, j is offset in s, bi is brackets index */ 189 | int i, j, n = 0, step; 190 | 191 | for (i = j = 0; i < re_len && j <= s_len; i += step) { 192 | 193 | /* Handle quantifiers. Get the length of the chunk. */ 194 | step = re[i] == '(' ? info->brackets[bi + 1].len + 2 : 195 | get_op_len(re + i, re_len - i); 196 | 197 | DBG(("%s [%.*s] [%.*s] re_len=%d step=%d i=%d j=%d\n", __func__, 198 | re_len - i, re + i, s_len - j, s + j, re_len, step, i, j)); 199 | 200 | FAIL_IF(is_quantifier(&re[i]), SLRE_UNEXPECTED_QUANTIFIER); 201 | FAIL_IF(step <= 0, SLRE_INVALID_CHARACTER_SET); 202 | 203 | if (i + step < re_len && is_quantifier(re + i + step)) { 204 | DBG(("QUANTIFIER: [%.*s]%c [%.*s]\n", step, re + i, 205 | re[i + step], s_len - j, s + j)); 206 | if (re[i + step] == '?') { 207 | int result = bar(re + i, step, s + j, s_len - j, info, bi); 208 | j += result > 0 ? result : 0; 209 | i++; 210 | } else if (re[i + step] == '+' || re[i + step] == '*') { 211 | int j2 = j, nj = j, n1, n2 = -1, ni, non_greedy = 0; 212 | 213 | /* Points to the regexp code after the quantifier */ 214 | ni = i + step + 1; 215 | if (ni < re_len && re[ni] == '?') { 216 | non_greedy = 1; 217 | ni++; 218 | } 219 | 220 | do { 221 | if ((n1 = bar(re + i, step, s + j2, s_len - j2, info, bi)) > 0) { 222 | j2 += n1; 223 | } 224 | if (re[i + step] == '+' && n1 < 0) break; 225 | 226 | if (ni >= re_len) { 227 | /* After quantifier, there is nothing */ 228 | nj = j2; 229 | } else if ((n2 = bar(re + ni, re_len - ni, s + j2, 230 | s_len - j2, info, bi)) >= 0) { 231 | /* Regex after quantifier matched */ 232 | nj = j2 + n2; 233 | } 234 | if (nj > j && non_greedy) break; 235 | } while (n1 > 0); 236 | 237 | if (n1 < 0 && re[i + step] == '*' && 238 | (n2 = bar(re + ni, re_len - ni, s + j, s_len - j, info, bi)) > 0) { 239 | nj = j + n2; 240 | } 241 | 242 | DBG(("STAR/PLUS END: %d %d %d %d %d\n", j, nj, re_len - ni, n1, n2)); 243 | FAIL_IF(re[i + step] == '+' && nj == j, SLRE_NO_MATCH); 244 | 245 | /* If while loop body above was not executed for the * quantifier, */ 246 | /* make sure the rest of the regex matches */ 247 | FAIL_IF(nj == j && ni < re_len && n2 < 0, SLRE_NO_MATCH); 248 | 249 | /* Returning here cause we've matched the rest of RE already */ 250 | return nj; 251 | } 252 | continue; 253 | } 254 | 255 | if (re[i] == '[') { 256 | n = match_set(re + i + 1, re_len - (i + 2), s + j, info); 257 | DBG(("SET %.*s [%.*s] -> %d\n", step, re + i, s_len - j, s + j, n)); 258 | FAIL_IF(n <= 0, SLRE_NO_MATCH); 259 | j += n; 260 | } else if (re[i] == '(') { 261 | bi++; 262 | FAIL_IF(bi >= info->num_brackets, SLRE_INTERNAL_ERROR); 263 | DBG(("CAPTURING [%.*s] [%.*s] [%s]\n", 264 | step, re + i, s_len - j, s + j, re + i + step)); 265 | 266 | if (re_len - (i + step) <= 0) { 267 | /* Nothing follows brackets */ 268 | n = doh(s + j, s_len - j, info, bi); 269 | } else { 270 | int j2; 271 | for (j2 = 0; j2 < s_len - j; j2++) { 272 | if ((n = doh(s + j, s_len - (j + j2), info, bi)) >= 0 && 273 | bar(re + i + step, re_len - (i + step), 274 | s + j + n, s_len - (j + n), info, bi) >= 0) break; 275 | } 276 | } 277 | 278 | DBG(("CAPTURED [%.*s] [%.*s]:%d\n", step, re + i, s_len - j, s + j, n)); 279 | FAIL_IF(n < 0, n); 280 | if (info->caps != NULL) { 281 | info->caps[bi - 1].ptr = s + j; 282 | info->caps[bi - 1].len = n; 283 | } 284 | j += n; 285 | } else if (re[i] == '^') { 286 | FAIL_IF(j != 0, SLRE_NO_MATCH); 287 | } else if (re[i] == '$') { 288 | FAIL_IF(j != s_len, SLRE_NO_MATCH); 289 | } else { 290 | FAIL_IF(j >= s_len, SLRE_NO_MATCH); 291 | n = match_op((unsigned char *) (re + i), (unsigned char *) (s + j), info); 292 | FAIL_IF(n <= 0, n); 293 | j += n; 294 | } 295 | } 296 | 297 | return j; 298 | } 299 | 300 | /* Process branch points */ 301 | static int doh(const char *s, int s_len, struct regex_info *info, int bi) { 302 | const struct bracket_pair *b = &info->brackets[bi]; 303 | int i = 0, len, result; 304 | const char *p; 305 | 306 | do { 307 | p = i == 0 ? b->ptr : info->branches[b->branches + i - 1].schlong + 1; 308 | len = b->num_branches == 0 ? b->len : 309 | i == b->num_branches ? b->ptr + b->len - p : 310 | info->branches[b->branches + i].schlong - p; 311 | DBG(("%s %d %d [%.*s] [%.*s]\n", __func__, bi, i, len, p, s_len, s)); 312 | result = bar(p, len, s, s_len, info, bi); 313 | DBG(("%s <- %d\n", __func__, result)); 314 | } while (result <= 0 && i++ < b->num_branches); /* At least 1 iteration */ 315 | 316 | return result; 317 | } 318 | 319 | static int baz(const char *s, int s_len, struct regex_info *info) { 320 | int i, result = -1, is_anchored = info->brackets[0].ptr[0] == '^'; 321 | 322 | for (i = 0; i <= s_len; i++) { 323 | result = doh(s + i, s_len - i, info, 0); 324 | if (result >= 0) { 325 | result += i; 326 | break; 327 | } 328 | if (is_anchored) break; 329 | } 330 | 331 | return result; 332 | } 333 | 334 | static void setup_branch_points(struct regex_info *info) { 335 | int i, j; 336 | struct branch tmp; 337 | 338 | /* First, sort branches. Must be stable, no qsort. Use bubble algo. */ 339 | for (i = 0; i < info->num_branches; i++) { 340 | for (j = i + 1; j < info->num_branches; j++) { 341 | if (info->branches[i].bracket_index > info->branches[j].bracket_index) { 342 | tmp = info->branches[i]; 343 | info->branches[i] = info->branches[j]; 344 | info->branches[j] = tmp; 345 | } 346 | } 347 | } 348 | 349 | /* 350 | * For each bracket, set their branch points. This way, for every bracket 351 | * (i.e. every chunk of regex) we know all branch points before matching. 352 | */ 353 | for (i = j = 0; i < info->num_brackets; i++) { 354 | info->brackets[i].num_branches = 0; 355 | info->brackets[i].branches = j; 356 | while (j < info->num_branches && info->branches[j].bracket_index == i) { 357 | info->brackets[i].num_branches++; 358 | j++; 359 | } 360 | } 361 | } 362 | 363 | static int foo(const char *re, int re_len, const char *s, int s_len, 364 | struct regex_info *info) { 365 | int i, step, depth = 0; 366 | 367 | /* First bracket captures everything */ 368 | info->brackets[0].ptr = re; 369 | info->brackets[0].len = re_len; 370 | info->num_brackets = 1; 371 | 372 | /* Make a single pass over regex string, memorize brackets and branches */ 373 | for (i = 0; i < re_len; i += step) { 374 | step = get_op_len(re + i, re_len - i); 375 | 376 | if (re[i] == '|') { 377 | FAIL_IF(info->num_branches >= ARRAY_SIZE(info->branches), 378 | SLRE_TOO_MANY_BRANCHES); 379 | info->branches[info->num_branches].bracket_index = 380 | info->brackets[info->num_brackets - 1].len == -1 ? 381 | info->num_brackets - 1 : depth; 382 | info->branches[info->num_branches].schlong = &re[i]; 383 | info->num_branches++; 384 | } else if (re[i] == '\\') { 385 | FAIL_IF(i >= re_len - 1, SLRE_INVALID_METACHARACTER); 386 | if (re[i + 1] == 'x') { 387 | /* Hex digit specification must follow */ 388 | FAIL_IF(re[i + 1] == 'x' && i >= re_len - 3, 389 | SLRE_INVALID_METACHARACTER); 390 | FAIL_IF(re[i + 1] == 'x' && !(isxdigit(re[i + 2]) && 391 | isxdigit(re[i + 3])), SLRE_INVALID_METACHARACTER); 392 | } else { 393 | FAIL_IF(!is_metacharacter((unsigned char *) re + i + 1), 394 | SLRE_INVALID_METACHARACTER); 395 | } 396 | } else if (re[i] == '(') { 397 | FAIL_IF(info->num_brackets >= ARRAY_SIZE(info->brackets), 398 | SLRE_TOO_MANY_BRACKETS); 399 | depth++; /* Order is important here. Depth increments first. */ 400 | info->brackets[info->num_brackets].ptr = re + i + 1; 401 | info->brackets[info->num_brackets].len = -1; 402 | info->num_brackets++; 403 | FAIL_IF(info->num_caps > 0 && info->num_brackets - 1 > info->num_caps, 404 | SLRE_CAPS_ARRAY_TOO_SMALL); 405 | } else if (re[i] == ')') { 406 | int ind = info->brackets[info->num_brackets - 1].len == -1 ? 407 | info->num_brackets - 1 : depth; 408 | info->brackets[ind].len = &re[i] - info->brackets[ind].ptr; 409 | DBG(("SETTING BRACKET %d [%.*s]\n", 410 | ind, info->brackets[ind].len, info->brackets[ind].ptr)); 411 | depth--; 412 | FAIL_IF(depth < 0, SLRE_UNBALANCED_BRACKETS); 413 | FAIL_IF(i > 0 && re[i - 1] == '(', SLRE_NO_MATCH); 414 | } 415 | } 416 | 417 | FAIL_IF(depth != 0, SLRE_UNBALANCED_BRACKETS); 418 | setup_branch_points(info); 419 | 420 | return baz(s, s_len, info); 421 | } 422 | 423 | int slre_match(const char *regexp, const char *s, int s_len, 424 | struct slre_cap *caps, int num_caps) { 425 | struct regex_info info; 426 | 427 | /* Initialize info structure */ 428 | info.flags = info.num_brackets = info.num_branches = 0; 429 | info.num_caps = num_caps; 430 | info.caps = caps; 431 | 432 | DBG(("========================> [%s] [%.*s]\n", regexp, s_len, s)); 433 | 434 | /* Handle regexp flags. At the moment, only 'i' is supported */ 435 | if (memcmp(regexp, "(?i)", 4) == 0) { 436 | info.flags |= IGNORE_CASE; 437 | regexp += 4; 438 | } 439 | 440 | return foo(regexp, strlen(regexp), s, s_len, &info); 441 | } 442 | -------------------------------------------------------------------------------- /slre.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2004-2013 Sergey Lyubka 3 | * Copyright (c) 2013 Cesanta Software Limited 4 | * All rights reserved 5 | * 6 | * This library is dual-licensed: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License version 2 as 8 | * published by the Free Software Foundation. For the terms of this 9 | * license, see . 10 | * 11 | * You are free to use this library under the terms of the GNU General 12 | * Public License, but WITHOUT ANY WARRANTY; without even the implied 13 | * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 14 | * See the GNU General Public License for more details. 15 | * 16 | * Alternatively, you can license this library under a commercial 17 | * license, as set out in . 18 | */ 19 | 20 | /* 21 | * This is a regular expression library that implements a subset of Perl RE. 22 | * Please refer to README.md for a detailed reference. 23 | */ 24 | 25 | #ifndef SLRE_HEADER_DEFINED 26 | #define SLRE_HEADER_DEFINED 27 | 28 | #ifdef __cplusplus 29 | extern "C" { 30 | #endif 31 | 32 | struct slre_cap { 33 | const char *ptr; 34 | int len; 35 | }; 36 | 37 | int slre_match(const char *regexp, const char *buf, int buf_len, 38 | struct slre_cap *caps, int num_caps); 39 | 40 | /* slre_match() failure codes */ 41 | #define SLRE_NO_MATCH -1 42 | #define SLRE_UNEXPECTED_QUANTIFIER -2 43 | #define SLRE_UNBALANCED_BRACKETS -3 44 | #define SLRE_INTERNAL_ERROR -4 45 | #define SLRE_INVALID_CHARACTER_SET -5 46 | #define SLRE_INVALID_METACHARACTER -6 47 | #define SLRE_CAPS_ARRAY_TOO_SMALL -7 48 | #define SLRE_TOO_MANY_BRANCHES -8 49 | #define SLRE_TOO_MANY_BRACKETS -9 50 | 51 | #ifdef __cplusplus 52 | } 53 | #endif 54 | 55 | #endif /* SLRE_HEADER_DEFINED */ 56 | -------------------------------------------------------------------------------- /spark.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sparkcore-local-http-server-rest-json", 3 | "version": "0.1.1", 4 | "author": "CaptainIgloo ", 5 | "license": "GPLv3", 6 | "description": "This project implement a Http server on Sparkcore itself. Once compiled into the Web IDE, the server will be available at http://IP-SPARKCORE (port 80 by default)." 7 | 8 | } 9 | --------------------------------------------------------------------------------