]> git.piffa.net Git - arduino/blob - books/pdummies/Libraries/HttpClient/HttpClient.h
first commit
[arduino] / books / pdummies / Libraries / HttpClient / HttpClient.h
1 // Class to simplify HTTP fetching on Arduino
2 // (c) Copyright MCQN Ltd. 2010-2012
3 // Released under Apache License, version 2.0
4
5 #ifndef HttpClient_h
6 #define HttpClient_h
7
8 #include <Arduino.h>
9 #include <IPAddress.h>
10 #include "Client.h"
11
12 static const int HTTP_SUCCESS =0;
13 // The end of the headers has been reached.  This consumes the '\n'
14 // Could not connect to the server
15 static const int HTTP_ERROR_CONNECTION_FAILED =-1;
16 // This call was made when the HttpClient class wasn't expecting it
17 // to be called.  Usually indicates your code is using the class
18 // incorrectly
19 static const int HTTP_ERROR_API =-2;
20 // Spent too long waiting for a reply
21 static const int HTTP_ERROR_TIMED_OUT =-3;
22 // The response from the server is invalid, is it definitely an HTTP
23 // server?
24 static const int HTTP_ERROR_INVALID_RESPONSE =-4;
25
26 class HttpClient : public Client
27 {
28 public:
29     static const int kNoContentLengthHeader =-1;
30     static const int kHttpPort =80;
31     static const char* kUserAgent;
32     static const char* kGet;
33     static const char* kPost;
34     static const char* kPut;
35     static const char* kDelete;
36
37 // FIXME Write longer API request, using port and user-agent, example
38 // FIXME Update tempToPachube example to calculate Content-Length correctly
39
40 #ifdef PROXY_ENABLED // currently disabled as introduces dependency on Dns.h in Ethernet
41     HttpClient(Client& aClient, const char* aProxy =NULL, uint16_t aProxyPort =0);
42 #else
43     HttpClient(Client& aClient);
44 #endif
45
46     /** Start a more complex request.
47         Use this when you need to send additional headers in the request,
48         but you will also need to call endRequest() when you are finished.
49     */
50     void beginRequest();
51
52     /** End a more complex request.
53         Use this when you need to have sent additional headers in the request,
54         but you will also need to call beginRequest() at the start.
55     */
56     void endRequest();
57
58     /** Connect to the server and start to send a GET request.
59       @param aServerName  Name of the server being connected to.  If NULL, the
60                           "Host" header line won't be sent
61       @param aServerPort  Port to connect to on the server
62       @param aURLPath     Url to request
63       @param aUserAgent   User-Agent string to send.  If NULL the default
64                           user-agent kUserAgent will be sent
65       @return 0 if successful, else error
66     */
67     int get(const char* aServerName, uint16_t aServerPort, const char* aURLPath, 
68             const char* aUserAgent =NULL)
69       { return startRequest(aServerName, aServerPort, aURLPath, kGet, aUserAgent); }
70
71     /** Connect to the server and start to send a GET request.
72       @param aServerName  Name of the server being connected to.  If NULL, the
73                           "Host" header line won't be sent
74       @param aURLPath     Url to request
75       @param aUserAgent   User-Agent string to send.  If NULL the default
76                           user-agent kUserAgent will be sent
77       @return 0 if successful, else error
78     */
79     int get(const char* aServerName, const char* aURLPath, const char* aUserAgent =NULL)
80       { return startRequest(aServerName, kHttpPort, aURLPath, kGet, aUserAgent); }
81
82     /** Connect to the server and start to send a GET request.  This version connects
83       doesn't perform a DNS lookup and just connects to the given IP address.
84       @param aServerAddress IP address of the server to connect to
85       @param aServerName    Name of the server being connected to.  If NULL, the
86                             "Host" header line won't be sent
87       @param aServerPort    Port to connect to on the server
88       @param aURLPath       Url to request
89       @param aUserAgent     User-Agent string to send.  If NULL the default
90                             user-agent kUserAgent will be sent
91       @return 0 if successful, else error
92     */
93     int get(const IPAddress& aServerAddress,
94             const char* aServerName, 
95             uint16_t aServerPort,
96             const char* aURLPath, 
97             const char* aUserAgent =NULL)
98       { return startRequest(aServerAddress, aServerName, aServerPort, aURLPath, kGet, aUserAgent); }
99
100     /** Connect to the server and start to send a GET request.  This version connects
101       doesn't perform a DNS lookup and just connects to the given IP address.
102       @param aServerAddress IP address of the server to connect to
103       @param aServerName    Name of the server being connected to.  If NULL, the
104                             "Host" header line won't be sent
105       @param aURLPath       Url to request
106       @param aUserAgent     User-Agent string to send.  If NULL the default
107                             user-agent kUserAgent will be sent
108       @return 0 if successful, else error
109     */
110     int get(const IPAddress& aServerAddress,
111             const char* aServerName, 
112             const char* aURLPath, 
113             const char* aUserAgent =NULL)
114       { return startRequest(aServerAddress, aServerName, kHttpPort, aURLPath, kGet, aUserAgent); }
115
116     /** Connect to the server and start to send a POST request.
117       @param aServerName  Name of the server being connected to.  If NULL, the
118                           "Host" header line won't be sent
119       @param aServerPort  Port to connect to on the server
120       @param aURLPath     Url to request
121       @param aUserAgent   User-Agent string to send.  If NULL the default
122                           user-agent kUserAgent will be sent
123       @return 0 if successful, else error
124     */
125     int post(const char* aServerName, 
126              uint16_t aServerPort,
127              const char* aURLPath, 
128              const char* aUserAgent =NULL)
129       { return startRequest(aServerName, aServerPort, aURLPath, kPost, aUserAgent); }
130
131     /** Connect to the server and start to send a POST request.
132       @param aServerName  Name of the server being connected to.  If NULL, the
133                           "Host" header line won't be sent
134       @param aURLPath     Url to request
135       @param aUserAgent   User-Agent string to send.  If NULL the default
136                           user-agent kUserAgent will be sent
137       @return 0 if successful, else error
138     */
139     int post(const char* aServerName, 
140              const char* aURLPath, 
141              const char* aUserAgent =NULL)
142       { return startRequest(aServerName, kHttpPort, aURLPath, kPost, aUserAgent); }
143
144     /** Connect to the server and start to send a POST request.  This version connects
145       doesn't perform a DNS lookup and just connects to the given IP address.
146       @param aServerAddress IP address of the server to connect to
147       @param aServerName  Name of the server being connected to.  If NULL, the
148                           "Host" header line won't be sent
149       @param aServerPort  Port to connect to on the server
150       @param aURLPath     Url to request
151       @param aUserAgent   User-Agent string to send.  If NULL the default
152                           user-agent kUserAgent will be sent
153       @return 0 if successful, else error
154     */
155     int post(const IPAddress& aServerAddress,
156              const char* aServerName, 
157              uint16_t aServerPort,
158              const char* aURLPath, 
159              const char* aUserAgent =NULL)
160       { return startRequest(aServerAddress, aServerName, aServerPort, aURLPath, kPost, aUserAgent); }
161
162     /** Connect to the server and start to send a POST request.  This version connects
163       doesn't perform a DNS lookup and just connects to the given IP address.
164       @param aServerAddress IP address of the server to connect to
165       @param aServerName  Name of the server being connected to.  If NULL, the
166                           "Host" header line won't be sent
167       @param aURLPath     Url to request
168       @param aUserAgent   User-Agent string to send.  If NULL the default
169                           user-agent kUserAgent will be sent
170       @return 0 if successful, else error
171     */
172     int post(const IPAddress& aServerAddress,
173              const char* aServerName, 
174              const char* aURLPath, 
175              const char* aUserAgent =NULL)
176       { return startRequest(aServerAddress, aServerName, kHttpPort, aURLPath, kPost, aUserAgent); }
177
178     /** Connect to the server and start to send a PUT request.
179       @param aServerName  Name of the server being connected to.  If NULL, the
180                           "Host" header line won't be sent
181       @param aServerPort  Port to connect to on the server
182       @param aURLPath     Url to request
183       @param aUserAgent   User-Agent string to send.  If NULL the default
184                           user-agent kUserAgent will be sent
185       @return 0 if successful, else error
186     */
187     int put(const char* aServerName, 
188             uint16_t aServerPort,
189             const char* aURLPath, 
190             const char* aUserAgent =NULL)
191       { return startRequest(aServerName, aServerPort, aURLPath, kPut, aUserAgent); }
192
193     /** Connect to the server and start to send a PUT request.
194       @param aServerName  Name of the server being connected to.  If NULL, the
195                           "Host" header line won't be sent
196       @param aURLPath     Url to request
197       @param aUserAgent   User-Agent string to send.  If NULL the default
198                           user-agent kUserAgent will be sent
199       @return 0 if successful, else error
200     */
201     int put(const char* aServerName, 
202             const char* aURLPath, 
203             const char* aUserAgent =NULL)
204       { return startRequest(aServerName, kHttpPort, aURLPath, kPut, aUserAgent); }
205
206     /** Connect to the server and start to send a PUT request.  This version connects
207       doesn't perform a DNS lookup and just connects to the given IP address.
208       @param aServerAddress IP address of the server to connect to
209       @param aServerName  Name of the server being connected to.  If NULL, the
210                           "Host" header line won't be sent
211       @param aServerPort  Port to connect to on the server
212       @param aURLPath     Url to request
213       @param aUserAgent   User-Agent string to send.  If NULL the default
214                           user-agent kUserAgent will be sent
215       @return 0 if successful, else error
216     */
217     int put(const IPAddress& aServerAddress,
218             const char* aServerName, 
219             uint16_t aServerPort,
220             const char* aURLPath, 
221             const char* aUserAgent =NULL)
222       { return startRequest(aServerAddress, aServerName, aServerPort, aURLPath, kPut, aUserAgent); }
223
224     /** Connect to the server and start to send a PUT request.  This version connects
225       doesn't perform a DNS lookup and just connects to the given IP address.
226       @param aServerAddress IP address of the server to connect to
227       @param aServerName  Name of the server being connected to.  If NULL, the
228                           "Host" header line won't be sent
229       @param aURLPath     Url to request
230       @param aUserAgent   User-Agent string to send.  If NULL the default
231                           user-agent kUserAgent will be sent
232       @return 0 if successful, else error
233     */
234     int put(const IPAddress& aServerAddress,
235             const char* aServerName, 
236             const char* aURLPath, 
237             const char* aUserAgent =NULL)
238       { return startRequest(aServerAddress, aServerName, kHttpPort, aURLPath, kPut, aUserAgent); }
239
240     /** Connect to the server and start to send the request.
241       @param aServerName  Name of the server being connected to.
242       @param aServerPort  Port to connect to on the server
243       @param aURLPath     Url to request
244       @param aHttpMethod  Type of HTTP request to make, e.g. "GET", "POST", etc.
245       @param aUserAgent   User-Agent string to send.  If NULL the default
246                           user-agent kUserAgent will be sent
247       @return 0 if successful, else error
248     */
249     int startRequest(const char* aServerName,
250                      uint16_t    aServerPort,
251                      const char* aURLPath,
252                      const char* aHttpMethod,
253                      const char* aUserAgent);
254
255     /** Connect to the server and start to send the request.
256       @param aServerAddress IP address of the server to connect to.
257       @param aServerName Name of the server being connected to.  If NULL, the
258                          "Host" header line won't be sent
259       @param aServerPort  Port to connect to on the server
260       @param aURLPath   Url to request
261       @param aHttpMethod  Type of HTTP request to make, e.g. "GET", "POST", etc.
262       @param aUserAgent User-Agent string to send.  If NULL the default
263                         user-agent kUserAgent will be sent
264       @return 0 if successful, else error
265     */
266     int startRequest(const IPAddress& aServerAddress,
267                      const char* aServerName,
268                      uint16_t    aServerPort,
269                      const char* aURLPath,
270                      const char* aHttpMethod,
271                      const char* aUserAgent);
272
273     /** Send an additional header line.  This can only be called in between the
274       calls to startRequest and finishRequest.
275       @param aHeader Header line to send, in its entirety (but without the
276                      trailing CRLF.  E.g. "Authorization: Basic YQDDCAIGES" 
277     */
278     void sendHeader(const char* aHeader);
279
280     /** Send an additional header line.  This is an alternate form of
281       sendHeader() which takes the header name and content as separate strings.
282       The call will add the ": " to separate the header, so for example, to
283       send a XXXXXX header call sendHeader("XXXXX", "Something")
284       @param aHeaderName Type of header being sent
285       @param aHeaderValue Value for that header
286     */
287     void sendHeader(const char* aHeaderName, const char* aHeaderValue);
288
289     /** Send an additional header line.  This is an alternate form of
290       sendHeader() which takes the header name and content separately but where
291       the value is provided as an integer.
292       The call will add the ": " to separate the header, so for example, to
293       send a XXXXXX header call sendHeader("XXXXX", 123)
294       @param aHeaderName Type of header being sent
295       @param aHeaderValue Value for that header
296     */
297     void sendHeader(const char* aHeaderName, const int aHeaderValue);
298
299     /** Send a basic authentication header.  This will encode the given username
300       and password, and send them in suitable header line for doing Basic
301       Authentication.
302       @param aUser Username for the authorization
303       @param aPassword Password for the user aUser
304     */
305     void sendBasicAuth(const char* aUser, const char* aPassword);
306
307     /** Finish sending the HTTP request.  This basically just sends the blank
308       line to signify the end of the request
309     */
310     void finishRequest();
311
312     /** Get the HTTP status code contained in the response.
313       For example, 200 for successful request, 404 for file not found, etc.
314     */
315     int responseStatusCode();
316
317     /** Read the next character of the response headers.
318       This functions in the same way as read() but to be used when reading
319       through the headers.  Check whether or not the end of the headers has
320       been reached by calling endOfHeadersReached(), although after that point
321       this will still return data as read() would, but slightly less efficiently
322       @return The next character of the response headers
323     */
324     int readHeader();
325
326     /** Skip any response headers to get to the body.
327       Use this if you don't want to do any special processing of the headers
328       returned in the response.  You can also use it after you've found all of
329       the headers you're interested in, and just want to get on with processing
330       the body.
331       @return HTTP_SUCCESS if successful, else an error code
332     */
333     int skipResponseHeaders();
334
335     /** Test whether all of the response headers have been consumed.
336       @return true if we are now processing the response body, else false
337     */
338     bool endOfHeadersReached() { return (iState == eReadingBody); };
339
340     /** Test whether the end of the body has been reached.
341       Only works if the Content-Length header was returned by the server
342       @return true if we are now at the end of the body, else false
343     */
344     bool endOfBodyReached();
345     virtual bool endOfStream() { return endOfBodyReached(); };
346     virtual bool completed() { return endOfBodyReached(); };
347
348     /** Return the length of the body.
349       @return Length of the body, in bytes, or kNoContentLengthHeader if no
350       Content-Length header was returned by the server
351     */
352     int contentLength() { return iContentLength; };
353
354     // Inherited from Print
355     // Note: 1st call to these indicates the user is sending the body, so if need
356     // Note: be we should finish the header first
357     virtual size_t write(uint8_t aByte) { if (iState < eRequestSent) { finishHeaders(); }; return iClient-> write(aByte); };
358     virtual size_t write(const uint8_t *aBuffer, size_t aSize) { if (iState < eRequestSent) { finishHeaders(); }; return iClient->write(aBuffer, aSize); };
359     // Inherited from Stream
360     virtual int available() { return iClient->available(); };
361     /** Read the next byte from the server.
362       @return Byte read or -1 if there are no bytes available.
363     */
364     virtual int read();
365     virtual int read(uint8_t *buf, size_t size);
366     virtual int peek() { return iClient->peek(); };
367     virtual void flush() { return iClient->flush(); };
368
369     // Inherited from Client
370     virtual int connect(IPAddress ip, uint16_t port) { return iClient->connect(ip, port); };
371     virtual int connect(const char *host, uint16_t port) { return iClient->connect(host, port); };
372     virtual void stop();
373     virtual uint8_t connected() { iClient->connected(); };
374     virtual operator bool() { return bool(iClient); };
375 protected:
376     /** Reset internal state data back to the "just initialised" state
377     */
378     void resetState();
379
380     /** Send the first part of the request and the initial headers.
381       @param aServerName Name of the server being connected to.  If NULL, the
382                          "Host" header line won't be sent
383       @param aServerIP  IP address of the server (only used if we're going through a
384                         proxy and aServerName is NULL
385       @param aServerPort  Port of the server being connected to.
386       @param aURLPath   Url to request
387       @param aHttpMethod  Type of HTTP request to make, e.g. "GET", "POST", etc.
388       @param aUserAgent User-Agent string to send.  If NULL the default
389                         user-agent kUserAgent will be sent
390       @return 0 if successful, else error
391     */
392     int sendInitialHeaders(const char* aServerName,
393                      IPAddress   aServerIP,
394                      uint16_t    aPort,
395                      const char* aURLPath,
396                      const char* aHttpMethod,
397                      const char* aUserAgent);
398
399     /* Let the server know that we've reached the end of the headers
400     */
401     void finishHeaders();
402
403     // Number of milliseconds that we wait each time there isn't any data
404     // available to be read (during status code and header processing)
405     static const int kHttpWaitForDataDelay = 1000;
406     // Number of milliseconds that we'll wait in total without receiveing any
407     // data before returning HTTP_ERROR_TIMED_OUT (during status code and header
408     // processing)
409     static const int kHttpResponseTimeout = 30*1000;
410     static const char* kContentLengthPrefix;
411     typedef enum {
412         eIdle,
413         eRequestStarted,
414         eRequestSent,
415         eReadingStatusCode,
416         eStatusCodeRead,
417         eReadingContentLength,
418         eSkipToEndOfHeader,
419         eLineStartingCRFound,
420         eReadingBody
421     } tHttpState;
422     // Ethernet client we're using
423     Client* iClient;
424     // Current state of the finite-state-machine
425     tHttpState iState;
426     // Stores the status code for the response, once known
427     int iStatusCode;
428     // Stores the value of the Content-Length header, if present
429     int iContentLength;
430     // How many bytes of the response body have been read by the user
431     int iBodyLengthConsumed;
432     // How far through a Content-Length header prefix we are
433     const char* iContentLengthPtr;
434     // Address of the proxy to use, if we're using one
435     IPAddress iProxyAddress;
436     uint16_t iProxyPort;
437 };
438
439 #endif