mirror of
https://github.com/arduino-libraries/ArduinoHttpClient.git
synced 2025-04-19 21:22:15 +03:00
Initial version on github. Updates old version which lived on googlecode to use new DHCP and DNS API in the Arduino Ethernet library
This commit is contained in:
commit
6e632624bf
433
HttpClient.cpp
Normal file
433
HttpClient.cpp
Normal file
@ -0,0 +1,433 @@
|
|||||||
|
// Class to simplify HTTP fetching on Arduino
|
||||||
|
// (c) Copyright 2010-2011 MCQN Ltd
|
||||||
|
// Released under Apache License, version 2.0
|
||||||
|
|
||||||
|
#include "HttpClient.h"
|
||||||
|
#include <../b64/b64.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
#include "wiring.h"
|
||||||
|
|
||||||
|
// Initialize constants
|
||||||
|
const char* HttpClient::kUserAgent = "Arduino/2.0";
|
||||||
|
const char* HttpClient::kGet = "GET";
|
||||||
|
const char* HttpClient::kPost = "POST";
|
||||||
|
const char* HttpClient::kPut = "PUT";
|
||||||
|
const char* HttpClient::kDelete = "DELETE";
|
||||||
|
const char* HttpClient::kContentLengthPrefix = "Content-Length: ";
|
||||||
|
|
||||||
|
HttpClient::HttpClient(Client& aClient)
|
||||||
|
: iClient(&aClient)
|
||||||
|
{
|
||||||
|
resetState();
|
||||||
|
}
|
||||||
|
|
||||||
|
void HttpClient::resetState()
|
||||||
|
{
|
||||||
|
iState = eIdle;
|
||||||
|
iStatusCode = 0;
|
||||||
|
iContentLength = 0;
|
||||||
|
iBodyLengthConsumed = 0;
|
||||||
|
iContentLengthPtr = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void HttpClient::stop()
|
||||||
|
{
|
||||||
|
iClient->stop();
|
||||||
|
resetState();
|
||||||
|
}
|
||||||
|
|
||||||
|
int HttpClient::startRequest(const char* aServerName, uint16_t aServerPort, const char* aURLPath, const char* aHttpMethod, const char* aUserAgent, const char* aAcceptList)
|
||||||
|
{
|
||||||
|
if (eIdle != iState)
|
||||||
|
{
|
||||||
|
return HttpErrAPI;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!iClient->connect(aServerName, aServerPort))
|
||||||
|
{
|
||||||
|
#ifdef LOGGING
|
||||||
|
Serial.println("Connection failed");
|
||||||
|
#endif
|
||||||
|
return HttpErrConnectionFailed;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now we're connected, send the first part of the request
|
||||||
|
return sendInitialHeaders(aServerName, aURLPath, aHttpMethod, aUserAgent, aAcceptList);
|
||||||
|
}
|
||||||
|
|
||||||
|
int HttpClient::startRequest(const IPAddress& aServerAddress, uint16_t aServerPort, const char* aServerName, const char* aURLPath, const char* aHttpMethod, const char* aUserAgent, const char* aAcceptList)
|
||||||
|
{
|
||||||
|
if (eIdle != iState)
|
||||||
|
{
|
||||||
|
return HttpErrAPI;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!iClient->connect(aServerAddress, aServerPort))
|
||||||
|
{
|
||||||
|
#ifdef LOGGING
|
||||||
|
Serial.println("Connection failed");
|
||||||
|
#endif
|
||||||
|
return HttpErrConnectionFailed;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now we're connected, send the first part of the request
|
||||||
|
return sendInitialHeaders(aServerName, aURLPath, aHttpMethod, aUserAgent, aAcceptList);
|
||||||
|
}
|
||||||
|
|
||||||
|
int HttpClient::sendInitialHeaders(const char* aServerName, const char* aURLPath, const char* aHttpMethod, const char* aUserAgent, const char* aAcceptList)
|
||||||
|
{
|
||||||
|
#ifdef LOGGING
|
||||||
|
Serial.println("Connected");
|
||||||
|
#endif
|
||||||
|
// Send the HTTP command, i.e. "GET /somepath/ HTTP/1.0"
|
||||||
|
print(aHttpMethod);
|
||||||
|
print(" ");
|
||||||
|
print(aURLPath);
|
||||||
|
println(" HTTP/1.0");
|
||||||
|
// The host header, if required
|
||||||
|
if (aServerName)
|
||||||
|
{
|
||||||
|
print("Host: ");
|
||||||
|
println(aServerName);
|
||||||
|
}
|
||||||
|
// And user-agent string
|
||||||
|
print("User-Agent: ");
|
||||||
|
if (aUserAgent)
|
||||||
|
{
|
||||||
|
println(aUserAgent);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
println(kUserAgent);
|
||||||
|
}
|
||||||
|
if (aAcceptList)
|
||||||
|
{
|
||||||
|
// We've got an accept list to send
|
||||||
|
print("Accept: ");
|
||||||
|
println(aAcceptList);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Everything has gone well
|
||||||
|
iState = eRequestStarted;
|
||||||
|
return HttpSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
void HttpClient::sendHeader(const char* aHeader)
|
||||||
|
{
|
||||||
|
println(aHeader);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HttpClient::sendHeader(const char* aHeaderName, const char* aHeaderValue)
|
||||||
|
{
|
||||||
|
print(aHeaderName);
|
||||||
|
print(": ");
|
||||||
|
println(aHeaderValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HttpClient::sendHeader(const char* aHeaderName, const int aHeaderValue)
|
||||||
|
{
|
||||||
|
print(aHeaderName);
|
||||||
|
print(": ");
|
||||||
|
println(aHeaderValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
void HttpClient::sendBasicAuth(const char* aUser, const char* aPassword)
|
||||||
|
{
|
||||||
|
// Send the initial part of this header line
|
||||||
|
print("Authorization: Basic ");
|
||||||
|
// Now Base64 encode "aUser:aPassword" and send that
|
||||||
|
// This seems trickier than it should be but it's mostly to avoid either
|
||||||
|
// (a) some arbitrarily sized buffer which hopes to be big enough, or
|
||||||
|
// (b) allocating and freeing memory
|
||||||
|
// ...so we'll loop through 3 bytes at a time, outputting the results as we
|
||||||
|
// go.
|
||||||
|
// In Base64, each 3 bytes of unencoded data become 4 bytes of encoded data
|
||||||
|
unsigned char input[3];
|
||||||
|
unsigned char output[5]; // Leave space for a '\0' terminator so we can easily print
|
||||||
|
int userLen = strlen(aUser);
|
||||||
|
int passwordLen = strlen(aPassword);
|
||||||
|
int inputOffset = 0;
|
||||||
|
for (int i = 0; i < (userLen+1+passwordLen); i++)
|
||||||
|
{
|
||||||
|
// Copy the relevant input byte into the input
|
||||||
|
if (i < userLen)
|
||||||
|
{
|
||||||
|
input[inputOffset++] = aUser[i];
|
||||||
|
}
|
||||||
|
else if (i == userLen)
|
||||||
|
{
|
||||||
|
input[inputOffset++] = ':';
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
input[inputOffset++] = aPassword[i-(userLen+1)];
|
||||||
|
}
|
||||||
|
// See if we've got a chunk to encode
|
||||||
|
if ( (inputOffset == 3) || (i == userLen+passwordLen) )
|
||||||
|
{
|
||||||
|
// We've either got to a 3-byte boundary, or we've reached then end
|
||||||
|
b64_encode(input, inputOffset, output, 4);
|
||||||
|
// NUL-terminate the output string
|
||||||
|
output[4] = '\0';
|
||||||
|
// And write it out
|
||||||
|
print((char*)output);
|
||||||
|
// FIXME We might want to fill output with '=' characters if b64_encode doesn't
|
||||||
|
// FIXME do it for us when we're encoding the final chunk
|
||||||
|
inputOffset = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// And end the header we've sent
|
||||||
|
println();
|
||||||
|
}
|
||||||
|
|
||||||
|
void HttpClient::finishRequest()
|
||||||
|
{
|
||||||
|
println();
|
||||||
|
iState = eRequestSent;
|
||||||
|
}
|
||||||
|
|
||||||
|
int HttpClient::responseStatusCode()
|
||||||
|
{
|
||||||
|
if (iState < eRequestSent)
|
||||||
|
{
|
||||||
|
return HttpErrAPI;
|
||||||
|
}
|
||||||
|
// The first line will be of the form Status-Line:
|
||||||
|
// HTTP-Version SP Status-Code SP Reason-Phrase CRLF
|
||||||
|
// Where HTTP-Version is of the form:
|
||||||
|
// HTTP-Version = "HTTP" "/" 1*DIGIT "." 1*DIGIT
|
||||||
|
|
||||||
|
char c = '\0';
|
||||||
|
do
|
||||||
|
{
|
||||||
|
// Make sure the status code is reset, and likewise the state. This
|
||||||
|
// lets us easily cope with 1xx informational responses by just
|
||||||
|
// ignoring them really, and reading the next line for a proper response
|
||||||
|
iStatusCode = 0;
|
||||||
|
iState = eRequestSent;
|
||||||
|
|
||||||
|
unsigned long timeoutStart = millis();
|
||||||
|
// Psuedo-regexp we're expecting before the status-code
|
||||||
|
const char* statusPrefix = "HTTP/*.* ";
|
||||||
|
const char* statusPtr = statusPrefix;
|
||||||
|
// Whilst we haven't timed out & haven't reached the end of the headers
|
||||||
|
while ((c != '\n') &&
|
||||||
|
( (millis() - timeoutStart) < kHttpResponseTimeout ))
|
||||||
|
{
|
||||||
|
if (available())
|
||||||
|
{
|
||||||
|
c = read();
|
||||||
|
switch(iState)
|
||||||
|
{
|
||||||
|
case eRequestSent:
|
||||||
|
// We haven't reached the status code yet
|
||||||
|
if ( (*statusPtr == '*') || (*statusPtr == c) )
|
||||||
|
{
|
||||||
|
// This character matches, just move along
|
||||||
|
statusPtr++;
|
||||||
|
if (*statusPtr == '\0')
|
||||||
|
{
|
||||||
|
// We've reached the end of the prefix
|
||||||
|
iState = eReadingStatusCode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return HttpErrInvalidResponse;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case eReadingStatusCode:
|
||||||
|
if (isdigit(c))
|
||||||
|
{
|
||||||
|
// This assumes we won't get more than the 3 digits we
|
||||||
|
// want
|
||||||
|
iStatusCode = iStatusCode*10 + (c - '0');
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// We've reached the end of the status code
|
||||||
|
// We could sanity check it here or double-check for ' '
|
||||||
|
// rather than anything else, but let's be lenient
|
||||||
|
iState = eStatusCodeRead;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case eStatusCodeRead:
|
||||||
|
// We're just waiting for the end of the line now
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
// We read something, reset the timeout counter
|
||||||
|
timeoutStart = millis();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// We haven't got any data, so let's pause to allow some to
|
||||||
|
// arrive
|
||||||
|
delay(kHttpWaitForDataDelay);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ( (c == '\n') && (iStatusCode < 200) )
|
||||||
|
{
|
||||||
|
// We've reached the end of an informational status line
|
||||||
|
c = '\0'; // Clear c so we'll go back into the data reading loop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// If we've read a status code successfully but it's informational (1xx)
|
||||||
|
// loop back to the start
|
||||||
|
while ( (iState == eStatusCodeRead) && (iStatusCode < 200) );
|
||||||
|
|
||||||
|
if ( (c == '\n') && (iState == eStatusCodeRead) )
|
||||||
|
{
|
||||||
|
// We've read the status-line successfully
|
||||||
|
return iStatusCode;
|
||||||
|
}
|
||||||
|
else if (c != '\n')
|
||||||
|
{
|
||||||
|
// We must've timed out before we reached the end of the line
|
||||||
|
return HttpErrTimedOut;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// This wasn't a properly formed status line, or at least not one we
|
||||||
|
// could understand
|
||||||
|
return HttpErrInvalidResponse;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int HttpClient::skipResponseHeaders()
|
||||||
|
{
|
||||||
|
// Just keep reading until we finish reading the headers or time out
|
||||||
|
unsigned long timeoutStart = millis();
|
||||||
|
// Whilst we haven't timed out & haven't reached the end of the headers
|
||||||
|
while ((!endOfHeadersReached()) &&
|
||||||
|
( (millis() - timeoutStart) < kHttpResponseTimeout ))
|
||||||
|
{
|
||||||
|
if (available())
|
||||||
|
{
|
||||||
|
(void)readHeader();
|
||||||
|
// We read something, reset the timeout counter
|
||||||
|
timeoutStart = millis();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// We haven't got any data, so let's pause to allow some to
|
||||||
|
// arrive
|
||||||
|
delay(kHttpWaitForDataDelay);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (endOfHeadersReached())
|
||||||
|
{
|
||||||
|
// Success
|
||||||
|
return HttpSuccess;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// We must've timed out
|
||||||
|
return HttpErrTimedOut;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HttpClient::endOfBodyReached()
|
||||||
|
{
|
||||||
|
if (endOfHeadersReached() && (contentLength() != kNoContentLengthHeader))
|
||||||
|
{
|
||||||
|
// We've got to the body and we know how long it will be
|
||||||
|
return (iBodyLengthConsumed >= contentLength());
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int HttpClient::read()
|
||||||
|
{
|
||||||
|
int ret =iClient->read();
|
||||||
|
if (endOfHeadersReached() && iContentLength > 0)
|
||||||
|
{
|
||||||
|
// We're outputting the body now and we've seen a Content-Length header
|
||||||
|
// So keep track of how many bytes are left
|
||||||
|
if (ret >= 0)
|
||||||
|
{
|
||||||
|
iBodyLengthConsumed++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int HttpClient::readHeader()
|
||||||
|
{
|
||||||
|
char c = read();
|
||||||
|
|
||||||
|
if (endOfHeadersReached())
|
||||||
|
{
|
||||||
|
// We've passed the headers, but rather than return an error, we'll just
|
||||||
|
// act as a slightly less efficient version of read()
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Whilst reading out the headers to whoever wants them, we'll keep an
|
||||||
|
// eye out for the "Content-Length" header
|
||||||
|
switch(iState)
|
||||||
|
{
|
||||||
|
case eStatusCodeRead:
|
||||||
|
// We're at the start of a line, or somewhere in the middle of reading
|
||||||
|
// the Content-Length prefix
|
||||||
|
if (*iContentLengthPtr == c)
|
||||||
|
{
|
||||||
|
// This character matches, just move along
|
||||||
|
iContentLengthPtr++;
|
||||||
|
if (*iContentLengthPtr == '\0')
|
||||||
|
{
|
||||||
|
// We've reached the end of the prefix
|
||||||
|
iState = eReadingContentLength;
|
||||||
|
// Just in case we get multiple Content-Length headers, this
|
||||||
|
// will ensure we just get the value of the last one
|
||||||
|
iContentLength = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if ((iContentLengthPtr == kContentLengthPrefix) && (c == '\r'))
|
||||||
|
{
|
||||||
|
// We've found a '\r' at the start of a line, so this is probably
|
||||||
|
// the end of the headers
|
||||||
|
iState = eLineStartingCRFound;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// This isn't the Content-Length header, skip to the end of the line
|
||||||
|
iState = eSkipToEndOfHeader;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case eReadingContentLength:
|
||||||
|
if (isdigit(c))
|
||||||
|
{
|
||||||
|
iContentLength = iContentLength*10 + (c - '0');
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// We've reached the end of the content length
|
||||||
|
// We could sanity check it here or double-check for "\r\n"
|
||||||
|
// rather than anything else, but let's be lenient
|
||||||
|
iState = eSkipToEndOfHeader;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case eLineStartingCRFound:
|
||||||
|
if (c == '\n')
|
||||||
|
{
|
||||||
|
iState = eReadingBody;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// We're just waiting for the end of the line now
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
|
||||||
|
if ( (c == '\n') && !endOfHeadersReached() )
|
||||||
|
{
|
||||||
|
// We've got to the end of this line, start processing again
|
||||||
|
iState = eStatusCodeRead;
|
||||||
|
iContentLengthPtr = kContentLengthPrefix;
|
||||||
|
}
|
||||||
|
// And return the character read to whoever wants it
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
345
HttpClient.h
Normal file
345
HttpClient.h
Normal file
@ -0,0 +1,345 @@
|
|||||||
|
// Class to simplify HTTP fetching on Arduino
|
||||||
|
// (c) Copyright MCQN Ltd. 2010-2011
|
||||||
|
// Released under Apache License, version 2.0
|
||||||
|
|
||||||
|
#ifndef HttpClient_h
|
||||||
|
#define HttpClient_h
|
||||||
|
|
||||||
|
#include "Ethernet.h"
|
||||||
|
|
||||||
|
class HttpClient : public Client
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
HttpSuccess = 0,
|
||||||
|
// The end of the headers has been reached. This consumes the '\n'
|
||||||
|
// Could not connect to the server
|
||||||
|
HttpErrConnectionFailed =-1,
|
||||||
|
// This call was made when the HttpClient class wasn't expecting it
|
||||||
|
// to be called. Usually indicates your code is using the class
|
||||||
|
// incorrectly
|
||||||
|
HttpErrAPI =-2,
|
||||||
|
// Spent too long waiting for a reply
|
||||||
|
HttpErrTimedOut =-3,
|
||||||
|
// The response from the server is invalid, is it definitely an HTTP
|
||||||
|
// server?
|
||||||
|
HttpErrInvalidResponse =-4,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const int kNoContentLengthHeader =-1;
|
||||||
|
static const char* kUserAgent;
|
||||||
|
static const char* kGet;
|
||||||
|
static const char* kPost;
|
||||||
|
static const char* kPut;
|
||||||
|
static const char* kDelete;
|
||||||
|
|
||||||
|
HttpClient(Client& aClient);
|
||||||
|
|
||||||
|
/** Connect to the server and start to send a GET request.
|
||||||
|
@param aServerName Name of the server being connected to. If NULL, the
|
||||||
|
"Host" header line won't be sent
|
||||||
|
@param aServerPort Port to connect to on the server
|
||||||
|
@param aURLPath Url to request
|
||||||
|
@param aUserAgent User-Agent string to send. If NULL the default
|
||||||
|
user-agent kUserAgent will be sent
|
||||||
|
@param aAcceptList List of MIME types that the client will accept. If
|
||||||
|
NULL the "Accept" header line won't be sent
|
||||||
|
@return 0 if successful, else error
|
||||||
|
*/
|
||||||
|
int get(const char* aServerName, uint16_t aServerPort,
|
||||||
|
const char* aURLPath, const char* aUserAgent =NULL,
|
||||||
|
const char* aAcceptList =NULL)
|
||||||
|
{ return startRequest(aServerName, aServerPort, aURLPath, kGet, aUserAgent, aAcceptList); }
|
||||||
|
|
||||||
|
/** Connect to the server and start to send a GET request. This version connects
|
||||||
|
doesn't perform a DNS lookup and just connects to the given IP address.
|
||||||
|
@param aServerAddress IP address of the server to connect to
|
||||||
|
@param aServerPort Port to connect to on the server
|
||||||
|
@param aServerName Name of the server being connected to. If NULL, the
|
||||||
|
"Host" header line won't be sent
|
||||||
|
@param aURLPath Url to request
|
||||||
|
@param aUserAgent User-Agent string to send. If NULL the default
|
||||||
|
user-agent kUserAgent will be sent
|
||||||
|
@param aAcceptList List of MIME types that the client will accept. If
|
||||||
|
NULL the "Accept" header line won't be sent
|
||||||
|
@return 0 if successful, else error
|
||||||
|
*/
|
||||||
|
int get(const IPAddress& aServerAddress,
|
||||||
|
uint16_t aServerPort,
|
||||||
|
const char* aServerName,
|
||||||
|
const char* aURLPath,
|
||||||
|
const char* aUserAgent =NULL,
|
||||||
|
const char* aAcceptList =NULL)
|
||||||
|
{ return startRequest(aServerAddress, aServerPort, aServerName, aURLPath, kGet, aUserAgent, aAcceptList); }
|
||||||
|
|
||||||
|
/** Connect to the server and start to send a POST request.
|
||||||
|
@param aServerName Name of the server being connected to. If NULL, the
|
||||||
|
"Host" header line won't be sent
|
||||||
|
@param aServerPort Port to connect to on the server
|
||||||
|
@param aURLPath Url to request
|
||||||
|
@param aUserAgent User-Agent string to send. If NULL the default
|
||||||
|
user-agent kUserAgent will be sent
|
||||||
|
@param aAcceptList List of MIME types that the client will accept. If
|
||||||
|
NULL the "Accept" header line won't be sent
|
||||||
|
@return 0 if successful, else error
|
||||||
|
*/
|
||||||
|
int post(const char* aServerName,
|
||||||
|
uint16_t aServerPort,
|
||||||
|
const char* aURLPath,
|
||||||
|
const char* aUserAgent =NULL,
|
||||||
|
const char* aAcceptList =NULL)
|
||||||
|
{ return startRequest(aServerName, aServerPort, aURLPath, kPost, aUserAgent, aAcceptList); }
|
||||||
|
|
||||||
|
/** Connect to the server and start to send a POST request. This version connects
|
||||||
|
doesn't perform a DNS lookup and just connects to the given IP address.
|
||||||
|
@param aServerAddress IP address of the server to connect to
|
||||||
|
@param aServerPort Port to connect to on the server
|
||||||
|
@param aServerName Name of the server being connected to. If NULL, the
|
||||||
|
"Host" header line won't be sent
|
||||||
|
@param aURLPath Url to request
|
||||||
|
@param aUserAgent User-Agent string to send. If NULL the default
|
||||||
|
user-agent kUserAgent will be sent
|
||||||
|
@param aAcceptList List of MIME types that the client will accept. If
|
||||||
|
NULL the "Accept" header line won't be sent
|
||||||
|
@return 0 if successful, else error
|
||||||
|
*/
|
||||||
|
int post(const IPAddress& aServerAddress,
|
||||||
|
uint16_t aServerPort,
|
||||||
|
const char* aServerName,
|
||||||
|
const char* aURLPath,
|
||||||
|
const char* aUserAgent =NULL,
|
||||||
|
const char* aAcceptList =NULL)
|
||||||
|
{ return startRequest(aServerAddress, aServerPort, aServerName, aURLPath, kPost, aUserAgent, aAcceptList); }
|
||||||
|
|
||||||
|
/** Connect to the server and start to send a PUT request.
|
||||||
|
@param aServerName Name of the server being connected to. If NULL, the
|
||||||
|
"Host" header line won't be sent
|
||||||
|
@param aServerPort Port to connect to on the server
|
||||||
|
@param aURLPath Url to request
|
||||||
|
@param aUserAgent User-Agent string to send. If NULL the default
|
||||||
|
user-agent kUserAgent will be sent
|
||||||
|
@param aAcceptList List of MIME types that the client will accept. If
|
||||||
|
NULL the "Accept" header line won't be sent
|
||||||
|
@return 0 if successful, else error
|
||||||
|
*/
|
||||||
|
int put(const char* aServerName,
|
||||||
|
uint16_t aServerPort,
|
||||||
|
const char* aURLPath,
|
||||||
|
const char* aUserAgent =NULL,
|
||||||
|
const char* aAcceptList =NULL)
|
||||||
|
{ return startRequest(aServerName, aServerPort, aURLPath, kPut, aUserAgent, aAcceptList); }
|
||||||
|
|
||||||
|
/** Connect to the server and start to send a PUT request. This version connects
|
||||||
|
doesn't perform a DNS lookup and just connects to the given IP address.
|
||||||
|
@param aServerAddress IP address of the server to connect to
|
||||||
|
@param aServerPort Port to connect to on the server
|
||||||
|
@param aServerName Name of the server being connected to. If NULL, the
|
||||||
|
"Host" header line won't be sent
|
||||||
|
@param aURLPath Url to request
|
||||||
|
@param aUserAgent User-Agent string to send. If NULL the default
|
||||||
|
user-agent kUserAgent will be sent
|
||||||
|
@param aAcceptList List of MIME types that the client will accept. If
|
||||||
|
NULL the "Accept" header line won't be sent
|
||||||
|
@return 0 if successful, else error
|
||||||
|
*/
|
||||||
|
int put(const IPAddress& aServerAddress,
|
||||||
|
uint16_t aServerPort,
|
||||||
|
const char* aServerName,
|
||||||
|
const char* aURLPath,
|
||||||
|
const char* aUserAgent =NULL,
|
||||||
|
const char* aAcceptList =NULL)
|
||||||
|
{ return startRequest(aServerAddress, aServerPort, aServerName, aURLPath, kPut, aUserAgent, aAcceptList); }
|
||||||
|
|
||||||
|
/** Connect to the server and start to send the request.
|
||||||
|
@param aServerName Name of the server being connected to.
|
||||||
|
@param aServerPort Port to connect to on the server
|
||||||
|
@param aURLPath Url to request
|
||||||
|
@param aHttpMethod Type of HTTP request to make, e.g. "GET", "POST", etc.
|
||||||
|
@param aUserAgent User-Agent string to send. If NULL the default
|
||||||
|
user-agent kUserAgent will be sent
|
||||||
|
@param aAcceptList List of MIME types that the client will accept. If
|
||||||
|
NULL the "Accept" header line won't be sent
|
||||||
|
@return 0 if successful, else error
|
||||||
|
*/
|
||||||
|
int startRequest(const char* aServerName,
|
||||||
|
uint16_t aServerPort,
|
||||||
|
const char* aURLPath,
|
||||||
|
const char* aHttpMethod,
|
||||||
|
const char* aUserAgent,
|
||||||
|
const char* aAcceptList);
|
||||||
|
|
||||||
|
/** Connect to the server and start to send the request.
|
||||||
|
@param aServerAddress IP address of the server to connect to.
|
||||||
|
@param aServerPort Port to connect to on the server
|
||||||
|
@param aServerName Name of the server being connected to. If NULL, the
|
||||||
|
"Host" header line won't be sent
|
||||||
|
@param aURLPath Url to request
|
||||||
|
@param aHttpMethod Type of HTTP request to make, e.g. "GET", "POST", etc.
|
||||||
|
@param aUserAgent User-Agent string to send. If NULL the default
|
||||||
|
user-agent kUserAgent will be sent
|
||||||
|
@param aAcceptList List of MIME types that the client will accept. If
|
||||||
|
NULL the "Accept" header line won't be sent
|
||||||
|
@return 0 if successful, else error
|
||||||
|
*/
|
||||||
|
int startRequest(const IPAddress& aServerAddress,
|
||||||
|
uint16_t aServerPort,
|
||||||
|
const char* aServerName,
|
||||||
|
const char* aURLPath,
|
||||||
|
const char* aHttpMethod,
|
||||||
|
const char* aUserAgent,
|
||||||
|
const char* aAcceptList);
|
||||||
|
|
||||||
|
/** Send an additional header line. This can only be called in between the
|
||||||
|
calls to startRequest and finishRequest.
|
||||||
|
@param aHeader Header line to send, in its entirety (but without the
|
||||||
|
trailing CRLF. E.g. "Authorization: Basic YQDDCAIGES"
|
||||||
|
*/
|
||||||
|
void sendHeader(const char* aHeader);
|
||||||
|
|
||||||
|
/** Send an additional header line. This is an alternate form of
|
||||||
|
sendHeader() which takes the header name and content as separate strings.
|
||||||
|
The call will add the ": " to separate the header, so for example, to
|
||||||
|
send a XXXXXX header call sendHeader("XXXXX", "Something")
|
||||||
|
@param aHeaderName Type of header being sent
|
||||||
|
@param aHeaderValue Value for that header
|
||||||
|
*/
|
||||||
|
void sendHeader(const char* aHeaderName, const char* aHeaderValue);
|
||||||
|
|
||||||
|
/** Send an additional header line. This is an alternate form of
|
||||||
|
sendHeader() which takes the header name and content separately but where
|
||||||
|
the value is provided as an integer.
|
||||||
|
The call will add the ": " to separate the header, so for example, to
|
||||||
|
send a XXXXXX header call sendHeader("XXXXX", 123)
|
||||||
|
@param aHeaderName Type of header being sent
|
||||||
|
@param aHeaderValue Value for that header
|
||||||
|
*/
|
||||||
|
void sendHeader(const char* aHeaderName, const int aHeaderValue);
|
||||||
|
|
||||||
|
/** Send a basic authentication header. This will encode the given username
|
||||||
|
and password, and send them in suitable header line for doing Basic
|
||||||
|
Authentication.
|
||||||
|
@param aUser Username for the authorization
|
||||||
|
@param aPassword Password for the user aUser
|
||||||
|
*/
|
||||||
|
void sendBasicAuth(const char* aUser, const char* aPassword);
|
||||||
|
|
||||||
|
/** Finish sending the HTTP request. This basically just sends the blank
|
||||||
|
line to signify the end of the request
|
||||||
|
*/
|
||||||
|
void finishRequest();
|
||||||
|
|
||||||
|
/** Get the HTTP status code contained in the response.
|
||||||
|
For example, 200 for successful request, 404 for file not found, etc.
|
||||||
|
*/
|
||||||
|
int responseStatusCode();
|
||||||
|
|
||||||
|
/** Read the next character of the response headers.
|
||||||
|
This functions in the same way as read() but to be used when reading
|
||||||
|
through the headers. Check whether or not the end of the headers has
|
||||||
|
been reached by calling endOfHeadersReached(), although after that point
|
||||||
|
this will still return data as read() would, but slightly less efficiently
|
||||||
|
@return The next character of the response headers
|
||||||
|
*/
|
||||||
|
int readHeader();
|
||||||
|
|
||||||
|
/** Skip any response headers to get to the body.
|
||||||
|
Use this if you don't want to do any special processing of the headers
|
||||||
|
returned in the response. You can also use it after you've found all of
|
||||||
|
the headers you're interested in, and just want to get on with processing
|
||||||
|
the body.
|
||||||
|
@return HttpSuccess if successful, else an error code
|
||||||
|
*/
|
||||||
|
int skipResponseHeaders();
|
||||||
|
|
||||||
|
/** Test whether all of the response headers have been consumed.
|
||||||
|
@return true if we are now processing the response body, else false
|
||||||
|
*/
|
||||||
|
bool endOfHeadersReached() { return (iState == eReadingBody); };
|
||||||
|
|
||||||
|
/** Test whether the end of the body has been reached.
|
||||||
|
Only works if the Content-Length header was returned by the server
|
||||||
|
@return true if we are now at the end of the body, else false
|
||||||
|
*/
|
||||||
|
bool endOfBodyReached();
|
||||||
|
virtual bool eof() { return endOfBodyReached(); };
|
||||||
|
|
||||||
|
/** Return the length of the body.
|
||||||
|
@return Length of the body, in bytes, or kNoContentLengthHeader if no
|
||||||
|
Content-Length header was returned by the server
|
||||||
|
*/
|
||||||
|
int contentLength() { return iContentLength; };
|
||||||
|
|
||||||
|
// Inherited from Print
|
||||||
|
virtual void write(uint8_t aByte) { iClient-> write(aByte); };
|
||||||
|
virtual void write(const char *aStr) { iClient->write(aStr); };
|
||||||
|
virtual void write(const uint8_t *aBuffer, size_t aSize) { iClient->write(aBuffer, aSize); };
|
||||||
|
// Inherited from Stream
|
||||||
|
virtual int available() { return iClient->available(); };
|
||||||
|
/** Read the next byte from the server.
|
||||||
|
@return Byte read or -1 if there are no bytes available.
|
||||||
|
*/
|
||||||
|
virtual int read();
|
||||||
|
virtual int peek() { return iClient->peek(); };
|
||||||
|
virtual void flush() { return iClient->flush(); };
|
||||||
|
|
||||||
|
// Inherited from Client
|
||||||
|
virtual int connect(IPAddress ip, uint16_t port) { return iClient->connect(ip, port); };
|
||||||
|
virtual int connect(const char *host, uint16_t port) { return iClient->connect(host, port); };
|
||||||
|
virtual void stop();
|
||||||
|
virtual uint8_t connected() { iClient->connected(); };
|
||||||
|
protected:
|
||||||
|
/** Reset internal state data back to the "just initialised" state
|
||||||
|
*/
|
||||||
|
void resetState();
|
||||||
|
/** Send the first part of the request and the initial headers.
|
||||||
|
@param aServerName Name of the server being connected to. If NULL, the
|
||||||
|
"Host" header line won't be sent
|
||||||
|
@param aURLPath Url to request
|
||||||
|
@param aHttpMethod Type of HTTP request to make, e.g. "GET", "POST", etc.
|
||||||
|
@param aUserAgent User-Agent string to send. If NULL the default
|
||||||
|
user-agent kUserAgent will be sent
|
||||||
|
@param aAcceptList List of MIME types that the client will accept. If
|
||||||
|
NULL the "Accept" header line won't be sent
|
||||||
|
@return 0 if successful, else error
|
||||||
|
*/
|
||||||
|
int sendInitialHeaders(const char* aServerName,
|
||||||
|
const char* aURLPath,
|
||||||
|
const char* aHttpMethod,
|
||||||
|
const char* aUserAgent,
|
||||||
|
const char* aAcceptList);
|
||||||
|
|
||||||
|
// Number of milliseconds that we wait each time there isn't any data
|
||||||
|
// available to be read (during status code and header processing)
|
||||||
|
static const int kHttpWaitForDataDelay = 1000;
|
||||||
|
// Number of milliseconds that we'll wait in total without receiveing any
|
||||||
|
// data before returning HttpErrTimedOut (during status code and header
|
||||||
|
// processing)
|
||||||
|
static const int kHttpResponseTimeout = 30*1000;
|
||||||
|
static const char* kContentLengthPrefix;
|
||||||
|
typedef enum {
|
||||||
|
eIdle,
|
||||||
|
eRequestStarted,
|
||||||
|
eRequestSent,
|
||||||
|
eReadingStatusCode,
|
||||||
|
eStatusCodeRead,
|
||||||
|
eReadingContentLength,
|
||||||
|
eSkipToEndOfHeader,
|
||||||
|
eLineStartingCRFound,
|
||||||
|
eReadingBody
|
||||||
|
} tHttpState;
|
||||||
|
// Ethernet client we're using
|
||||||
|
Client* iClient;
|
||||||
|
// Current state of the finite-state-machine
|
||||||
|
tHttpState iState;
|
||||||
|
// Stores the status code for the response, once known
|
||||||
|
int iStatusCode;
|
||||||
|
// Stores the value of the Content-Length header, if present
|
||||||
|
int iContentLength;
|
||||||
|
// How many bytes of the response body have been read by the user
|
||||||
|
int iBodyLengthConsumed;
|
||||||
|
// How far through a Content-Length header prefix we are
|
||||||
|
const char* iContentLengthPtr;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
124
examples/SimpleHttpExample/SimpleHttpExample.pde
Normal file
124
examples/SimpleHttpExample/SimpleHttpExample.pde
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
// (c) Copyright 2010-2011 MCQN Ltd.
|
||||||
|
// Released under Apache License, version 2.0
|
||||||
|
//
|
||||||
|
// Simple example to show how to use the HttpClient library
|
||||||
|
// Get's the web page given at http://<kHostname><kPath> and
|
||||||
|
// outputs the content to the serial port
|
||||||
|
|
||||||
|
#include <SPI.h>
|
||||||
|
#include <HttpClient.h>
|
||||||
|
#include <b64.h>
|
||||||
|
#include <Ethernet.h>
|
||||||
|
#include <Client.h>
|
||||||
|
|
||||||
|
// This example downloads the URL "http://arduino.cc/"
|
||||||
|
|
||||||
|
// Name of the server we want to connect to
|
||||||
|
char* kHostname = "arduino.cc";
|
||||||
|
// Path to download (this is the bit after the hostname in the URL
|
||||||
|
// that you want to download
|
||||||
|
const char* kPath = "/";
|
||||||
|
|
||||||
|
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
|
||||||
|
|
||||||
|
// Number of milliseconds to wait without receiving any data before we give up
|
||||||
|
const int kNetworkTimeout = 30*1000;
|
||||||
|
// Number of milliseconds to wait if no data is available before trying again
|
||||||
|
const int kNetworkDelay = 1000;
|
||||||
|
|
||||||
|
void setup()
|
||||||
|
{
|
||||||
|
// initialize serial communications at 9600 bps:
|
||||||
|
Serial.begin(9600);
|
||||||
|
|
||||||
|
while (Ethernet.begin(mac) != 1)
|
||||||
|
{
|
||||||
|
#ifdef LOGGING
|
||||||
|
Serial.println("Error getting IP address via DHCP, trying again...");
|
||||||
|
#endif
|
||||||
|
delay(15000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop()
|
||||||
|
{
|
||||||
|
int err =0;
|
||||||
|
|
||||||
|
Client c;
|
||||||
|
HttpClient http(c);
|
||||||
|
|
||||||
|
err = http.get(kHostname, 80, kPath);
|
||||||
|
if (err == 0)
|
||||||
|
{
|
||||||
|
Serial.println("startedRequest ok");
|
||||||
|
|
||||||
|
http.finishRequest();
|
||||||
|
|
||||||
|
err = http.responseStatusCode();
|
||||||
|
if (err >= 0)
|
||||||
|
{
|
||||||
|
Serial.print("Got status code: ");
|
||||||
|
Serial.println(err);
|
||||||
|
|
||||||
|
// Usually you'd check that the response code is 200 or a
|
||||||
|
// similar "success" code (200-299) before carrying on,
|
||||||
|
// but we'll print out whatever response we get
|
||||||
|
|
||||||
|
err = http.skipResponseHeaders();
|
||||||
|
if (err >= 0)
|
||||||
|
{
|
||||||
|
int bodyLen = http.contentLength();
|
||||||
|
Serial.print("Content length is: ");
|
||||||
|
Serial.println(bodyLen);
|
||||||
|
Serial.println();
|
||||||
|
Serial.println("Body returned follows:");
|
||||||
|
|
||||||
|
// Now we've got to the body, so we can print it out
|
||||||
|
unsigned long timeoutStart = millis();
|
||||||
|
char c;
|
||||||
|
// Whilst we haven't timed out & haven't reached the end of the body
|
||||||
|
while (http.connected() &&
|
||||||
|
( (millis() - timeoutStart) < kNetworkTimeout ))
|
||||||
|
{
|
||||||
|
if (http.available())
|
||||||
|
{
|
||||||
|
c = http.read();
|
||||||
|
// Print out this character
|
||||||
|
Serial.print(c);
|
||||||
|
|
||||||
|
bodyLen--;
|
||||||
|
// We read something, reset the timeout counter
|
||||||
|
timeoutStart = millis();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// We haven't got any data, so let's pause to allow some to
|
||||||
|
// arrive
|
||||||
|
delay(kNetworkDelay);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Serial.print("Failed to skip response headers: ");
|
||||||
|
Serial.println(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Serial.print("Getting response failed: ");
|
||||||
|
Serial.println(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Serial.print("Connect failed: ");
|
||||||
|
Serial.println(err);
|
||||||
|
}
|
||||||
|
http.stop();
|
||||||
|
|
||||||
|
// And just stop, now that we've tried a download
|
||||||
|
while(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
33
keywords.txt
Normal file
33
keywords.txt
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
#######################################
|
||||||
|
# Syntax Coloring Map For HttpClient
|
||||||
|
#######################################
|
||||||
|
|
||||||
|
#######################################
|
||||||
|
# Datatypes (KEYWORD1)
|
||||||
|
#######################################
|
||||||
|
|
||||||
|
HttpClient KEYWORD1
|
||||||
|
|
||||||
|
#######################################
|
||||||
|
# Methods and Functions (KEYWORD2)
|
||||||
|
#######################################
|
||||||
|
|
||||||
|
get KEYWORD2
|
||||||
|
post KEYWORD2
|
||||||
|
put KEYWORD2
|
||||||
|
startRequest KEYWORD2
|
||||||
|
sendHeader KEYWORD2
|
||||||
|
sendBasicAuth KEYWORD2
|
||||||
|
finishRequest KEYWORD2
|
||||||
|
responseStatusCode KEYWORD2
|
||||||
|
readHeader KEYWORD2
|
||||||
|
skipResponseHeaders KEYWORD2
|
||||||
|
endOfHeadersReached KEYWORD2
|
||||||
|
endOfBodyReached KEYWORD2
|
||||||
|
eof KEYWORD2
|
||||||
|
contentLength KEYWORD2
|
||||||
|
|
||||||
|
#######################################
|
||||||
|
# Constants (LITERAL1)
|
||||||
|
#######################################
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user