- Main Page
- Related Pages
- Modules
- Data Structures
- Files

- File List
- Globals

# webclient.c

    00001 /*********************************************************************************
    00002
    00003  Copyright 2006-2008 MakingThings
    00004
    00005  Licensed under the Apache License,
    00006  Version 2.0 (the "License"); you may not use this file except in compliance
    00007  with the License. You may obtain a copy of the License at
    00008
    00009  http://www.apache.org/licenses/LICENSE-2.0
    00010
    00011  Unless required by applicable law or agreed to in writing, software distributed
    00012  under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
    00013  CONDITIONS OF ANY KIND, either express or implied. See the License for
    00014  the specific language governing permissions and limitations under the License.
    00015
    00016 *********************************************************************************/
    00017
    00018 #include "config.h"
    00019 #ifdef MAKE_CTRL_NETWORK
    00020
    00021 #include "stdlib.h"
    00022 #include "webclient.h"
    00023 #include "lwip/api.h"
    00024
    00025 #define WEBCLIENT_INTERNAL_BUFFER_SIZE 200
    00026 char WebClient_InternalBuffer[ WEBCLIENT_INTERNAL_BUFFER_SIZE ];
    00027
    00028 /** \defgroup webclient Web Client
    00029   A very simple web client for HTTP operations.
    00030
    00031   The web client system allows the Make Controller to get/post data to a webserver.  This
    00032   makes it straightforward to use the Make Controller as a source of data for your web apps.
    00033
    00034   Note that these functions make liberal use of printf-style functions, which can require
    00035   lots of memory to be allocated to the task calling them.
    00036
    00037   There's currently not a method provided for name resolution - you can always ping the
    00038   server you want to communicate with to see its IP address, and just use that.
    00039
    00040   See Network_DnsGetHostByName() for a way to get the address of a particular web site.
    00041
    00042   \ingroup Libraries
    00043   @{
    00044 */
    00045
    00046 /**
    00047   Performs an HTTP GET operation to the path at the address / port specified.
    00048
    00049   Reads through the HTTP header and copies the data into the buffer you pass in.  Because
    00050   sites can often be slow in their responses, this will wait up to 1 second (in 100 ms. intervals)
    00051   for data to become available.
    00052
    00053   Some websites seem to reject connections occassionally - perhaps because we don't supply as
    00054   much info to the server as a browser might, for example.  Simpler websites should be just fine.
    00055
    00056   Note that this uses lots of printf style functions and may require a fair amount of memory to be allocated
    00057   to the task calling it.  The result is returned in the specified buffer.
    00058
    00059   @param address The IP address of the server to get from.  Usually created using the IP_ADDRESS( ) macro.
    00060   @param port The port to connect on.  Usually 80 for HTTP.
    00061   @param hostname A string specifying the name of the host to connect to.  When connecting to a server
    00062   that does shared hosting, this will specify who to connect with.
    00063   @param path The path on the server to connect to.
    00064   @param buffer A pointer to the buffer read back into.
    00065   @param buffer_size An integer specifying the actual size of the buffer.
    00066   @return the number of bytes read, or < 0 on error.
    00067
    00068   \par Example
    00069   \code
    00070   int addr = IP_ADDRESS( 72, 249, 53, 185); // makingthings.com is 72.249.53.185
    00071   int bufLength = 100;
    00072   char myBuffer[bufLength];
    00073   int getSize = WebClient_Get( addr, 80, "www.makingthings.com", "/test/path", myBuffer, bufLength );
    00074   \endcode
    00075   Now we should have the results of the HTTP GET from \b www.makingthings.com/test/path in \b myBuffer.
    00076 */
    00077 int WebClient_Get( int address, int port, char* hostname, char* path, char* buffer, int buffer_size )
    00078 {
    00079   char* b = WebClient_InternalBuffer;
    00080   struct netconn *s = Socket( address, port );
    00081   if ( s != NULL )
    00082   {
    00083     // construct the GET request
    00084     int send_len = snprintf( b, WEBCLIENT_INTERNAL_BUFFER_SIZE, "GET %s HTTP/1.1\r\n%s%s%s\r\n",
    00085                                 path,
    00086                                 ( hostname != NULL ) ? "Host: " : "",
    00087                                 ( hostname != NULL ) ? hostname : "",
    00088                                 ( hostname != NULL ) ? "\r\n" : ""  );
    00089     if ( send_len > WEBCLIENT_INTERNAL_BUFFER_SIZE )
    00090     {
    00091       SocketClose( s );
    00092       return CONTROLLER_ERROR_INSUFFICIENT_RESOURCES;
    00093     }
    00094
    00095     // send the GET request
    00096     if(!SocketWrite( s, b, send_len ))
    00097     {
    00098       SocketClose( s );
    00099       return CONTROLLER_ERROR_WRITE_FAILED;
    00100     }
    00101
    00102     int content_length = 0;
    00103     // read through the response header to get to the data, and pick up the content-length as we go
    00104     int buffer_length;
    00105     while ( ( buffer_length = SocketReadLine( s, b, WEBCLIENT_INTERNAL_BUFFER_SIZE ) ) )
    00106     {
    00107       if ( strncmp( b, "\r\n", 2 ) == 0 )
    00108         break;
    00109       if ( strncmp( b, "Content-Length", 14 ) == 0 )
    00110         content_length = atoi( &b[ 15 ] );
    00111     }
    00112
    00113     // read the data into the given buffer until there's none left, or the passed in buffer is full
    00114     int total_bytes_read = 0;
    00115     int buf_remaining = buffer_size;
    00116     if ( content_length > 0 && buffer_length > 0 )
    00117     {
    00118       char* bp = buffer;
    00119       while( total_bytes_read < buffer_size && total_bytes_read < content_length )
    00120       {
    00121         int avail = SocketBytesAvailable(s);
    00122         if(!avail) // sometimes the connection can be slooooow, sleep a bit and try again
    00123         {
    00124           int times = 10;
    00125           while(times--)
    00126           {
    00127             Sleep(100);
    00128             if((avail = SocketBytesAvailable(s)))
    00129               break;
    00130           }
    00131         }
    00132         if(!avail) // if we still didn't get anything, bail
    00133           break;
    00134
    00135         if(avail > buf_remaining) // make sure we don't read more than can fit
    00136           avail = buf_remaining;
    00137         buffer_length = SocketRead( s, bp, avail );
    00138         if(!buffer_length) // this will be 0 when we get a read error - bail in that case
    00139           break;
    00140
    00141         // update counts
    00142         buf_remaining -= buffer_length;
    00143         total_bytes_read += buffer_length;
    00144         bp += buffer_length;
    00145       }
    00146     }
    00147
    00148     SocketClose( s );
    00149     return total_bytes_read;
    00150   }
    00151   else
    00152     return CONTROLLER_ERROR_BAD_ADDRESS;
    00153 }
    00154
    00155 /**
    00156   Performs an HTTP POST operation to the path at the address / port specified.  The actual post contents
    00157   are found read from a given buffer and the result is returned in the same buffer.
    00158   @param address The IP address of the server to post to.
    00159   @param port The port on the server you're connecting to. Usually 80 for HTTP.
    00160   @param hostname A string specifying the name of the host to connect to.  When connecting to a server
    00161   that does shared hosting, this will specify who to connect with.
    00162   @param path The path on the server to post to.
    00163   @param buffer A pointer to the buffer to write from and read back into.
    00164   @param buffer_length An integer specifying the number of bytes to write.
    00165   @param buffer_size An integer specifying the actual size of the buffer.
    00166   @return status.
    00167
    00168   \par Example
    00169   \code
    00170   // we'll post a test message to www.makingthings.com/post/path
    00171   int addr = IP_ADDRESS( 72, 249, 53, 185); // makingthings.com is 72.249.53.185
    00172   int bufLength = 100;
    00173   char myBuffer[bufLength];
    00174   sprintf( myBuffer, "A test message to post" );
    00175   int result = WebClient_Post( addr, 80, "www.makingthings.com", "/post/path",
    00176                                     myBuffer, strlen("A test message to post"), bufLength );
    00177   \endcode
    00178 */
    00179 int WebClient_Post( int address, int port, char* hostname, char* path, char* buffer, int buffer_length, int buffer_size )
    00180 {
    00181   char* b = WebClient_InternalBuffer;
    00182   int buffer_read = 0;
    00183   int wrote = 0;
    00184   void* s = Socket( address, port );
    00185   if ( s != NULL )
    00186   {
    00187     int send_len = snprintf( b, WEBCLIENT_INTERNAL_BUFFER_SIZE,
    00188                                 "POST %s HTTP/1.1\r\n%s%s%sContent-Type: text/plain\r\nContent-Length: %d\r\n\r\n",
    00189                                 path,
    00190                                 ( hostname != NULL ) ? "Host: " : "",
    00191                                 ( hostname != NULL ) ? hostname : "",
    00192                                 ( hostname != NULL ) ? "\r\n" : "",
    00193                                 buffer_length );
    00194     if ( send_len > WEBCLIENT_INTERNAL_BUFFER_SIZE )
    00195     {
    00196       SocketClose( s );
    00197       return CONTROLLER_ERROR_INSUFFICIENT_RESOURCES;
    00198     }
    00199
    00200     wrote = SocketWrite( s, b, send_len );
    00201     if ( wrote == 0 )
    00202     {
    00203       SocketClose( s );
    00204       return CONTROLLER_ERROR_WRITE_FAILED;
    00205     }
    00206
    00207     SocketWrite( s, buffer, buffer_length );
    00208
    00209     int content_length = 0;
    00210     int b_len;
    00211     while ( ( b_len = SocketReadLine( s, b, WEBCLIENT_INTERNAL_BUFFER_SIZE ) ) )
    00212     {
    00213       if ( strncmp( b, "\r\n", 2 ) == 0 )
    00214         break;
    00215       if ( strncmp( b, "Content-Length", 14 ) == 0 )
    00216         content_length = atoi( &b[ 16 ] );
    00217     }
    00218
    00219     if ( content_length > 0 && b_len > 0 )
    00220     {
    00221       char* bp = buffer;
    00222       while ( ( b_len = SocketRead( s, bp, buffer_size - buffer_read ) ) )
    00223       {
    00224         buffer_read += b_len;
    00225         bp += b_len;
    00226         if ( buffer_read >= content_length )
    00227           break;
    00228       }
    00229     }
    00230
    00231     SocketClose( s );
    00232     return buffer_read;
    00233   }
    00234   else
    00235     return CONTROLLER_ERROR_BAD_ADDRESS;
    00236 }
    00237
    00238 /** @}
    00239 */
    00240
    00241 #endif // MAKE_CTRL_NETWORK
    00242
    00243
    00244

The Make Controller Kit is an open source project maintained by MakingThings.
MakingThings code is released under the Apache 2.0 license.
Bug tracker, development wiki and status can be found at http://dev.makingthings.com.
This document was last updated on 18 May 2009.