|
1 | | -#include <Arduino.h> |
2 | 1 | #include "Client.h" |
| 2 | +#include "RestClient.h" |
3 | 3 |
|
| 4 | +//#define HTTP_DEBUG true |
| 5 | +#ifdef HTTP_DEBUG |
| 6 | +#define HTTP_DEBUG_PRINT(string) (Serial.print(string)) |
| 7 | +#endif |
4 | 8 |
|
5 | | -class RestClient { |
6 | | - |
7 | | - public: |
8 | | - RestClient(Client& netClient, const char* _host); |
9 | | - RestClient(Client& netClient, const char* _host, int _port); |
10 | | - |
11 | | - //Generic HTTP Request |
12 | | - int request(const char* method, String path, String body); |
13 | | - // Set a Request Header |
14 | | - void setHeader(String); |
15 | | - // Set Content-Type Header |
16 | | - void setContentType(String); |
17 | | - |
18 | | - // GET path |
19 | | - int get(const char*); |
20 | | - |
21 | | - // POST path and body |
22 | | - int post(const char* path, const char* body); |
23 | | - |
24 | | - // PUT path and body |
25 | | - int put(const char* path, const char* body); |
26 | | - |
27 | | - // DELETE path |
28 | | - int del(const char*); |
29 | | - // DELETE path and body |
30 | | - int del(const char*, const char*); |
31 | | - // get HTTP response: |
32 | | - String readResponse(){ return responseBody;}; |
33 | | - // TO DO: add cookie functions |
34 | | - |
35 | | - private: |
36 | | - Client* client; |
37 | | - int getResponse(); |
38 | | - // void write(const char*); |
39 | | - const char* host; |
40 | | - int port; |
41 | | - int num_headers; |
42 | | - String headers[10]; |
43 | | - String contentType; |
44 | | - String responseBody; // body of the HTTP response |
45 | | - long requestStart; // time the request started |
46 | | - int timeout; // timeout to avoid blocking |
47 | | -}; |
| 9 | +#ifndef HTTP_DEBUG |
| 10 | +#define HTTP_DEBUG_PRINT(string) |
| 11 | +#endif |
| 12 | + |
| 13 | + |
| 14 | +RestClient::RestClient(Client& netClient, const char* _host) { |
| 15 | + host = _host; |
| 16 | + port = 80; |
| 17 | + num_headers = 0; |
| 18 | + contentType = "x-www-form-urlencoded"; // default |
| 19 | + this->client = &netClient; |
| 20 | + this->responseBody = ""; |
| 21 | + this->timeout = 1000; // default. TODO: add a setter function |
| 22 | +} |
| 23 | + |
| 24 | +RestClient::RestClient(Client& netClient, const char* _host, int _port) { |
| 25 | + host = _host; |
| 26 | + port = _port; |
| 27 | + num_headers = 0; |
| 28 | + contentType = "application/x-www-form-urlencoded"; // default |
| 29 | + this->client = &netClient; |
| 30 | +} |
| 31 | + |
| 32 | +// GET path |
| 33 | +int RestClient::get(const char* path){ |
| 34 | + return request("GET", path, ""); |
| 35 | +} |
| 36 | + |
| 37 | +// POST path and body |
| 38 | +int RestClient::post(const char* path, String body){ |
| 39 | + return request("POST", path, body); |
| 40 | +} |
| 41 | + |
| 42 | +// PUT path and body |
| 43 | +int RestClient::put(const char* path, String body){ |
| 44 | + return request("PUT", path, body); |
| 45 | +} |
| 46 | + |
| 47 | +// DELETE path |
| 48 | +int RestClient::del(const char* path){ |
| 49 | + return request("DELETE", path, ""); |
| 50 | +} |
| 51 | + |
| 52 | +// DELETE path and body |
| 53 | +int RestClient::del(const char* path, String body ){ |
| 54 | + return request("DELETE", path, body); |
| 55 | +} |
| 56 | + |
| 57 | +void RestClient::setHeader(String header){ |
| 58 | + headers[num_headers] = header; |
| 59 | + num_headers++; |
| 60 | +} |
| 61 | + |
| 62 | +void RestClient::setContentType(String contentTypeValue){ |
| 63 | + contentType = contentTypeValue; |
| 64 | +} |
| 65 | + |
| 66 | +// The mother- generic request method. |
| 67 | +// |
| 68 | +int RestClient::request(const char* method, String path, String body){ |
| 69 | + |
| 70 | + HTTP_DEBUG_PRINT("HTTP: connect\n"); |
| 71 | + |
| 72 | + if(client->connect(host, port)){ |
| 73 | + HTTP_DEBUG_PRINT("HTTP: connected\n"); |
| 74 | + HTTP_DEBUG_PRINT("REQUEST: \n"); |
| 75 | + // Make a HTTP request line: |
| 76 | + String requestString = method; |
| 77 | + requestString += " "; |
| 78 | + requestString += path; |
| 79 | + requestString += " HTTP/1.1"; |
| 80 | + requestString += "\n"; |
| 81 | + for(int i=0; i<num_headers; i++){ |
| 82 | + requestString += headers[i]; |
| 83 | + requestString += "\n"; |
| 84 | + } |
| 85 | + requestString += "Host: "; |
| 86 | + requestString += host; |
| 87 | + requestString += "\n"; |
| 88 | + requestString += "Connection: close"; |
| 89 | + requestString += "\n"; |
| 90 | + //TODO: deal with binary content |
| 91 | + if(body != ""){ |
| 92 | + requestString += "Content-Length: "; |
| 93 | + requestString += body.length(); |
| 94 | + requestString += "\n"; |
| 95 | + requestString += "Content-Type: "; |
| 96 | + requestString += contentType; |
| 97 | + requestString += "\n\n"; |
| 98 | + requestString += body; |
| 99 | + requestString += "\n\n"; |
| 100 | + } |
| 101 | + client->print(requestString); |
| 102 | + |
| 103 | + // make sure you've sent all bytes. Ugly hack. |
| 104 | + // TODO: check output buffer instead? |
| 105 | + delay(50); |
| 106 | + |
| 107 | + HTTP_DEBUG_PRINT("HTTP: call getResponse\n"); |
| 108 | + int statusCode = getResponse(); |
| 109 | + HTTP_DEBUG_PRINT("HTTP: return getResponse\n"); |
| 110 | + |
| 111 | + //cleanup |
| 112 | + // only stop if server disconnected. Otherwise you can |
| 113 | + // get in an infinite loop in getResponse: |
| 114 | + if (!client->connected()) { |
| 115 | + HTTP_DEBUG_PRINT("HTTP: stop client\n"); |
| 116 | + num_headers = 0; |
| 117 | + //client->stop(); |
| 118 | + HTTP_DEBUG_PRINT("HTTP: client stopped\n"); |
| 119 | + } |
| 120 | + return statusCode; |
| 121 | + }else{ |
| 122 | + HTTP_DEBUG_PRINT("HTTP Connection failed\n"); |
| 123 | + return 0; |
| 124 | + } |
| 125 | +} |
| 126 | + |
| 127 | +int RestClient::getResponse() { |
| 128 | + this->requestStart = millis(); |
| 129 | + // an http request ends with a blank line |
| 130 | + boolean currentLineIsBlank = true; |
| 131 | + boolean httpBody = false; |
| 132 | + boolean inStatus = false; |
| 133 | + // clear response string: |
| 134 | + this->responseBody = ""; |
| 135 | + |
| 136 | + char statusCode[4]; |
| 137 | + int i = 0; |
| 138 | + int code = 0; |
| 139 | + |
| 140 | + HTTP_DEBUG_PRINT("HTTP: RESPONSE: \n"); |
| 141 | + while (client->connected()) { |
| 142 | + // HTTP_DEBUG_PRINT("."); |
| 143 | + if (client->available()) { |
| 144 | + // HTTP_DEBUG_PRINT(","); |
| 145 | + |
| 146 | + char c = client->read(); |
| 147 | + HTTP_DEBUG_PRINT(c); |
| 148 | + |
| 149 | + if(c == ' ' && !inStatus){ |
| 150 | + inStatus = true; |
| 151 | + } |
| 152 | + |
| 153 | + if(inStatus && i < 3 && c != ' '){ |
| 154 | + statusCode[i] = c; |
| 155 | + i++; |
| 156 | + } |
| 157 | + if(i == 3){ |
| 158 | + statusCode[i] = '\0'; |
| 159 | + code = atoi(statusCode); |
| 160 | + } |
| 161 | + |
| 162 | + if(httpBody){ |
| 163 | + // add this char tot the responseBody |
| 164 | + this->responseBody += c; |
| 165 | + } |
| 166 | + else |
| 167 | + { |
| 168 | + if (c == '\n' && currentLineIsBlank) { |
| 169 | + httpBody = true; |
| 170 | + } |
| 171 | + |
| 172 | + if (c == '\n') { |
| 173 | + // you're starting a new line |
| 174 | + currentLineIsBlank = true; |
| 175 | + } |
| 176 | + else if (c != '\r') { |
| 177 | + // you've gotten a character on the current line |
| 178 | + currentLineIsBlank = false; |
| 179 | + } |
| 180 | + } |
| 181 | + } else { |
| 182 | + // there is a condition where client connects |
| 183 | + // and available() always <= 0. So timeout is here to catch that: |
| 184 | + if (millis() - this->requestStart > this->timeout) return 0; |
| 185 | + } |
| 186 | + } |
| 187 | + |
| 188 | + HTTP_DEBUG_PRINT("HTTP: return getResponse3\n"); |
| 189 | + return code; |
| 190 | +} |
0 commit comments