200 lines
4.4 KiB
C++
200 lines
4.4 KiB
C++
|
/*
|
||
|
* Stairs
|
||
|
* Copyright 2017 (c) Mark van Renswoude
|
||
|
*
|
||
|
* https://git.x2software.net/pub/Stairs
|
||
|
*/
|
||
|
#include "geocode.h"
|
||
|
#include <ESP8266WiFi.h>
|
||
|
#include <ESPAsyncTCP.h>
|
||
|
|
||
|
#include "../debug.h"
|
||
|
#include "../secret.h"
|
||
|
|
||
|
|
||
|
struct ResponseBuffer
|
||
|
{
|
||
|
void* data;
|
||
|
size_t length;
|
||
|
ResponseBuffer* next;
|
||
|
};
|
||
|
|
||
|
|
||
|
String urlencode(const String url)
|
||
|
{
|
||
|
String encoded;
|
||
|
|
||
|
for (int i = 0; i < url.length(); i++)
|
||
|
{
|
||
|
char c = url.charAt(i);
|
||
|
if (c == 0x20)
|
||
|
encoded += "%20";
|
||
|
else if (isalnum(c))
|
||
|
encoded += c;
|
||
|
else
|
||
|
{
|
||
|
encoded += "%";
|
||
|
if (c < 0x10) encoded += "0";
|
||
|
encoded += String(c, HEX);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return encoded;
|
||
|
}
|
||
|
|
||
|
|
||
|
void memcpy_unaligned(void* dest, void* source, size_t length)
|
||
|
{
|
||
|
uint8_t* destChar = (uint8_t*)dest;
|
||
|
uint8_t* sourceChar = (uint8_t*)source;
|
||
|
|
||
|
while (length > 0)
|
||
|
{
|
||
|
*destChar = *sourceChar;
|
||
|
|
||
|
sourceChar++;
|
||
|
destChar++;
|
||
|
length--;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
void handleGetLatLong(AsyncWebServerRequest *request)
|
||
|
{
|
||
|
_dln("API :: get lat long");
|
||
|
|
||
|
AsyncWebParameter* addressParam = request->getParam("address");
|
||
|
if (addressParam == nullptr)
|
||
|
{
|
||
|
request->send(400);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
String address = addressParam->value();
|
||
|
if (!address.length())
|
||
|
{
|
||
|
request->send(400);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
AsyncClient* httpClient = new AsyncClient();
|
||
|
httpClient->onError([&](void* arg, AsyncClient* client, int error)
|
||
|
{
|
||
|
_dln("API :: get lat long: OnError");
|
||
|
request->send(500);
|
||
|
delete client;
|
||
|
});
|
||
|
|
||
|
|
||
|
httpClient->onConnect([&](void * arg, AsyncClient* client)
|
||
|
{
|
||
|
client->onError(nullptr, nullptr);
|
||
|
ResponseBuffer* responseData = nullptr;
|
||
|
|
||
|
client->onData([&](void* arg, AsyncClient* c, void* data, size_t len)
|
||
|
{
|
||
|
_dln("> OnData");
|
||
|
// Store all received chunks in a linked list
|
||
|
ResponseBuffer* next = new ResponseBuffer;
|
||
|
next->data = malloc(len);
|
||
|
memcpy_unaligned(next->data, data, len);
|
||
|
next->length = len;
|
||
|
next->next = nullptr;
|
||
|
|
||
|
if (responseData == nullptr)
|
||
|
responseData = next;
|
||
|
else
|
||
|
{
|
||
|
ResponseBuffer* prev = responseData;
|
||
|
while (prev->next != nullptr)
|
||
|
prev = prev->next;
|
||
|
|
||
|
prev->next = next;
|
||
|
}
|
||
|
_dln("< OnData");
|
||
|
});
|
||
|
|
||
|
client->onDisconnect([&](void* arg, AsyncClient* c)
|
||
|
{
|
||
|
_dln("> OnDisconnect");
|
||
|
if (responseData == nullptr)
|
||
|
{
|
||
|
request->send(500);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Send back the linked list using a chunked response
|
||
|
ResponseBuffer* sendChunk = responseData;
|
||
|
size_t sendOffset = 0;
|
||
|
|
||
|
AsyncWebServerResponse *response = request->beginChunkedResponse("application/json", [&](uint8_t *buffer, size_t maxLen, size_t index) -> size_t
|
||
|
{
|
||
|
_dln("> ChunkedResponse");
|
||
|
if (sendOffset >= sendChunk->length)
|
||
|
{
|
||
|
// End of the chunk, go to the next one
|
||
|
sendChunk = sendChunk->next;
|
||
|
sendOffset = 0;
|
||
|
}
|
||
|
|
||
|
if (sendChunk == nullptr)
|
||
|
{
|
||
|
// We sent the last one, clean up the linked list
|
||
|
ResponseBuffer* next;
|
||
|
while (responseData != nullptr)
|
||
|
{
|
||
|
next = responseData->next;
|
||
|
|
||
|
free(responseData->data);
|
||
|
delete responseData;
|
||
|
|
||
|
responseData = next;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
if (maxLen >= sendChunk->length - sendOffset)
|
||
|
{
|
||
|
// Send the remainder of the chunk
|
||
|
memcpy_unaligned(buffer, (uint8_t*)sendChunk->data + sendOffset, sendChunk->length - sendOffset);
|
||
|
sendOffset = sendChunk->length;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
memcpy_unaligned(buffer, (uint8_t*)sendChunk->data + sendOffset, maxLen);
|
||
|
sendOffset += maxLen;
|
||
|
}
|
||
|
_dln("< ChunkedResponse");
|
||
|
});
|
||
|
request->send(response);
|
||
|
|
||
|
delete c;
|
||
|
_dln("< OnDisconnect");
|
||
|
});
|
||
|
|
||
|
#ifdef SecretsPresent
|
||
|
String url = "/maps/api/geocode/json?address=" + urlencode(address) + "&key=" + urlencode(GoogleAPIKey);
|
||
|
#else
|
||
|
String url = "/maps/api/geocode/json?address=" + urlencode(address);
|
||
|
#endif
|
||
|
|
||
|
client->write(String("GET " + url + " HTTP/1.0\r\nHost: maps.googleapis.com\n\r\n").c_str());
|
||
|
});
|
||
|
|
||
|
|
||
|
if (!httpClient->connect("maps.googleapis.com", 443, true))
|
||
|
{
|
||
|
_dln("API :: get lat long: failed to connect to Google API");
|
||
|
delete httpClient;
|
||
|
|
||
|
request->send(500);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
void registerGeocodeRoutes(AsyncWebServer* server)
|
||
|
{
|
||
|
server->on("/api/geo/latlong", HTTP_GET, handleGetLatLong);
|
||
|
}
|