MakingThings
  • 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.