ARM mbed M2X API Client: The ARM mbed client library is used to send/receive data to/from AT&T's M2X service from mbed LPC1768 microcontrollers.

Dependents:   m2x-demo-all M2X_MTS_ACCEL_DEMO M2X_MTS_Accel M2X_K64F_ACCEL ... more

Committer:
citrusbyte
Date:
Sat Jan 02 02:29:43 2016 +0000
Revision:
21:6878944d2ce2
Parent:
19:4dfa28d37b8f
Update library version

Who changed what in which revision?

UserRevisionLine numberNew contents of line
jb8414 0:f479e4f4db0e 1 #include "M2XStreamClient.h"
jb8414 0:f479e4f4db0e 2
citrusbyte 19:4dfa28d37b8f 3 #include <jsonlite.h>
citrusbyte 19:4dfa28d37b8f 4
citrusbyte 19:4dfa28d37b8f 5 #include "StreamParseFunctions.h"
citrusbyte 19:4dfa28d37b8f 6 #include "LocationParseFunctions.h"
citrusbyte 19:4dfa28d37b8f 7
citrusbyte 19:4dfa28d37b8f 8 const char* M2XStreamClient::kDefaultM2XHost = "api-m2x.att.com";
citrusbyte 19:4dfa28d37b8f 9
citrusbyte 19:4dfa28d37b8f 10 static int write_delete_values(Print* print, const char* from, const char* end);
citrusbyte 19:4dfa28d37b8f 11 int print_encoded_string(Print* print, const char* str);
citrusbyte 19:4dfa28d37b8f 12 int tolower(int ch);
citrusbyte 19:4dfa28d37b8f 13
citrusbyte 19:4dfa28d37b8f 14 #if defined(ARDUINO_PLATFORM) || defined(MBED_PLATFORM)
citrusbyte 19:4dfa28d37b8f 15 int tolower(int ch)
citrusbyte 19:4dfa28d37b8f 16 {
citrusbyte 19:4dfa28d37b8f 17 // Arduino and mbed use ASCII table, so we can simplify the implementation
citrusbyte 19:4dfa28d37b8f 18 if ((ch >= 'A') && (ch <= 'Z')) {
citrusbyte 19:4dfa28d37b8f 19 return (ch + 32);
citrusbyte 19:4dfa28d37b8f 20 }
citrusbyte 19:4dfa28d37b8f 21 return ch;
citrusbyte 19:4dfa28d37b8f 22 }
citrusbyte 19:4dfa28d37b8f 23 #else
citrusbyte 19:4dfa28d37b8f 24 // For other platform, we use libc's tolower by default
citrusbyte 19:4dfa28d37b8f 25 #include <ctype.h>
citrusbyte 19:4dfa28d37b8f 26 #endif
jb8414 0:f479e4f4db0e 27
citrusbyte 19:4dfa28d37b8f 28 M2XStreamClient::M2XStreamClient(Client* client,
citrusbyte 19:4dfa28d37b8f 29 const char* key,
citrusbyte 19:4dfa28d37b8f 30 int case_insensitive,
citrusbyte 19:4dfa28d37b8f 31 const char* host,
citrusbyte 19:4dfa28d37b8f 32 int port,
citrusbyte 19:4dfa28d37b8f 33 const char* path_prefix) : _client(client),
citrusbyte 19:4dfa28d37b8f 34 _key(key),
citrusbyte 19:4dfa28d37b8f 35 _case_insensitive(case_insensitive),
citrusbyte 19:4dfa28d37b8f 36 _host(host),
citrusbyte 19:4dfa28d37b8f 37 _port(port),
citrusbyte 19:4dfa28d37b8f 38 _path_prefix(path_prefix),
citrusbyte 19:4dfa28d37b8f 39 _null_print() {
citrusbyte 19:4dfa28d37b8f 40 }
citrusbyte 19:4dfa28d37b8f 41
citrusbyte 19:4dfa28d37b8f 42 int M2XStreamClient::listStreamValues(const char* deviceId, const char* streamName,
citrusbyte 19:4dfa28d37b8f 43 stream_value_read_callback callback, void* context,
citrusbyte 19:4dfa28d37b8f 44 const char* query) {
citrusbyte 19:4dfa28d37b8f 45 if (_client->connect(_host, _port)) {
citrusbyte 19:4dfa28d37b8f 46 DBGLN("%s", "Connected to M2X server!");
citrusbyte 19:4dfa28d37b8f 47 _client->print("GET ");
citrusbyte 19:4dfa28d37b8f 48 if (_path_prefix)
citrusbyte 19:4dfa28d37b8f 49 _client->print(_path_prefix);
citrusbyte 19:4dfa28d37b8f 50 _client->print("/v2/devices/");
citrusbyte 19:4dfa28d37b8f 51 print_encoded_string(_client, deviceId);
citrusbyte 19:4dfa28d37b8f 52 _client->print("/streams/");
citrusbyte 19:4dfa28d37b8f 53 print_encoded_string(_client, streamName);
citrusbyte 19:4dfa28d37b8f 54 _client->print("/values.json");
citrusbyte 19:4dfa28d37b8f 55
citrusbyte 19:4dfa28d37b8f 56 if (query) {
citrusbyte 19:4dfa28d37b8f 57 if (query[0] != '?') {
citrusbyte 19:4dfa28d37b8f 58 _client->print('?');
citrusbyte 19:4dfa28d37b8f 59 }
citrusbyte 19:4dfa28d37b8f 60 _client->print(query);
citrusbyte 19:4dfa28d37b8f 61 }
citrusbyte 19:4dfa28d37b8f 62
citrusbyte 19:4dfa28d37b8f 63 _client->println(" HTTP/1.0");
citrusbyte 19:4dfa28d37b8f 64 writeHttpHeader(-1);
citrusbyte 19:4dfa28d37b8f 65 } else {
citrusbyte 19:4dfa28d37b8f 66 DBGLN("%s", "ERROR: Cannot connect to M2X server!");
citrusbyte 19:4dfa28d37b8f 67 return E_NOCONNECTION;
citrusbyte 19:4dfa28d37b8f 68 }
citrusbyte 19:4dfa28d37b8f 69 int status = readStatusCode(false);
citrusbyte 19:4dfa28d37b8f 70 if (status == 200) {
citrusbyte 19:4dfa28d37b8f 71 readStreamValue(callback, context);
citrusbyte 19:4dfa28d37b8f 72 }
citrusbyte 19:4dfa28d37b8f 73
citrusbyte 19:4dfa28d37b8f 74 close();
citrusbyte 19:4dfa28d37b8f 75 return status;
jb8414 0:f479e4f4db0e 76 }
jb8414 0:f479e4f4db0e 77
citrusbyte 19:4dfa28d37b8f 78 int M2XStreamClient::readLocation(const char* deviceId,
citrusbyte 19:4dfa28d37b8f 79 location_read_callback callback,
citrusbyte 19:4dfa28d37b8f 80 void* context) {
citrusbyte 19:4dfa28d37b8f 81 if (_client->connect(_host, _port)) {
citrusbyte 19:4dfa28d37b8f 82 DBGLN("%s", "Connected to M2X server!");
citrusbyte 19:4dfa28d37b8f 83 _client->print("GET ");
citrusbyte 19:4dfa28d37b8f 84 if (_path_prefix)
citrusbyte 19:4dfa28d37b8f 85 _client->print(_path_prefix);
citrusbyte 19:4dfa28d37b8f 86 _client->print("/v2/devices/");
citrusbyte 19:4dfa28d37b8f 87 print_encoded_string(_client, deviceId);
citrusbyte 19:4dfa28d37b8f 88 _client->println("/location HTTP/1.0");
citrusbyte 19:4dfa28d37b8f 89
citrusbyte 19:4dfa28d37b8f 90 writeHttpHeader(-1);
citrusbyte 19:4dfa28d37b8f 91 } else {
citrusbyte 19:4dfa28d37b8f 92 DBGLN("%s", "ERROR: Cannot connect to M2X server!");
citrusbyte 19:4dfa28d37b8f 93 return E_NOCONNECTION;
citrusbyte 19:4dfa28d37b8f 94 }
citrusbyte 19:4dfa28d37b8f 95 int status = readStatusCode(false);
citrusbyte 19:4dfa28d37b8f 96 if (status == 200) {
citrusbyte 19:4dfa28d37b8f 97 readLocation(callback, context);
citrusbyte 19:4dfa28d37b8f 98 }
citrusbyte 19:4dfa28d37b8f 99
citrusbyte 19:4dfa28d37b8f 100 close();
citrusbyte 19:4dfa28d37b8f 101 return status;
citrusbyte 19:4dfa28d37b8f 102 }
citrusbyte 19:4dfa28d37b8f 103
citrusbyte 19:4dfa28d37b8f 104 int M2XStreamClient::deleteValues(const char* deviceId, const char* streamName,
citrusbyte 19:4dfa28d37b8f 105 const char* from, const char* end) {
citrusbyte 19:4dfa28d37b8f 106 if (_client->connect(_host, _port)) {
citrusbyte 19:4dfa28d37b8f 107 DBGLN("%s", "Connected to M2X server!");
citrusbyte 19:4dfa28d37b8f 108 int length = write_delete_values(&_null_print, from, end);
citrusbyte 19:4dfa28d37b8f 109 writeDeleteHeader(deviceId, streamName, length);
citrusbyte 19:4dfa28d37b8f 110 write_delete_values(_client, from, end);
citrusbyte 19:4dfa28d37b8f 111 } else {
citrusbyte 19:4dfa28d37b8f 112 DBGLN("%s", "ERROR: Cannot connect to M2X server!");
citrusbyte 19:4dfa28d37b8f 113 return E_NOCONNECTION;
citrusbyte 19:4dfa28d37b8f 114 }
citrusbyte 19:4dfa28d37b8f 115
citrusbyte 19:4dfa28d37b8f 116 return readStatusCode(true);
citrusbyte 10:4ce9eba38dbe 117 }
citrusbyte 10:4ce9eba38dbe 118
citrusbyte 19:4dfa28d37b8f 119 int M2XStreamClient::getTimestamp32(int32_t *ts) {
citrusbyte 19:4dfa28d37b8f 120 // The maximum value of signed 64-bit integer is 0x7fffffffffffffff,
citrusbyte 19:4dfa28d37b8f 121 // which is 9223372036854775807. It consists of 19 characters, so a
citrusbyte 19:4dfa28d37b8f 122 // buffer of 20 is definitely enough here
citrusbyte 19:4dfa28d37b8f 123 int length = 20;
citrusbyte 19:4dfa28d37b8f 124 char buffer[20];
citrusbyte 19:4dfa28d37b8f 125 int status = getTimestamp(buffer, &length);
citrusbyte 19:4dfa28d37b8f 126 if (status == 200) {
citrusbyte 19:4dfa28d37b8f 127 int32_t result = 0;
citrusbyte 19:4dfa28d37b8f 128 for (int i = 0; i < length; i++) {
citrusbyte 19:4dfa28d37b8f 129 result = result * 10 + (buffer[i] - '0');
citrusbyte 19:4dfa28d37b8f 130 }
citrusbyte 19:4dfa28d37b8f 131 if (ts != NULL) { *ts = result; }
citrusbyte 19:4dfa28d37b8f 132 }
citrusbyte 19:4dfa28d37b8f 133 return status;
citrusbyte 19:4dfa28d37b8f 134 }
citrusbyte 19:4dfa28d37b8f 135
citrusbyte 19:4dfa28d37b8f 136 int M2XStreamClient::getTimestamp(char* buffer, int *bufferLength) {
citrusbyte 19:4dfa28d37b8f 137 if (bufferLength == NULL) { return E_INVALID; }
citrusbyte 19:4dfa28d37b8f 138 if (_client->connect(_host, _port)) {
citrusbyte 19:4dfa28d37b8f 139 DBGLN("%s", "Connected to M2X server!");
citrusbyte 19:4dfa28d37b8f 140 _client->println("GET /v2/time/seconds HTTP/1.0");
citrusbyte 16:7903152de19f 141
citrusbyte 19:4dfa28d37b8f 142 writeHttpHeader(-1);
citrusbyte 19:4dfa28d37b8f 143 } else {
citrusbyte 19:4dfa28d37b8f 144 DBGLN("%s", "ERROR: Cannot connect to M2X server!");
citrusbyte 19:4dfa28d37b8f 145 return E_NOCONNECTION;
citrusbyte 16:7903152de19f 146 }
citrusbyte 19:4dfa28d37b8f 147 int status = readStatusCode(false);
citrusbyte 19:4dfa28d37b8f 148 if (status == 200) {
citrusbyte 19:4dfa28d37b8f 149 int length = readContentLength();
citrusbyte 19:4dfa28d37b8f 150 if (length < 0) {
citrusbyte 19:4dfa28d37b8f 151 close();
citrusbyte 19:4dfa28d37b8f 152 return length;
citrusbyte 19:4dfa28d37b8f 153 }
citrusbyte 19:4dfa28d37b8f 154 if (*bufferLength < length) {
citrusbyte 19:4dfa28d37b8f 155 *bufferLength = length;
citrusbyte 19:4dfa28d37b8f 156 return E_BUFFER_TOO_SMALL;
citrusbyte 19:4dfa28d37b8f 157 }
citrusbyte 19:4dfa28d37b8f 158 *bufferLength = length;
citrusbyte 19:4dfa28d37b8f 159 int index = skipHttpHeader();
citrusbyte 19:4dfa28d37b8f 160 if (index != E_OK) {
citrusbyte 19:4dfa28d37b8f 161 close();
citrusbyte 19:4dfa28d37b8f 162 return index;
citrusbyte 19:4dfa28d37b8f 163 }
citrusbyte 19:4dfa28d37b8f 164 index = 0;
citrusbyte 19:4dfa28d37b8f 165 while (index < length) {
citrusbyte 19:4dfa28d37b8f 166 DBG("%s", "Received Data: ");
citrusbyte 19:4dfa28d37b8f 167 while ((index < length) && _client->available()) {
citrusbyte 19:4dfa28d37b8f 168 buffer[index++] = _client->read();
citrusbyte 19:4dfa28d37b8f 169 DBG("%c", buffer[index - 1]);
citrusbyte 19:4dfa28d37b8f 170 }
citrusbyte 19:4dfa28d37b8f 171 DBGLNEND;
citrusbyte 16:7903152de19f 172
citrusbyte 19:4dfa28d37b8f 173 if ((!_client->connected()) &&
citrusbyte 19:4dfa28d37b8f 174 (index < length)) {
citrusbyte 19:4dfa28d37b8f 175 close();
citrusbyte 19:4dfa28d37b8f 176 return E_NOCONNECTION;
citrusbyte 19:4dfa28d37b8f 177 }
citrusbyte 19:4dfa28d37b8f 178
citrusbyte 19:4dfa28d37b8f 179 delay(200);
citrusbyte 19:4dfa28d37b8f 180 }
citrusbyte 19:4dfa28d37b8f 181 }
citrusbyte 19:4dfa28d37b8f 182 close();
citrusbyte 16:7903152de19f 183 return status;
citrusbyte 16:7903152de19f 184 }
citrusbyte 16:7903152de19f 185
citrusbyte 19:4dfa28d37b8f 186 static int write_delete_values(Print* print, const char* from,
citrusbyte 19:4dfa28d37b8f 187 const char* end) {
citrusbyte 19:4dfa28d37b8f 188 int bytes = 0;
citrusbyte 19:4dfa28d37b8f 189 bytes += print->print("{\"from\":\"");
citrusbyte 19:4dfa28d37b8f 190 bytes += print->print(from);
citrusbyte 19:4dfa28d37b8f 191 bytes += print->print("\",\"end\":\"");
citrusbyte 19:4dfa28d37b8f 192 bytes += print->print(end);
citrusbyte 19:4dfa28d37b8f 193 bytes += print->print("\"}");
citrusbyte 19:4dfa28d37b8f 194 return bytes;
citrusbyte 19:4dfa28d37b8f 195 }
citrusbyte 19:4dfa28d37b8f 196
citrusbyte 19:4dfa28d37b8f 197 // Encodes and prints string using Percent-encoding specified
citrusbyte 19:4dfa28d37b8f 198 // in RFC 1738, Section 2.2
citrusbyte 19:4dfa28d37b8f 199 int print_encoded_string(Print* print, const char* str) {
citrusbyte 19:4dfa28d37b8f 200 int bytes = 0;
citrusbyte 19:4dfa28d37b8f 201 for (int i = 0; str[i] != 0; i++) {
citrusbyte 19:4dfa28d37b8f 202 if (((str[i] >= 'A') && (str[i] <= 'Z')) ||
citrusbyte 19:4dfa28d37b8f 203 ((str[i] >= 'a') && (str[i] <= 'z')) ||
citrusbyte 19:4dfa28d37b8f 204 ((str[i] >= '0') && (str[i] <= '9')) ||
citrusbyte 19:4dfa28d37b8f 205 (str[i] == '-') || (str[i] == '_') ||
citrusbyte 19:4dfa28d37b8f 206 (str[i] == '.') || (str[i] == '~')) {
citrusbyte 19:4dfa28d37b8f 207 bytes += print->print(str[i]);
citrusbyte 19:4dfa28d37b8f 208 } else {
citrusbyte 19:4dfa28d37b8f 209 // Encode all other characters
citrusbyte 19:4dfa28d37b8f 210 bytes += print->print('%');
citrusbyte 19:4dfa28d37b8f 211 bytes += print->print(HEX(str[i] / 16));
citrusbyte 19:4dfa28d37b8f 212 bytes += print->print(HEX(str[i] % 16));
citrusbyte 19:4dfa28d37b8f 213 }
jb8414 0:f479e4f4db0e 214 }
citrusbyte 19:4dfa28d37b8f 215 return bytes;
citrusbyte 19:4dfa28d37b8f 216 }
citrusbyte 19:4dfa28d37b8f 217
citrusbyte 19:4dfa28d37b8f 218 void M2XStreamClient::writePutHeader(const char* deviceId,
citrusbyte 19:4dfa28d37b8f 219 const char* streamName,
citrusbyte 19:4dfa28d37b8f 220 int contentLength) {
citrusbyte 19:4dfa28d37b8f 221 _client->print("PUT ");
citrusbyte 19:4dfa28d37b8f 222 if (_path_prefix)
citrusbyte 19:4dfa28d37b8f 223 _client->print(_path_prefix);
citrusbyte 19:4dfa28d37b8f 224 _client->print("/v2/devices/");
citrusbyte 19:4dfa28d37b8f 225 print_encoded_string(_client, deviceId);
citrusbyte 19:4dfa28d37b8f 226 _client->print("/streams/");
citrusbyte 19:4dfa28d37b8f 227 print_encoded_string(_client, streamName);
citrusbyte 19:4dfa28d37b8f 228 _client->println("/value HTTP/1.0");
citrusbyte 19:4dfa28d37b8f 229
citrusbyte 19:4dfa28d37b8f 230 writeHttpHeader(contentLength);
citrusbyte 19:4dfa28d37b8f 231 }
citrusbyte 19:4dfa28d37b8f 232
citrusbyte 19:4dfa28d37b8f 233 void M2XStreamClient::writeDeleteHeader(const char* deviceId,
citrusbyte 19:4dfa28d37b8f 234 const char* streamName,
citrusbyte 19:4dfa28d37b8f 235 int contentLength) {
citrusbyte 19:4dfa28d37b8f 236 _client->print("DELETE ");
citrusbyte 19:4dfa28d37b8f 237 if (_path_prefix)
citrusbyte 19:4dfa28d37b8f 238 _client->print(_path_prefix);
citrusbyte 19:4dfa28d37b8f 239 _client->print("/v2/devices/");
citrusbyte 19:4dfa28d37b8f 240 print_encoded_string(_client, deviceId);
citrusbyte 19:4dfa28d37b8f 241 _client->print("/streams/");
citrusbyte 19:4dfa28d37b8f 242 print_encoded_string(_client, streamName);
citrusbyte 19:4dfa28d37b8f 243 _client->print("/values");
citrusbyte 19:4dfa28d37b8f 244 _client->println(" HTTP/1.0");
citrusbyte 19:4dfa28d37b8f 245
citrusbyte 19:4dfa28d37b8f 246 writeHttpHeader(contentLength);
jb8414 0:f479e4f4db0e 247 }
jb8414 0:f479e4f4db0e 248
citrusbyte 19:4dfa28d37b8f 249 void M2XStreamClient::writeHttpHeader(int contentLength) {
citrusbyte 19:4dfa28d37b8f 250 _client->println(USER_AGENT);
citrusbyte 19:4dfa28d37b8f 251 _client->print("X-M2X-KEY: ");
citrusbyte 19:4dfa28d37b8f 252 _client->println(_key);
citrusbyte 19:4dfa28d37b8f 253
citrusbyte 19:4dfa28d37b8f 254 _client->print("Host: ");
citrusbyte 19:4dfa28d37b8f 255 print_encoded_string(_client, _host);
citrusbyte 19:4dfa28d37b8f 256 if (_port != kDefaultM2XPort) {
citrusbyte 19:4dfa28d37b8f 257 _client->print(":");
citrusbyte 19:4dfa28d37b8f 258 // port is an integer, does not need encoding
citrusbyte 19:4dfa28d37b8f 259 _client->print(_port);
citrusbyte 19:4dfa28d37b8f 260 }
citrusbyte 19:4dfa28d37b8f 261 _client->println();
citrusbyte 19:4dfa28d37b8f 262
citrusbyte 19:4dfa28d37b8f 263 if (contentLength > 0) {
citrusbyte 19:4dfa28d37b8f 264 _client->println("Content-Type: application/json");
citrusbyte 19:4dfa28d37b8f 265 DBG("%s", "Content Length: ");
citrusbyte 19:4dfa28d37b8f 266 DBGLN("%d", contentLength);
citrusbyte 19:4dfa28d37b8f 267
citrusbyte 19:4dfa28d37b8f 268 _client->print("Content-Length: ");
citrusbyte 19:4dfa28d37b8f 269 _client->println(contentLength);
citrusbyte 19:4dfa28d37b8f 270 }
citrusbyte 19:4dfa28d37b8f 271 _client->println();
citrusbyte 17:9db4a86b876a 272 }
citrusbyte 19:4dfa28d37b8f 273
citrusbyte 19:4dfa28d37b8f 274 int M2XStreamClient::waitForString(const char* str) {
citrusbyte 19:4dfa28d37b8f 275 int currentIndex = 0;
citrusbyte 19:4dfa28d37b8f 276 if (str[currentIndex] == '\0') return E_OK;
citrusbyte 19:4dfa28d37b8f 277
citrusbyte 19:4dfa28d37b8f 278 while (true) {
citrusbyte 19:4dfa28d37b8f 279 while (_client->available()) {
citrusbyte 19:4dfa28d37b8f 280 char c = _client->read();
citrusbyte 19:4dfa28d37b8f 281 DBG("%c", c);
citrusbyte 19:4dfa28d37b8f 282
citrusbyte 19:4dfa28d37b8f 283 int cmp;
citrusbyte 19:4dfa28d37b8f 284 if (_case_insensitive) {
citrusbyte 19:4dfa28d37b8f 285 cmp = tolower(c) - tolower(str[currentIndex]);
citrusbyte 19:4dfa28d37b8f 286 } else {
citrusbyte 19:4dfa28d37b8f 287 cmp = c - str[currentIndex];
citrusbyte 19:4dfa28d37b8f 288 }
jb8414 0:f479e4f4db0e 289
citrusbyte 19:4dfa28d37b8f 290 if ((str[currentIndex] == '*') || (cmp == 0)) {
citrusbyte 19:4dfa28d37b8f 291 currentIndex++;
citrusbyte 19:4dfa28d37b8f 292 if (str[currentIndex] == '\0') {
citrusbyte 19:4dfa28d37b8f 293 return E_OK;
citrusbyte 19:4dfa28d37b8f 294 }
citrusbyte 19:4dfa28d37b8f 295 } else {
citrusbyte 19:4dfa28d37b8f 296 // start from the beginning
citrusbyte 19:4dfa28d37b8f 297 currentIndex = 0;
citrusbyte 19:4dfa28d37b8f 298 }
citrusbyte 19:4dfa28d37b8f 299 }
citrusbyte 19:4dfa28d37b8f 300
citrusbyte 19:4dfa28d37b8f 301 if (!_client->connected()) {
citrusbyte 19:4dfa28d37b8f 302 DBGLN("%s", "ERROR: The client is disconnected from the server!");
citrusbyte 19:4dfa28d37b8f 303
citrusbyte 19:4dfa28d37b8f 304 close();
citrusbyte 19:4dfa28d37b8f 305 return E_DISCONNECTED;
citrusbyte 19:4dfa28d37b8f 306 }
citrusbyte 19:4dfa28d37b8f 307
citrusbyte 19:4dfa28d37b8f 308 delay(1000);
citrusbyte 19:4dfa28d37b8f 309 }
citrusbyte 19:4dfa28d37b8f 310 // never reached here
citrusbyte 19:4dfa28d37b8f 311 return E_NOTREACHABLE;
citrusbyte 19:4dfa28d37b8f 312 }
jb8414 0:f479e4f4db0e 313
citrusbyte 19:4dfa28d37b8f 314 int M2XStreamClient::readStatusCode(bool closeClient) {
citrusbyte 19:4dfa28d37b8f 315 int responseCode = 0;
citrusbyte 19:4dfa28d37b8f 316 int ret = waitForString("HTTP/*.* ");
citrusbyte 19:4dfa28d37b8f 317 if (ret != E_OK) {
citrusbyte 19:4dfa28d37b8f 318 if (closeClient) close();
citrusbyte 19:4dfa28d37b8f 319 return ret;
citrusbyte 19:4dfa28d37b8f 320 }
citrusbyte 19:4dfa28d37b8f 321
citrusbyte 19:4dfa28d37b8f 322 // ret is not needed from here(since it must be E_OK), so we can use it
citrusbyte 19:4dfa28d37b8f 323 // as a regular variable now.
citrusbyte 19:4dfa28d37b8f 324 ret = 0;
citrusbyte 19:4dfa28d37b8f 325 while (true) {
citrusbyte 19:4dfa28d37b8f 326 while (_client->available()) {
citrusbyte 19:4dfa28d37b8f 327 char c = _client->read();
citrusbyte 19:4dfa28d37b8f 328 DBG("%c", c);
citrusbyte 19:4dfa28d37b8f 329
citrusbyte 19:4dfa28d37b8f 330 responseCode = responseCode * 10 + (c - '0');
citrusbyte 19:4dfa28d37b8f 331 ret++;
citrusbyte 19:4dfa28d37b8f 332 if (ret == 3) {
citrusbyte 19:4dfa28d37b8f 333 if (closeClient) close();
citrusbyte 19:4dfa28d37b8f 334 return responseCode;
citrusbyte 19:4dfa28d37b8f 335 }
citrusbyte 19:4dfa28d37b8f 336 }
citrusbyte 19:4dfa28d37b8f 337
citrusbyte 19:4dfa28d37b8f 338 if (!_client->connected()) {
citrusbyte 19:4dfa28d37b8f 339 DBGLN("%s", "ERROR: The client is disconnected from the server!");
citrusbyte 19:4dfa28d37b8f 340
citrusbyte 19:4dfa28d37b8f 341 if (closeClient) close();
citrusbyte 19:4dfa28d37b8f 342 return E_DISCONNECTED;
citrusbyte 19:4dfa28d37b8f 343 }
citrusbyte 19:4dfa28d37b8f 344
citrusbyte 19:4dfa28d37b8f 345 delay(1000);
jb8414 0:f479e4f4db0e 346 }
jb8414 0:f479e4f4db0e 347
citrusbyte 19:4dfa28d37b8f 348 // never reached here
citrusbyte 19:4dfa28d37b8f 349 return E_NOTREACHABLE;
citrusbyte 19:4dfa28d37b8f 350 }
jb8414 0:f479e4f4db0e 351
citrusbyte 19:4dfa28d37b8f 352 int M2XStreamClient::readContentLength() {
citrusbyte 19:4dfa28d37b8f 353 int ret = waitForString("Content-Length: ");
citrusbyte 19:4dfa28d37b8f 354 if (ret != E_OK) {
citrusbyte 19:4dfa28d37b8f 355 return ret;
citrusbyte 19:4dfa28d37b8f 356 }
jb8414 0:f479e4f4db0e 357
citrusbyte 19:4dfa28d37b8f 358 // From now on, ret is not needed, we can use it
citrusbyte 19:4dfa28d37b8f 359 // to keep the final result
citrusbyte 19:4dfa28d37b8f 360 ret = 0;
citrusbyte 19:4dfa28d37b8f 361 while (true) {
citrusbyte 19:4dfa28d37b8f 362 while (_client->available()) {
citrusbyte 19:4dfa28d37b8f 363 char c = _client->read();
citrusbyte 19:4dfa28d37b8f 364 DBG("%c", c);
jb8414 0:f479e4f4db0e 365
citrusbyte 19:4dfa28d37b8f 366 if ((c == '\r') || (c == '\n')) {
citrusbyte 19:4dfa28d37b8f 367 return (ret == 0) ? (E_INVALID) : (ret);
citrusbyte 19:4dfa28d37b8f 368 } else {
citrusbyte 19:4dfa28d37b8f 369 ret = ret * 10 + (c - '0');
citrusbyte 19:4dfa28d37b8f 370 }
citrusbyte 19:4dfa28d37b8f 371 }
citrusbyte 19:4dfa28d37b8f 372
citrusbyte 19:4dfa28d37b8f 373 if (!_client->connected()) {
citrusbyte 19:4dfa28d37b8f 374 DBGLN("%s", "ERROR: The client is disconnected from the server!");
citrusbyte 19:4dfa28d37b8f 375
citrusbyte 19:4dfa28d37b8f 376 return E_DISCONNECTED;
jb8414 0:f479e4f4db0e 377 }
citrusbyte 10:4ce9eba38dbe 378
citrusbyte 19:4dfa28d37b8f 379 delay(1000);
jb8414 0:f479e4f4db0e 380 }
citrusbyte 19:4dfa28d37b8f 381
citrusbyte 19:4dfa28d37b8f 382 // never reached here
citrusbyte 19:4dfa28d37b8f 383 return E_NOTREACHABLE;
citrusbyte 19:4dfa28d37b8f 384 }
citrusbyte 19:4dfa28d37b8f 385
citrusbyte 19:4dfa28d37b8f 386 int M2XStreamClient::skipHttpHeader() {
citrusbyte 19:4dfa28d37b8f 387 return waitForString("\n\r\n");
citrusbyte 19:4dfa28d37b8f 388 }
citrusbyte 19:4dfa28d37b8f 389
citrusbyte 19:4dfa28d37b8f 390 void M2XStreamClient::close() {
citrusbyte 19:4dfa28d37b8f 391 // Eats up buffered data before closing
citrusbyte 19:4dfa28d37b8f 392 _client->flush();
citrusbyte 19:4dfa28d37b8f 393 _client->stop();
citrusbyte 19:4dfa28d37b8f 394 }
citrusbyte 19:4dfa28d37b8f 395
citrusbyte 19:4dfa28d37b8f 396 int M2XStreamClient::readStreamValue(stream_value_read_callback callback,
citrusbyte 19:4dfa28d37b8f 397 void* context) {
citrusbyte 19:4dfa28d37b8f 398 const int BUF_LEN = 64;
citrusbyte 19:4dfa28d37b8f 399 char buf[BUF_LEN];
citrusbyte 19:4dfa28d37b8f 400
citrusbyte 19:4dfa28d37b8f 401 int length = readContentLength();
citrusbyte 19:4dfa28d37b8f 402 if (length < 0) {
citrusbyte 19:4dfa28d37b8f 403 close();
citrusbyte 19:4dfa28d37b8f 404 return length;
citrusbyte 19:4dfa28d37b8f 405 }
jb8414 0:f479e4f4db0e 406
citrusbyte 19:4dfa28d37b8f 407 int index = skipHttpHeader();
citrusbyte 19:4dfa28d37b8f 408 if (index != E_OK) {
citrusbyte 19:4dfa28d37b8f 409 close();
citrusbyte 19:4dfa28d37b8f 410 return index;
citrusbyte 19:4dfa28d37b8f 411 }
citrusbyte 19:4dfa28d37b8f 412 index = 0;
citrusbyte 19:4dfa28d37b8f 413
citrusbyte 19:4dfa28d37b8f 414 stream_parsing_context_state state;
citrusbyte 19:4dfa28d37b8f 415 state.state = state.index = 0;
citrusbyte 19:4dfa28d37b8f 416 state.callback = callback;
citrusbyte 19:4dfa28d37b8f 417 state.context = context;
citrusbyte 19:4dfa28d37b8f 418
citrusbyte 19:4dfa28d37b8f 419 jsonlite_parser_callbacks cbs = jsonlite_default_callbacks;
citrusbyte 19:4dfa28d37b8f 420 cbs.key_found = on_stream_key_found;
citrusbyte 19:4dfa28d37b8f 421 cbs.number_found = on_stream_number_found;
citrusbyte 19:4dfa28d37b8f 422 cbs.string_found = on_stream_string_found;
citrusbyte 19:4dfa28d37b8f 423 cbs.context.client_state = &state;
jb8414 0:f479e4f4db0e 424
citrusbyte 19:4dfa28d37b8f 425 jsonlite_parser p = jsonlite_parser_init(jsonlite_parser_estimate_size(5));
citrusbyte 19:4dfa28d37b8f 426 jsonlite_parser_set_callback(p, &cbs);
citrusbyte 19:4dfa28d37b8f 427
citrusbyte 19:4dfa28d37b8f 428 jsonlite_result result = jsonlite_result_unknown;
citrusbyte 19:4dfa28d37b8f 429 while (index < length) {
citrusbyte 19:4dfa28d37b8f 430 int i = 0;
citrusbyte 19:4dfa28d37b8f 431
citrusbyte 19:4dfa28d37b8f 432 DBG("%s", "Received Data: ");
citrusbyte 19:4dfa28d37b8f 433 while ((i < BUF_LEN) && _client->available()) {
citrusbyte 19:4dfa28d37b8f 434 buf[i++] = _client->read();
citrusbyte 19:4dfa28d37b8f 435 DBG("%c", buf[i - 1]);
citrusbyte 19:4dfa28d37b8f 436 }
citrusbyte 19:4dfa28d37b8f 437 DBGLNEND;
citrusbyte 19:4dfa28d37b8f 438
citrusbyte 19:4dfa28d37b8f 439 if ((!_client->connected()) &&
citrusbyte 19:4dfa28d37b8f 440 (!_client->available()) &&
citrusbyte 19:4dfa28d37b8f 441 ((index + i) < length)) {
citrusbyte 19:4dfa28d37b8f 442 jsonlite_parser_release(p);
citrusbyte 19:4dfa28d37b8f 443 close();
citrusbyte 19:4dfa28d37b8f 444 return E_NOCONNECTION;
citrusbyte 19:4dfa28d37b8f 445 }
jb8414 0:f479e4f4db0e 446
citrusbyte 19:4dfa28d37b8f 447 result = jsonlite_parser_tokenize(p, buf, i);
citrusbyte 19:4dfa28d37b8f 448 if ((result != jsonlite_result_ok) &&
citrusbyte 19:4dfa28d37b8f 449 (result != jsonlite_result_end_of_stream)) {
citrusbyte 19:4dfa28d37b8f 450 jsonlite_parser_release(p);
citrusbyte 19:4dfa28d37b8f 451 close();
citrusbyte 19:4dfa28d37b8f 452 return E_JSON_INVALID;
citrusbyte 19:4dfa28d37b8f 453 }
citrusbyte 19:4dfa28d37b8f 454
citrusbyte 19:4dfa28d37b8f 455 index += i;
citrusbyte 19:4dfa28d37b8f 456 }
citrusbyte 19:4dfa28d37b8f 457
citrusbyte 19:4dfa28d37b8f 458 jsonlite_parser_release(p);
citrusbyte 19:4dfa28d37b8f 459 close();
citrusbyte 19:4dfa28d37b8f 460 return (result == jsonlite_result_ok) ? (E_OK) : (E_JSON_INVALID);
citrusbyte 19:4dfa28d37b8f 461 }
citrusbyte 19:4dfa28d37b8f 462
citrusbyte 19:4dfa28d37b8f 463 int M2XStreamClient::readLocation(location_read_callback callback,
citrusbyte 19:4dfa28d37b8f 464 void* context) {
citrusbyte 19:4dfa28d37b8f 465 const int BUF_LEN = 40;
citrusbyte 19:4dfa28d37b8f 466 char buf[BUF_LEN];
citrusbyte 19:4dfa28d37b8f 467
citrusbyte 19:4dfa28d37b8f 468 int length = readContentLength();
citrusbyte 19:4dfa28d37b8f 469 if (length < 0) {
citrusbyte 19:4dfa28d37b8f 470 close();
citrusbyte 19:4dfa28d37b8f 471 return length;
citrusbyte 19:4dfa28d37b8f 472 }
citrusbyte 19:4dfa28d37b8f 473
citrusbyte 19:4dfa28d37b8f 474 int index = skipHttpHeader();
citrusbyte 19:4dfa28d37b8f 475 if (index != E_OK) {
citrusbyte 19:4dfa28d37b8f 476 close();
citrusbyte 19:4dfa28d37b8f 477 return index;
citrusbyte 19:4dfa28d37b8f 478 }
citrusbyte 19:4dfa28d37b8f 479 index = 0;
citrusbyte 19:4dfa28d37b8f 480
citrusbyte 19:4dfa28d37b8f 481 location_parsing_context_state state;
citrusbyte 19:4dfa28d37b8f 482 state.state = state.index = 0;
citrusbyte 19:4dfa28d37b8f 483 state.callback = callback;
citrusbyte 19:4dfa28d37b8f 484 state.context = context;
jb8414 0:f479e4f4db0e 485
citrusbyte 19:4dfa28d37b8f 486 jsonlite_parser_callbacks cbs = jsonlite_default_callbacks;
citrusbyte 19:4dfa28d37b8f 487 cbs.key_found = on_location_key_found;
citrusbyte 19:4dfa28d37b8f 488 cbs.string_found = on_location_string_found;
citrusbyte 19:4dfa28d37b8f 489 cbs.context.client_state = &state;
citrusbyte 19:4dfa28d37b8f 490
citrusbyte 19:4dfa28d37b8f 491 jsonlite_parser p = jsonlite_parser_init(jsonlite_parser_estimate_size(5));
citrusbyte 19:4dfa28d37b8f 492 jsonlite_parser_set_callback(p, &cbs);
citrusbyte 19:4dfa28d37b8f 493
citrusbyte 19:4dfa28d37b8f 494 jsonlite_result result = jsonlite_result_unknown;
citrusbyte 19:4dfa28d37b8f 495 while (index < length) {
citrusbyte 19:4dfa28d37b8f 496 int i = 0;
citrusbyte 19:4dfa28d37b8f 497
citrusbyte 19:4dfa28d37b8f 498 DBG("%s", "Received Data: ");
citrusbyte 19:4dfa28d37b8f 499 while ((i < BUF_LEN) && _client->available()) {
citrusbyte 19:4dfa28d37b8f 500 buf[i++] = _client->read();
citrusbyte 19:4dfa28d37b8f 501 DBG("%c", buf[i - 1]);
citrusbyte 19:4dfa28d37b8f 502 }
citrusbyte 19:4dfa28d37b8f 503 DBGLNEND;
jb8414 0:f479e4f4db0e 504
citrusbyte 19:4dfa28d37b8f 505 if ((!_client->connected()) &&
citrusbyte 19:4dfa28d37b8f 506 (!_client->available()) &&
citrusbyte 19:4dfa28d37b8f 507 ((index + i) < length)) {
citrusbyte 19:4dfa28d37b8f 508 jsonlite_parser_release(p);
citrusbyte 19:4dfa28d37b8f 509 close();
citrusbyte 19:4dfa28d37b8f 510 return E_NOCONNECTION;
citrusbyte 19:4dfa28d37b8f 511 }
citrusbyte 19:4dfa28d37b8f 512
citrusbyte 19:4dfa28d37b8f 513 result = jsonlite_parser_tokenize(p, buf, i);
citrusbyte 19:4dfa28d37b8f 514 if ((result != jsonlite_result_ok) &&
citrusbyte 19:4dfa28d37b8f 515 (result != jsonlite_result_end_of_stream)) {
citrusbyte 19:4dfa28d37b8f 516 jsonlite_parser_release(p);
citrusbyte 19:4dfa28d37b8f 517 close();
citrusbyte 19:4dfa28d37b8f 518 return E_JSON_INVALID;
citrusbyte 19:4dfa28d37b8f 519 }
citrusbyte 19:4dfa28d37b8f 520
citrusbyte 19:4dfa28d37b8f 521 index += i;
citrusbyte 19:4dfa28d37b8f 522 }
citrusbyte 19:4dfa28d37b8f 523
citrusbyte 19:4dfa28d37b8f 524 jsonlite_parser_release(p);
citrusbyte 19:4dfa28d37b8f 525 close();
citrusbyte 19:4dfa28d37b8f 526 return (result == jsonlite_result_ok) ? (E_OK) : (E_JSON_INVALID);
jb8414 0:f479e4f4db0e 527 }