/*
* HTTPClient
* HTTPClient.c
*
* http://interactive-matter.org/
*
* This file is part of HTTPClient.
*
* HTTPClient is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* HTTPClient is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with HTTPClient. If not, see .
*
* Created on: 18.09.2010
* Author: marcus
*/
#include
#include
#include
#include
#include
#include "HTTPClient.h"
//a struct to store the uriEncoder & the handle to the http client
typedef struct
{
HTTPClient* client;
uint8_t encode;
#define URI_ENCODE _BV(0)
#define URI_ENCODE_RESERVED _BV(1)
} http_stream_udata;
//we need this probably more than once o it is defined here
#define URI_ALLOWED(byte) ((byte>='A' && byte<='Z') || (byte>='a' && byte<='z') || (byte>='0' && byte<='9') || byte == '-' || byte == '_' || byte == '.' || byte == '~')
#define URI_RESERVED(byte) (byte == '!' || byte == '*' || byte == '\'' || byte == '(' || byte == ')' || byte == ';' || byte == ':' || byte == '&' || byte == '=' || byte == '+' || byte == '$' || byte == ',' || byte == '/' || byte == '?' || byte == '#' || byte == '[' || byte == ']')
HTTPClient::HTTPClient(char* host, uint8_t* ip) :
EthernetClient(), hostName(host), debugCommunication(0), ip(ip), port(80)
{
//nothing else to do
}
HTTPClient::HTTPClient(char* host, uint8_t* ip, uint16_t port) :
EthernetClient(), hostName(host), debugCommunication(0), ip(ip), port(port)
{
//nothing else to do
}
FILE*
HTTPClient::getURI(char* uri)
{
return getURI(uri, NULL, NULL);
}
FILE*
HTTPClient::getURI(char* uri, http_client_parameter parameters[])
{
return getURI(uri, parameters, NULL);
}
FILE*
HTTPClient::getURI(char* uri, http_client_parameter parameters[],
http_client_parameter headers[])
{
FILE* result = openClientFile();
if (result == NULL)
{
return NULL;
}
//the request and the default headers
sendUriAndHeaders(result, this->hostName, PSTR("GET"), uri, parameters,
headers);
//ok header finished
fprintf_P(result, PSTR("\r\n"));
skipHeader(result);
return result;
}
FILE*
HTTPClient::postURI(char* uri, char* data)
{
return postURI(uri, NULL, data, NULL);
}
FILE*
HTTPClient::postURI(char* uri, http_client_parameter parameters[], char* data)
{
return postURI(uri, parameters, data, NULL);
}
FILE*
HTTPClient::postURI(char* uri, http_client_parameter parameters[], char* data,
http_client_parameter headers[])
{
FILE* result = openClientFile();
if (result == NULL)
{
return NULL;
}
sendUriAndHeaders(result, this->hostName, PSTR("POST"), uri, parameters,
headers);
sendContentPayload(result, data);
skipHeader(result);
return result;
}
FILE*
HTTPClient::putURI(char* uri, char* data)
{
return putURI(uri, NULL, data, NULL);
}
FILE*
HTTPClient::putURI(char* uri, http_client_parameter parameters[], char* data)
{
return putURI(uri, parameters, data, NULL);
}
FILE*
HTTPClient::putURI(char* uri, http_client_parameter parameters[], char* data,
http_client_parameter headers[])
{
FILE* result = openClientFile();
if (result == NULL)
{
return NULL;
}
sendUriAndHeaders(result, this->hostName, PSTR("PUT"), uri, parameters,
headers);
sendContentPayload(result, data);
skipHeader(result);
return result;
}
int
HTTPClient::getLastReturnCode(void)
{
return lastReturnCode;
}
void
HTTPClient::debug(char debugOn)
{
this->debugCommunication = debugOn;
}
void
HTTPClient::setEncoding(FILE* stream, char encode, char encodeReserved)
{
http_stream_udata* udata = (http_stream_udata*) fdev_get_udata(stream);
if (encode == 0)
{
udata->encode = 0;
}
else
{
if (encodeReserved)
{
udata->encode = URI_ENCODE | URI_ENCODE_RESERVED;
}
else
{
udata->encode = URI_ENCODE;
}
}
}
FILE*
HTTPClient::openClientFile()
{
FILE* result = fdevopen(clientWrite, clientRead);
if (result == NULL)
{
return NULL;
}
http_stream_udata* udata = (http_stream_udata*) malloc(
sizeof(http_stream_udata));
fdev_set_udata(result,udata);
udata->client = this;
udata->encode = 0;
if (connected())
{
stop();
}
if (connect(ip,port))
{
return result;
}
else
{
closeStream(result);
return NULL;
}
}
char
HTTPClient::sendUriAndHeaders(FILE* stream, char* hostName, const char* requestType, char* uri,
http_client_parameter parameters[], http_client_parameter headers[])
{
if (stream == NULL)
{
return 0;
}
fprintf_P(stream, requestType, uri);
fprintf_P(stream, PSTR(" "), uri);
//encode but use reserved characters
HTTPClient::setEncoding(stream, 1, 0);
fprintf_P(stream, PSTR("%s"), uri);
if (parameters != NULL)
{
fprintf_P(stream, PSTR("?"));
char parameter_number = 0;
http_client_parameter* parameter = ¶meters[0];
while (parameter->name != NULL)
{
if (parameter_number > 0)
{
fprintf_P(stream, PSTR("&"));
}
HTTPClient::setEncoding(stream, 1, 1);
fprintf_P(stream, PSTR("%s"), parameter->name);
HTTPClient::setEncoding(stream, 1, 0);
fprintf_P(stream, PSTR("="));
HTTPClient::setEncoding(stream, 1, 1);
if (parameter->value != NULL)
{
fprintf_P(stream, PSTR("%s"), parameter->value);
}
HTTPClient::setEncoding(stream, 1, 0);
parameter_number++;
parameter = ¶meters[parameter_number];
}
}
HTTPClient::setEncoding(stream, 0, 0);
fprintf_P(stream, PSTR(
" HTTP/1.1\r\nHost: %s\r\nAccept: */*\r\nConnection: close\r\n"), hostName);
//is there an additional header?
if (headers != NULL)
{
char headerNumber = 0;
while (headers[headerNumber].name != NULL)
{
if (headers[headerNumber].value != NULL)
{
fprintf_P(stream, PSTR("%s: %s\r\n"), headers[headerNumber].name,
headers[headerNumber].value);
headerNumber++;
}
}
}
return 0;
}
char
HTTPClient::sendContentPayload(FILE* stream, char* data)
{
if (stream == NULL)
{
return 0;
}
//calculate the content length
int content_length = 0;
if (data != NULL)
{
content_length = strlen(data) + 1;
}
fprintf_P(stream, PSTR("Content-Length: %i\r\n"), content_length);
//ok finished header
fprintf_P(stream, PSTR("\r\n"));
//now sending data
if (content_length > 0)
{
fprintf_P(stream, PSTR("%s"), data);
}
fprintf_P(stream, PSTR("\n"));
return 0;
}
int
HTTPClient::clientWrite(char byte, FILE* stream)
{
if (stream == NULL)
{
return EOF;
}
http_stream_udata* udata = (http_stream_udata*) fdev_get_udata(stream);
HTTPClient* client = udata->client;
if (client->connected() == 0)
{
closeStream(stream);
return EOF;
}
if (udata->encode == 0)
{
client->write(byte);
if (client->debugCommunication)
{
Serial.print(byte);
}
}
else
{
if (URI_ALLOWED(byte) || ((URI_RESERVED(byte) && (udata->encode
& URI_ENCODE_RESERVED) == 0)))
{
client->write(byte);
if (client->debugCommunication)
{
Serial.print(byte);
}
}
else
{
char encoded[4] =
{ 0, 0, 0 };
sprintf(encoded, "%%%2x", byte);
// Write only the first three bytes, not the trailing null
for (char i = 0; i < 3; i++)
{
client->write(encoded[i]);
if (client->debugCommunication)
{
Serial.print(encoded[i]);
}
}
}
}
return 0;
}
int
HTTPClient::clientRead(FILE* stream)
{
if (stream == NULL)
{
return EOF;
}
http_stream_udata* udata = (http_stream_udata*) fdev_get_udata(stream);
HTTPClient* client = udata->client;
if (!client->connected())
{
return EOF;
}
//block until we got a byte
while (client->available() == 0)
{
if (client->connected() == 0)
{
return EOF;
}
};
int result = client->read();
if (result == EOF)
{
return EOF;
}
if (client->debugCommunication)
{
Serial.print((byte) result);
}
//as long as we do not read encoded or it is no % everything is ok
if (udata->encode == 0 || result != '%')
{
return result;
}
else
{
//block until we got the needed bytes
while (client->available() >= 2)
{
if (client->connected() == 0)
{
return EOF;
}
};
char return_value = 0;
for (char i = 0; i < 2; i++)
{
result = client->read();
if (result == EOF)
{
return EOF;
}
else if (result >= 'A' && result <= 'Z')
{
return_value += (1 - i) * 16 * (result - 'A');
}
else if (result >= 'a' && result <= 'z')
{
return_value += (1 - i) * 16 * (result - 'a');
}
else if (result >= '0' && result <= '9')
{
return_value += (1 - i) * 16 * (result - '0');
}
}
return return_value;
}
}
int
HTTPClient::skipHeader(FILE* stream)
{
//skip over the header
int httpReturnCode;
lastReturnCode = NULL;
if (stream == NULL) return NULL;
http_stream_udata* udata = (http_stream_udata*) fdev_get_udata(stream);
HTTPClient* client = udata->client;
int delayReadHeaderCount=0;
// check if at least the first HTTP-response available, up to 1000ms=1s
while ((client->available()<17) && (delayReadHeaderCount++client->stop();
free(udata);
fclose(stream);
}
}