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.