1 // Class to simplify HTTP fetching on Arduino
2 // (c) Copyright 2010-2011 MCQN Ltd
3 // Released under Apache License, version 2.0
5 #include "HttpClient.h"
7 #ifdef PROXY_ENABLED // currently disabled as introduces dependency on Dns.h in Ethernet
13 // Initialize constants
14 const char* HttpClient::kUserAgent = "Arduino/2.0";
15 const char* HttpClient::kGet = "GET";
16 const char* HttpClient::kPost = "POST";
17 const char* HttpClient::kPut = "PUT";
18 const char* HttpClient::kDelete = "DELETE";
19 const char* HttpClient::kContentLengthPrefix = "Content-Length: ";
21 #ifdef PROXY_ENABLED // currently disabled as introduces dependency on Dns.h in Ethernet
22 HttpClient::HttpClient(Client& aClient, const char* aProxy, uint16_t aProxyPort)
23 : iClient(&aClient), iProxyPort(aProxyPort)
28 // Resolve the IP address for the proxy
30 dns.begin(Ethernet.dnsServerIP());
31 // Not ideal that we discard any errors here, but not a lot we can do in the ctor
32 // and we'll get a connect error later anyway
33 (void)dns.getHostByName(aProxy, iProxyAddress);
37 HttpClient::HttpClient(Client& aClient)
38 : iClient(&aClient), iProxyPort(0)
44 void HttpClient::resetState()
49 iBodyLengthConsumed = 0;
50 iContentLengthPtr = 0;
53 void HttpClient::stop()
59 void HttpClient::beginRequest()
61 iState = eRequestStarted;
64 int HttpClient::startRequest(const char* aServerName, uint16_t aServerPort, const char* aURLPath, const char* aHttpMethod, const char* aUserAgent)
66 tHttpState initialState = iState;
67 if ((eIdle != iState) && (eRequestStarted != iState))
69 return HTTP_ERROR_API;
74 if (!iClient->connect(iProxyAddress, iProxyPort) > 0)
77 Serial.println("Proxy connection failed");
79 return HTTP_ERROR_CONNECTION_FAILED;
84 if (!iClient->connect(aServerName, aServerPort) > 0)
87 Serial.println("Connection failed");
89 return HTTP_ERROR_CONNECTION_FAILED;
93 // Now we're connected, send the first part of the request
94 int ret = sendInitialHeaders(aServerName, IPAddress(0,0,0,0), aServerPort, aURLPath, aHttpMethod, aUserAgent);
95 if ((initialState == eIdle) && (HTTP_SUCCESS == ret))
97 // This was a simple version of the API, so terminate the headers now
100 // else we'll call it in endRequest or in the first call to print, etc.
105 int HttpClient::startRequest(const IPAddress& aServerAddress, const char* aServerName, uint16_t aServerPort, const char* aURLPath, const char* aHttpMethod, const char* aUserAgent)
107 tHttpState initialState = iState;
108 if ((eIdle != iState) && (eRequestStarted != iState))
110 return HTTP_ERROR_API;
115 if (!iClient->connect(iProxyAddress, iProxyPort) > 0)
118 Serial.println("Proxy connection failed");
120 return HTTP_ERROR_CONNECTION_FAILED;
125 if (!iClient->connect(aServerAddress, aServerPort) > 0)
128 Serial.println("Connection failed");
130 return HTTP_ERROR_CONNECTION_FAILED;
134 // Now we're connected, send the first part of the request
135 int ret = sendInitialHeaders(aServerName, aServerAddress, aServerPort, aURLPath, aHttpMethod, aUserAgent);
136 if ((initialState == eIdle) && (HTTP_SUCCESS == ret))
138 // This was a simple version of the API, so terminate the headers now
141 // else we'll call it in endRequest or in the first call to print, etc.
146 int HttpClient::sendInitialHeaders(const char* aServerName, IPAddress aServerIP, uint16_t aPort, const char* aURLPath, const char* aHttpMethod, const char* aUserAgent)
149 Serial.println("Connected");
151 // Send the HTTP command, i.e. "GET /somepath/ HTTP/1.0"
152 iClient->print(aHttpMethod);
156 // We're going through a proxy, send a full URL
157 iClient->print("http://");
160 // We've got a server name, so use it
161 iClient->print(aServerName);
165 // We'll have to use the IP address
166 iClient->print(aServerIP);
168 if (aPort != kHttpPort)
171 iClient->print(aPort);
174 iClient->print(aURLPath);
175 iClient->println(" HTTP/1.1");
176 // The host header, if required
179 iClient->print("Host: ");
180 iClient->print(aServerName);
181 if (aPort != kHttpPort)
184 iClient->print(aPort);
188 // And user-agent string
189 iClient->print("User-Agent: ");
192 iClient->println(aUserAgent);
196 iClient->println(kUserAgent);
199 // Everything has gone well
200 iState = eRequestStarted;
204 void HttpClient::sendHeader(const char* aHeader)
206 iClient->println(aHeader);
209 void HttpClient::sendHeader(const char* aHeaderName, const char* aHeaderValue)
211 iClient->print(aHeaderName);
212 iClient->print(": ");
213 iClient->println(aHeaderValue);
216 void HttpClient::sendHeader(const char* aHeaderName, const int aHeaderValue)
218 iClient->print(aHeaderName);
219 iClient->print(": ");
220 iClient->println(aHeaderValue);
223 void HttpClient::sendBasicAuth(const char* aUser, const char* aPassword)
225 // Send the initial part of this header line
226 iClient->print("Authorization: Basic ");
227 // Now Base64 encode "aUser:aPassword" and send that
228 // This seems trickier than it should be but it's mostly to avoid either
229 // (a) some arbitrarily sized buffer which hopes to be big enough, or
230 // (b) allocating and freeing memory
231 // ...so we'll loop through 3 bytes at a time, outputting the results as we
233 // In Base64, each 3 bytes of unencoded data become 4 bytes of encoded data
234 unsigned char input[3];
235 unsigned char output[5]; // Leave space for a '\0' terminator so we can easily print
236 int userLen = strlen(aUser);
237 int passwordLen = strlen(aPassword);
239 for (int i = 0; i < (userLen+1+passwordLen); i++)
241 // Copy the relevant input byte into the input
244 input[inputOffset++] = aUser[i];
246 else if (i == userLen)
248 input[inputOffset++] = ':';
252 input[inputOffset++] = aPassword[i-(userLen+1)];
254 // See if we've got a chunk to encode
255 if ( (inputOffset == 3) || (i == userLen+passwordLen) )
257 // We've either got to a 3-byte boundary, or we've reached then end
258 b64_encode(input, inputOffset, output, 4);
259 // NUL-terminate the output string
262 iClient->print((char*)output);
263 // FIXME We might want to fill output with '=' characters if b64_encode doesn't
264 // FIXME do it for us when we're encoding the final chunk
268 // And end the header we've sent
272 void HttpClient::finishHeaders()
275 iState = eRequestSent;
278 void HttpClient::endRequest()
280 if (iState < eRequestSent)
282 // We still need to finish off the headers
285 // else the end of headers has already been sent, so nothing to do here
288 int HttpClient::responseStatusCode()
290 if (iState < eRequestSent)
292 return HTTP_ERROR_API;
294 // The first line will be of the form Status-Line:
295 // HTTP-Version SP Status-Code SP Reason-Phrase CRLF
296 // Where HTTP-Version is of the form:
297 // HTTP-Version = "HTTP" "/" 1*DIGIT "." 1*DIGIT
302 // Make sure the status code is reset, and likewise the state. This
303 // lets us easily cope with 1xx informational responses by just
304 // ignoring them really, and reading the next line for a proper response
306 iState = eRequestSent;
308 unsigned long timeoutStart = millis();
309 // Psuedo-regexp we're expecting before the status-code
310 const char* statusPrefix = "HTTP/*.* ";
311 const char* statusPtr = statusPrefix;
312 // Whilst we haven't timed out & haven't reached the end of the headers
313 while ((c != '\n') &&
314 ( (millis() - timeoutStart) < kHttpResponseTimeout ))
324 // We haven't reached the status code yet
325 if ( (*statusPtr == '*') || (*statusPtr == c) )
327 // This character matches, just move along
329 if (*statusPtr == '\0')
331 // We've reached the end of the prefix
332 iState = eReadingStatusCode;
337 return HTTP_ERROR_INVALID_RESPONSE;
340 case eReadingStatusCode:
343 // This assumes we won't get more than the 3 digits we
345 iStatusCode = iStatusCode*10 + (c - '0');
349 // We've reached the end of the status code
350 // We could sanity check it here or double-check for ' '
351 // rather than anything else, but let's be lenient
352 iState = eStatusCodeRead;
355 case eStatusCodeRead:
356 // We're just waiting for the end of the line now
359 // We read something, reset the timeout counter
360 timeoutStart = millis();
365 // We haven't got any data, so let's pause to allow some to
367 delay(kHttpWaitForDataDelay);
370 if ( (c == '\n') && (iStatusCode < 200) )
372 // We've reached the end of an informational status line
373 c = '\0'; // Clear c so we'll go back into the data reading loop
376 // If we've read a status code successfully but it's informational (1xx)
377 // loop back to the start
378 while ( (iState == eStatusCodeRead) && (iStatusCode < 200) );
380 if ( (c == '\n') && (iState == eStatusCodeRead) )
382 // We've read the status-line successfully
387 // We must've timed out before we reached the end of the line
388 return HTTP_ERROR_TIMED_OUT;
392 // This wasn't a properly formed status line, or at least not one we
394 return HTTP_ERROR_INVALID_RESPONSE;
398 int HttpClient::skipResponseHeaders()
400 // Just keep reading until we finish reading the headers or time out
401 unsigned long timeoutStart = millis();
402 // Whilst we haven't timed out & haven't reached the end of the headers
403 while ((!endOfHeadersReached()) &&
404 ( (millis() - timeoutStart) < kHttpResponseTimeout ))
409 // We read something, reset the timeout counter
410 timeoutStart = millis();
414 // We haven't got any data, so let's pause to allow some to
416 delay(kHttpWaitForDataDelay);
419 if (endOfHeadersReached())
426 // We must've timed out
427 return HTTP_ERROR_TIMED_OUT;
431 bool HttpClient::endOfBodyReached()
433 if (endOfHeadersReached() && (contentLength() != kNoContentLengthHeader))
435 // We've got to the body and we know how long it will be
436 return (iBodyLengthConsumed >= contentLength());
441 int HttpClient::read()
443 #if 0 // Fails on WiFi because multi-byte read seems to be broken
445 int ret = read(b, 1);
455 int ret = iClient->read();
458 if (endOfHeadersReached() && iContentLength > 0)
460 // We're outputting the body now and we've seen a Content-Length header
461 // So keep track of how many bytes are left
462 iBodyLengthConsumed++;
469 int HttpClient::read(uint8_t *buf, size_t size)
471 int ret =iClient->read(buf, size);
472 if (endOfHeadersReached() && iContentLength > 0)
474 // We're outputting the body now and we've seen a Content-Length header
475 // So keep track of how many bytes are left
478 iBodyLengthConsumed += ret;
484 int HttpClient::readHeader()
488 if (endOfHeadersReached())
490 // We've passed the headers, but rather than return an error, we'll just
491 // act as a slightly less efficient version of read()
495 // Whilst reading out the headers to whoever wants them, we'll keep an
496 // eye out for the "Content-Length" header
499 case eStatusCodeRead:
500 // We're at the start of a line, or somewhere in the middle of reading
501 // the Content-Length prefix
502 if (*iContentLengthPtr == c)
504 // This character matches, just move along
506 if (*iContentLengthPtr == '\0')
508 // We've reached the end of the prefix
509 iState = eReadingContentLength;
510 // Just in case we get multiple Content-Length headers, this
511 // will ensure we just get the value of the last one
515 else if ((iContentLengthPtr == kContentLengthPrefix) && (c == '\r'))
517 // We've found a '\r' at the start of a line, so this is probably
518 // the end of the headers
519 iState = eLineStartingCRFound;
523 // This isn't the Content-Length header, skip to the end of the line
524 iState = eSkipToEndOfHeader;
527 case eReadingContentLength:
530 iContentLength = iContentLength*10 + (c - '0');
534 // We've reached the end of the content length
535 // We could sanity check it here or double-check for "\r\n"
536 // rather than anything else, but let's be lenient
537 iState = eSkipToEndOfHeader;
540 case eLineStartingCRFound:
543 iState = eReadingBody;
547 // We're just waiting for the end of the line now
551 if ( (c == '\n') && !endOfHeadersReached() )
553 // We've got to the end of this line, start processing again
554 iState = eStatusCodeRead;
555 iContentLengthPtr = kContentLengthPrefix;
557 // And return the character read to whoever wants it