Minor tweaks to support longer header key value pairs

Dependents:   Car_Bon_car_module

Fork of HTTPClient by David Smart

Committer:
WiredHome
Date:
Sun Feb 02 16:47:06 2014 +0000
Revision:
20:4ea5255c1b04
Parent:
19:bcbf0af9fac3
Child:
21:d33f7e1ce811
Fixed Basic authorization and sending of the extra header information. Modified the debug stuff for consistency with some other apps.

Who changed what in which revision?

UserRevisionLine numberNew contents of line
donatien 0:2ccb9960a044 1 /* HTTPClient.cpp */
donatien 10:e1351de84c16 2 /* Copyright (C) 2012 mbed.org, MIT License
donatien 10:e1351de84c16 3 *
donatien 10:e1351de84c16 4 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
donatien 10:e1351de84c16 5 * and associated documentation files (the "Software"), to deal in the Software without restriction,
donatien 10:e1351de84c16 6 * including without limitation the rights to use, copy, modify, merge, publish, distribute,
donatien 10:e1351de84c16 7 * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
donatien 10:e1351de84c16 8 * furnished to do so, subject to the following conditions:
donatien 10:e1351de84c16 9 *
donatien 10:e1351de84c16 10 * The above copyright notice and this permission notice shall be included in all copies or
donatien 10:e1351de84c16 11 * substantial portions of the Software.
donatien 10:e1351de84c16 12 *
donatien 10:e1351de84c16 13 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
donatien 10:e1351de84c16 14 * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
donatien 10:e1351de84c16 15 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
donatien 10:e1351de84c16 16 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
donatien 10:e1351de84c16 17 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
donatien 10:e1351de84c16 18 */
donatien 0:2ccb9960a044 19
donatien 7:4e39864f7b15 20 //Debug is disabled by default
WiredHome 20:4ea5255c1b04 21 //#define DEBUG "HTCL"
donatien 11:390362de8c3f 22 #include <cstdio>
WiredHome 20:4ea5255c1b04 23 #if (defined(DEBUG) && !defined(TARGET_LPC11U24))
WiredHome 20:4ea5255c1b04 24 #define DBG(x, ...) std::printf("[DBG %s %3d] "x"\r\n", DEBUG, __LINE__, ##__VA_ARGS__);
WiredHome 20:4ea5255c1b04 25 #define WARN(x, ...) std::printf("[WRN %s %3d] "x"\r\n", DEBUG, __LINE__, ##__VA_ARGS__);
WiredHome 20:4ea5255c1b04 26 #define ERR(x, ...) std::printf("[ERR %s %3d] "x"\r\n", DEBUG, __LINE__, ##__VA_ARGS__);
WiredHome 20:4ea5255c1b04 27 #define INFO(x, ...) std::printf("[INF %s %3d] "x"\r\n", DEBUG, __LINE__, ##__VA_ARGS__);
donatien 12:89d09a6db00a 28 #else
WiredHome 20:4ea5255c1b04 29 #define DBG(x, ...)
donatien 12:89d09a6db00a 30 #define WARN(x, ...)
WiredHome 20:4ea5255c1b04 31 #define ERR(x, ...)
WiredHome 20:4ea5255c1b04 32 #define INFO(x, ...)
donatien 7:4e39864f7b15 33 #endif
donatien 0:2ccb9960a044 34
WiredHome 20:4ea5255c1b04 35 //#define HTTP_PORT 80
donatien 0:2ccb9960a044 36
donatien 11:390362de8c3f 37 #define OK 0
donatien 11:390362de8c3f 38
donatien 11:390362de8c3f 39 #define MIN(x,y) (((x)<(y))?(x):(y))
donatien 11:390362de8c3f 40 #define MAX(x,y) (((x)>(y))?(x):(y))
donatien 11:390362de8c3f 41
donatien 0:2ccb9960a044 42 #define CHUNK_SIZE 256
donatien 0:2ccb9960a044 43
donatien 0:2ccb9960a044 44 #include <cstring>
donatien 0:2ccb9960a044 45
donatien 11:390362de8c3f 46 #include "HTTPClient.h"
donatien 11:390362de8c3f 47
donatien 0:2ccb9960a044 48 HTTPClient::HTTPClient() :
WiredHome 20:4ea5255c1b04 49 m_sock(), m_basicAuthUser(NULL), m_basicAuthPassword(NULL), m_httpResponseCode(0)
donatien 0:2ccb9960a044 50 {
donatien 0:2ccb9960a044 51
donatien 0:2ccb9960a044 52 }
donatien 0:2ccb9960a044 53
donatien 0:2ccb9960a044 54 HTTPClient::~HTTPClient()
donatien 0:2ccb9960a044 55 {
donatien 0:2ccb9960a044 56
donatien 0:2ccb9960a044 57 }
donatien 0:2ccb9960a044 58
donatien 0:2ccb9960a044 59 void HTTPClient::basicAuth(const char* user, const char* password) //Basic Authentification
donatien 0:2ccb9960a044 60 {
WiredHome 20:4ea5255c1b04 61 m_basicAuthUser = user;
WiredHome 20:4ea5255c1b04 62 m_basicAuthPassword = password;
donatien 0:2ccb9960a044 63 }
vwochnik 19:bcbf0af9fac3 64
vwochnik 19:bcbf0af9fac3 65 void HTTPClient::customHeaders(const char **headers, size_t pairs)
vwochnik 19:bcbf0af9fac3 66 {
vwochnik 19:bcbf0af9fac3 67 m_customHeaders = headers;
vwochnik 19:bcbf0af9fac3 68 m_nCustomHeaders = pairs;
vwochnik 19:bcbf0af9fac3 69 }
vwochnik 19:bcbf0af9fac3 70
donatien 0:2ccb9960a044 71
donatien 12:89d09a6db00a 72 HTTPResult HTTPClient::get(const char* url, IHTTPDataIn* pDataIn, int timeout /*= HTTP_CLIENT_DEFAULT_TIMEOUT*/) //Blocking
donatien 0:2ccb9960a044 73 {
WiredHome 20:4ea5255c1b04 74 return connect(url, HTTP_GET, NULL, pDataIn, timeout);
donatien 0:2ccb9960a044 75 }
donatien 0:2ccb9960a044 76
donatien 12:89d09a6db00a 77 HTTPResult HTTPClient::get(const char* url, char* result, size_t maxResultLen, int timeout /*= HTTP_CLIENT_DEFAULT_TIMEOUT*/) //Blocking
donatien 0:2ccb9960a044 78 {
WiredHome 20:4ea5255c1b04 79 HTTPText str(result, maxResultLen);
WiredHome 20:4ea5255c1b04 80 return get(url, &str, timeout);
donatien 0:2ccb9960a044 81 }
donatien 0:2ccb9960a044 82
donatien 12:89d09a6db00a 83 HTTPResult HTTPClient::post(const char* url, const IHTTPDataOut& dataOut, IHTTPDataIn* pDataIn, int timeout /*= HTTP_CLIENT_DEFAULT_TIMEOUT*/) //Blocking
donatien 0:2ccb9960a044 84 {
WiredHome 20:4ea5255c1b04 85 return connect(url, HTTP_POST, (IHTTPDataOut*)&dataOut, pDataIn, timeout);
donatien 0:2ccb9960a044 86 }
donatien 0:2ccb9960a044 87
donatien 16:1f743885e7de 88 HTTPResult HTTPClient::put(const char* url, const IHTTPDataOut& dataOut, IHTTPDataIn* pDataIn, int timeout /*= HTTP_CLIENT_DEFAULT_TIMEOUT*/) //Blocking
donatien 16:1f743885e7de 89 {
WiredHome 20:4ea5255c1b04 90 return connect(url, HTTP_PUT, (IHTTPDataOut*)&dataOut, pDataIn, timeout);
donatien 16:1f743885e7de 91 }
donatien 16:1f743885e7de 92
donatien 16:1f743885e7de 93 HTTPResult HTTPClient::del(const char* url, IHTTPDataIn* pDataIn, int timeout /*= HTTP_CLIENT_DEFAULT_TIMEOUT*/) //Blocking
donatien 16:1f743885e7de 94 {
WiredHome 20:4ea5255c1b04 95 return connect(url, HTTP_DELETE, NULL, pDataIn, timeout);
donatien 16:1f743885e7de 96 }
donatien 16:1f743885e7de 97
donatien 16:1f743885e7de 98
donatien 0:2ccb9960a044 99 int HTTPClient::getHTTPResponseCode()
donatien 0:2ccb9960a044 100 {
WiredHome 20:4ea5255c1b04 101 return m_httpResponseCode;
donatien 0:2ccb9960a044 102 }
donatien 0:2ccb9960a044 103
donatien 5:791fc3dcb6c4 104 #define CHECK_CONN_ERR(ret) \
donatien 5:791fc3dcb6c4 105 do{ \
donatien 7:4e39864f7b15 106 if(ret) { \
donatien 7:4e39864f7b15 107 m_sock.close(); \
donatien 5:791fc3dcb6c4 108 ERR("Connection error (%d)", ret); \
donatien 11:390362de8c3f 109 return HTTP_CONN; \
donatien 5:791fc3dcb6c4 110 } \
donatien 5:791fc3dcb6c4 111 } while(0)
donatien 5:791fc3dcb6c4 112
donatien 5:791fc3dcb6c4 113 #define PRTCL_ERR() \
donatien 5:791fc3dcb6c4 114 do{ \
donatien 7:4e39864f7b15 115 m_sock.close(); \
donatien 5:791fc3dcb6c4 116 ERR("Protocol error"); \
donatien 11:390362de8c3f 117 return HTTP_PRTCL; \
donatien 5:791fc3dcb6c4 118 } while(0)
donatien 0:2ccb9960a044 119
donatien 12:89d09a6db00a 120 HTTPResult HTTPClient::connect(const char* url, HTTP_METH method, IHTTPDataOut* pDataOut, IHTTPDataIn* pDataIn, int timeout) //Execute request
WiredHome 20:4ea5255c1b04 121 {
WiredHome 20:4ea5255c1b04 122 m_httpResponseCode = 0; //Invalidate code
WiredHome 20:4ea5255c1b04 123 m_timeout = timeout;
WiredHome 20:4ea5255c1b04 124
WiredHome 20:4ea5255c1b04 125 pDataIn->writeReset();
WiredHome 20:4ea5255c1b04 126 if( pDataOut ) {
WiredHome 20:4ea5255c1b04 127 pDataOut->readReset();
WiredHome 20:4ea5255c1b04 128 }
donatien 0:2ccb9960a044 129
WiredHome 20:4ea5255c1b04 130 char scheme[8];
WiredHome 20:4ea5255c1b04 131 uint16_t port;
WiredHome 20:4ea5255c1b04 132 char host[32];
WiredHome 20:4ea5255c1b04 133 char path[64];
WiredHome 20:4ea5255c1b04 134 //First we need to parse the url (http[s]://host[:port][/[path]]) -- HTTPS not supported (yet?)
WiredHome 20:4ea5255c1b04 135 HTTPResult res = parseURL(url, scheme, sizeof(scheme), host, sizeof(host), &port, path, sizeof(path));
WiredHome 20:4ea5255c1b04 136 if(res != HTTP_OK) {
WiredHome 20:4ea5255c1b04 137 ERR("parseURL returned %d", res);
WiredHome 20:4ea5255c1b04 138 return res;
WiredHome 20:4ea5255c1b04 139 }
donatien 0:2ccb9960a044 140
WiredHome 20:4ea5255c1b04 141 if(port == 0) { //TODO do handle HTTPS->443
WiredHome 20:4ea5255c1b04 142 port = 80;
WiredHome 20:4ea5255c1b04 143 }
WiredHome 20:4ea5255c1b04 144
WiredHome 20:4ea5255c1b04 145 DBG("Scheme: %s", scheme);
WiredHome 20:4ea5255c1b04 146 DBG("Host: %s", host);
WiredHome 20:4ea5255c1b04 147 DBG("Port: %d", port);
WiredHome 20:4ea5255c1b04 148 DBG("Path: %s", path);
donatien 0:2ccb9960a044 149
WiredHome 20:4ea5255c1b04 150 //Connect
WiredHome 20:4ea5255c1b04 151 DBG("Connecting socket to server");
WiredHome 20:4ea5255c1b04 152 int ret = m_sock.connect(host, port);
WiredHome 20:4ea5255c1b04 153 if (ret < 0) {
WiredHome 20:4ea5255c1b04 154 m_sock.close();
WiredHome 20:4ea5255c1b04 155 ERR("Could not connect");
WiredHome 20:4ea5255c1b04 156 return HTTP_CONN;
vwochnik 19:bcbf0af9fac3 157 }
donatien 0:2ccb9960a044 158
WiredHome 20:4ea5255c1b04 159 //Send request
WiredHome 20:4ea5255c1b04 160 DBG("Sending request");
WiredHome 20:4ea5255c1b04 161 char buf[CHUNK_SIZE];
WiredHome 20:4ea5255c1b04 162 const char* meth = (method==HTTP_GET)?"GET":(method==HTTP_POST)?"POST":(method==HTTP_PUT)?"PUT":(method==HTTP_DELETE)?"DELETE":"";
WiredHome 20:4ea5255c1b04 163 snprintf(buf, sizeof(buf), "%s %s HTTP/1.1\r\nHost: %s:%d\r\nConnection: keep-alive\r\n", meth, path, host, port); //Write request
vwochnik 19:bcbf0af9fac3 164 ret = send(buf);
WiredHome 20:4ea5255c1b04 165 if (ret) {
WiredHome 20:4ea5255c1b04 166 m_sock.close();
WiredHome 20:4ea5255c1b04 167 ERR("Could not write request");
WiredHome 20:4ea5255c1b04 168 return HTTP_CONN;
donatien 0:2ccb9960a044 169 }
WiredHome 20:4ea5255c1b04 170
WiredHome 20:4ea5255c1b04 171 // send authorization
WiredHome 20:4ea5255c1b04 172 if (m_basicAuthUser && m_basicAuthPassword) {
WiredHome 20:4ea5255c1b04 173 strcpy(buf, "Authorization: Basic ");
WiredHome 20:4ea5255c1b04 174 createauth(m_basicAuthUser, m_basicAuthPassword, buf+strlen(buf), sizeof(buf)-strlen(buf));
WiredHome 20:4ea5255c1b04 175 strcat(buf, "\r\n");
WiredHome 20:4ea5255c1b04 176 INFO(" (%s,%s) => (%s)", m_basicAuthUser, m_basicAuthPassword, buf);
WiredHome 20:4ea5255c1b04 177 ret = send(buf);
WiredHome 20:4ea5255c1b04 178 INFO(" ret = %d", ret);
WiredHome 20:4ea5255c1b04 179 if(ret) {
WiredHome 20:4ea5255c1b04 180 m_sock.close();
WiredHome 20:4ea5255c1b04 181 ERR("Could not write request");
WiredHome 20:4ea5255c1b04 182 return HTTP_CONN;
WiredHome 20:4ea5255c1b04 183 }
donatien 0:2ccb9960a044 184 }
WiredHome 20:4ea5255c1b04 185
WiredHome 20:4ea5255c1b04 186 //Send all headers
WiredHome 20:4ea5255c1b04 187 for (size_t nh = 0; nh < m_nCustomHeaders * 2; nh+=2) {
WiredHome 20:4ea5255c1b04 188 INFO("hdr[%d] %s:", nh, m_customHeaders[nh]);
WiredHome 20:4ea5255c1b04 189 INFO(" %s", m_customHeaders[nh+1]);
WiredHome 20:4ea5255c1b04 190 snprintf(buf, sizeof(buf), "%s: %s\r\n", m_customHeaders[nh], m_customHeaders[nh+1]);
WiredHome 20:4ea5255c1b04 191 ret = send(buf);
WiredHome 20:4ea5255c1b04 192 if (ret) {
WiredHome 20:4ea5255c1b04 193 ERR("closing");
WiredHome 20:4ea5255c1b04 194 wait_ms(50);
WiredHome 20:4ea5255c1b04 195 m_sock.close();
WiredHome 20:4ea5255c1b04 196 ERR("Could not write request");
WiredHome 20:4ea5255c1b04 197 return HTTP_CONN;
WiredHome 20:4ea5255c1b04 198 }
WiredHome 20:4ea5255c1b04 199 INFO(" hdr %d", ret);
donatien 0:2ccb9960a044 200 }
donatien 0:2ccb9960a044 201
WiredHome 20:4ea5255c1b04 202 //Send default headers
WiredHome 20:4ea5255c1b04 203 DBG("Sending headers");
WiredHome 20:4ea5255c1b04 204 if( pDataOut != NULL ) {
WiredHome 20:4ea5255c1b04 205 if( pDataOut->getIsChunked() ) {
WiredHome 20:4ea5255c1b04 206 ret = send("Transfer-Encoding: chunked\r\n");
WiredHome 20:4ea5255c1b04 207 CHECK_CONN_ERR(ret);
WiredHome 20:4ea5255c1b04 208 } else {
WiredHome 20:4ea5255c1b04 209 snprintf(buf, sizeof(buf), "Content-Length: %d\r\n", pDataOut->getDataLen());
WiredHome 20:4ea5255c1b04 210 ret = send(buf);
WiredHome 20:4ea5255c1b04 211 CHECK_CONN_ERR(ret);
WiredHome 20:4ea5255c1b04 212 }
WiredHome 20:4ea5255c1b04 213 char type[48];
WiredHome 20:4ea5255c1b04 214 if( pDataOut->getDataType(type, 48) == HTTP_OK ) {
WiredHome 20:4ea5255c1b04 215 snprintf(buf, sizeof(buf), "Content-Type: %s\r\n", type);
WiredHome 20:4ea5255c1b04 216 ret = send(buf);
WiredHome 20:4ea5255c1b04 217 CHECK_CONN_ERR(ret);
WiredHome 20:4ea5255c1b04 218 }
WiredHome 20:4ea5255c1b04 219 }
WiredHome 20:4ea5255c1b04 220
WiredHome 20:4ea5255c1b04 221 //Close headers
WiredHome 20:4ea5255c1b04 222 DBG("Headers sent");
WiredHome 20:4ea5255c1b04 223 ret = send("\r\n");
WiredHome 20:4ea5255c1b04 224 CHECK_CONN_ERR(ret);
WiredHome 20:4ea5255c1b04 225
WiredHome 20:4ea5255c1b04 226 size_t trfLen;
donatien 0:2ccb9960a044 227
WiredHome 20:4ea5255c1b04 228 //Send data (if available)
WiredHome 20:4ea5255c1b04 229 if( pDataOut != NULL ) {
WiredHome 20:4ea5255c1b04 230 DBG("Sending data");
WiredHome 20:4ea5255c1b04 231 while(true) {
WiredHome 20:4ea5255c1b04 232 size_t writtenLen = 0;
WiredHome 20:4ea5255c1b04 233 pDataOut->read(buf, CHUNK_SIZE, &trfLen);
WiredHome 20:4ea5255c1b04 234 if( pDataOut->getIsChunked() ) {
WiredHome 20:4ea5255c1b04 235 //Write chunk header
WiredHome 20:4ea5255c1b04 236 char chunkHeader[16];
WiredHome 20:4ea5255c1b04 237 snprintf(chunkHeader, sizeof(chunkHeader), "%X\r\n", trfLen); //In hex encoding
WiredHome 20:4ea5255c1b04 238 ret = send(chunkHeader);
WiredHome 20:4ea5255c1b04 239 CHECK_CONN_ERR(ret);
WiredHome 20:4ea5255c1b04 240 } else if( trfLen == 0 ) {
WiredHome 20:4ea5255c1b04 241 break;
WiredHome 20:4ea5255c1b04 242 }
WiredHome 20:4ea5255c1b04 243 if( trfLen != 0 ) {
WiredHome 20:4ea5255c1b04 244 ret = send(buf, trfLen);
WiredHome 20:4ea5255c1b04 245 CHECK_CONN_ERR(ret);
WiredHome 20:4ea5255c1b04 246 }
WiredHome 20:4ea5255c1b04 247
WiredHome 20:4ea5255c1b04 248 if( pDataOut->getIsChunked() ) {
WiredHome 20:4ea5255c1b04 249 ret = send("\r\n"); //Chunk-terminating CRLF
WiredHome 20:4ea5255c1b04 250 CHECK_CONN_ERR(ret);
WiredHome 20:4ea5255c1b04 251 } else {
WiredHome 20:4ea5255c1b04 252 writtenLen += trfLen;
WiredHome 20:4ea5255c1b04 253 if( writtenLen >= pDataOut->getDataLen() ) {
WiredHome 20:4ea5255c1b04 254 break;
WiredHome 20:4ea5255c1b04 255 }
WiredHome 20:4ea5255c1b04 256 }
WiredHome 20:4ea5255c1b04 257
WiredHome 20:4ea5255c1b04 258 if( trfLen == 0 ) {
WiredHome 20:4ea5255c1b04 259 break;
WiredHome 20:4ea5255c1b04 260 }
donatien 0:2ccb9960a044 261 }
WiredHome 20:4ea5255c1b04 262
WiredHome 20:4ea5255c1b04 263 }
donatien 0:2ccb9960a044 264
WiredHome 20:4ea5255c1b04 265 //Receive response
WiredHome 20:4ea5255c1b04 266 DBG("Receiving response");
WiredHome 20:4ea5255c1b04 267 ret = recv(buf, CHUNK_SIZE - 1, CHUNK_SIZE - 1, &trfLen); //Read n bytes
WiredHome 20:4ea5255c1b04 268 CHECK_CONN_ERR(ret);
WiredHome 20:4ea5255c1b04 269 buf[trfLen] = '\0';
WiredHome 20:4ea5255c1b04 270 INFO("Received \r\n(%s\r\n)", buf);
WiredHome 20:4ea5255c1b04 271
WiredHome 20:4ea5255c1b04 272 char* crlfPtr = strstr(buf, "\r\n");
WiredHome 20:4ea5255c1b04 273 if( crlfPtr == NULL) {
WiredHome 20:4ea5255c1b04 274 PRTCL_ERR();
donatien 0:2ccb9960a044 275 }
donatien 0:2ccb9960a044 276
WiredHome 20:4ea5255c1b04 277 int crlfPos = crlfPtr - buf;
WiredHome 20:4ea5255c1b04 278 buf[crlfPos] = '\0';
donatien 0:2ccb9960a044 279
WiredHome 20:4ea5255c1b04 280 //Parse HTTP response
WiredHome 20:4ea5255c1b04 281 if( sscanf(buf, "HTTP/%*d.%*d %d %*[^\r\n]", &m_httpResponseCode) != 1 ) {
WiredHome 20:4ea5255c1b04 282 //Cannot match string, error
WiredHome 20:4ea5255c1b04 283 ERR("Not a correct HTTP answer : %s\n", buf);
WiredHome 20:4ea5255c1b04 284 PRTCL_ERR();
WiredHome 20:4ea5255c1b04 285 }
donatien 0:2ccb9960a044 286
WiredHome 20:4ea5255c1b04 287 if( (m_httpResponseCode < 200) || (m_httpResponseCode >= 300) ) {
WiredHome 20:4ea5255c1b04 288 //Did not return a 2xx code; TODO fetch headers/(&data?) anyway and implement a mean of writing/reading headers
WiredHome 20:4ea5255c1b04 289 WARN("Response code %d", m_httpResponseCode);
donatien 5:791fc3dcb6c4 290 PRTCL_ERR();
donatien 0:2ccb9960a044 291 }
donatien 0:2ccb9960a044 292
WiredHome 20:4ea5255c1b04 293 DBG("Reading headers");
WiredHome 20:4ea5255c1b04 294
WiredHome 20:4ea5255c1b04 295 memmove(buf, &buf[crlfPos+2], trfLen - (crlfPos + 2) + 1); //Be sure to move NULL-terminating char as well
WiredHome 20:4ea5255c1b04 296 trfLen -= (crlfPos + 2);
donatien 0:2ccb9960a044 297
WiredHome 20:4ea5255c1b04 298 size_t recvContentLength = 0;
WiredHome 20:4ea5255c1b04 299 bool recvChunked = false;
WiredHome 20:4ea5255c1b04 300 //Now get headers
WiredHome 20:4ea5255c1b04 301 while( true ) {
WiredHome 20:4ea5255c1b04 302 crlfPtr = strstr(buf, "\r\n");
WiredHome 20:4ea5255c1b04 303 if(crlfPtr == NULL) {
WiredHome 20:4ea5255c1b04 304 if( trfLen < CHUNK_SIZE - 1 ) {
WiredHome 20:4ea5255c1b04 305 size_t newTrfLen;
WiredHome 20:4ea5255c1b04 306 ret = recv(buf + trfLen, 1, CHUNK_SIZE - trfLen - 1, &newTrfLen);
WiredHome 20:4ea5255c1b04 307 trfLen += newTrfLen;
WiredHome 20:4ea5255c1b04 308 buf[trfLen] = '\0';
WiredHome 20:4ea5255c1b04 309 DBG("Read %d chars; In buf: [%s]", newTrfLen, buf);
WiredHome 20:4ea5255c1b04 310 CHECK_CONN_ERR(ret);
WiredHome 20:4ea5255c1b04 311 continue;
WiredHome 20:4ea5255c1b04 312 } else {
WiredHome 20:4ea5255c1b04 313 PRTCL_ERR();
WiredHome 20:4ea5255c1b04 314 }
WiredHome 20:4ea5255c1b04 315 }
donatien 0:2ccb9960a044 316
WiredHome 20:4ea5255c1b04 317 crlfPos = crlfPtr - buf;
donatien 4:c071b05ac026 318
WiredHome 20:4ea5255c1b04 319 if(crlfPos == 0) { //End of headers
WiredHome 20:4ea5255c1b04 320 DBG("Headers read");
WiredHome 20:4ea5255c1b04 321 memmove(buf, &buf[2], trfLen - 2 + 1); //Be sure to move NULL-terminating char as well
WiredHome 20:4ea5255c1b04 322 trfLen -= 2;
WiredHome 20:4ea5255c1b04 323 break;
donatien 0:2ccb9960a044 324 }
WiredHome 20:4ea5255c1b04 325
WiredHome 20:4ea5255c1b04 326 buf[crlfPos] = '\0';
WiredHome 20:4ea5255c1b04 327
WiredHome 20:4ea5255c1b04 328 char key[32];
WiredHome 20:4ea5255c1b04 329 char value[32];
WiredHome 20:4ea5255c1b04 330
WiredHome 20:4ea5255c1b04 331 key[31] = '\0';
WiredHome 20:4ea5255c1b04 332 value[31] = '\0';
donatien 0:2ccb9960a044 333
WiredHome 20:4ea5255c1b04 334 int n = sscanf(buf, "%31[^:]: %31[^\r\n]", key, value);
WiredHome 20:4ea5255c1b04 335 if ( n == 2 ) {
WiredHome 20:4ea5255c1b04 336 DBG("Read header : %s: %s\n", key, value);
WiredHome 20:4ea5255c1b04 337 if( !strcmp(key, "Content-Length") ) {
WiredHome 20:4ea5255c1b04 338 sscanf(value, "%d", &recvContentLength);
WiredHome 20:4ea5255c1b04 339 pDataIn->setDataLen(recvContentLength);
WiredHome 20:4ea5255c1b04 340 } else if( !strcmp(key, "Transfer-Encoding") ) {
WiredHome 20:4ea5255c1b04 341 if( !strcmp(value, "Chunked") || !strcmp(value, "chunked") ) {
WiredHome 20:4ea5255c1b04 342 recvChunked = true;
WiredHome 20:4ea5255c1b04 343 pDataIn->setIsChunked(true);
WiredHome 20:4ea5255c1b04 344 }
WiredHome 20:4ea5255c1b04 345 } else if( !strcmp(key, "Content-Type") ) {
WiredHome 20:4ea5255c1b04 346 pDataIn->setDataType(value);
WiredHome 20:4ea5255c1b04 347 }
donatien 0:2ccb9960a044 348
WiredHome 20:4ea5255c1b04 349 memmove(buf, &buf[crlfPos+2], trfLen - (crlfPos + 2) + 1); //Be sure to move NULL-terminating char as well
WiredHome 20:4ea5255c1b04 350 trfLen -= (crlfPos + 2);
WiredHome 20:4ea5255c1b04 351
WiredHome 20:4ea5255c1b04 352 } else {
WiredHome 20:4ea5255c1b04 353 ERR("Could not parse header");
WiredHome 20:4ea5255c1b04 354 PRTCL_ERR();
WiredHome 20:4ea5255c1b04 355 }
WiredHome 20:4ea5255c1b04 356
donatien 0:2ccb9960a044 357 }
donatien 0:2ccb9960a044 358
WiredHome 20:4ea5255c1b04 359 //Receive data
WiredHome 20:4ea5255c1b04 360 DBG("Receiving data");
WiredHome 20:4ea5255c1b04 361 while(true) {
WiredHome 20:4ea5255c1b04 362 size_t readLen = 0;
donatien 0:2ccb9960a044 363
WiredHome 20:4ea5255c1b04 364 if( recvChunked ) {
WiredHome 20:4ea5255c1b04 365 //Read chunk header
WiredHome 20:4ea5255c1b04 366 bool foundCrlf;
WiredHome 20:4ea5255c1b04 367 do {
WiredHome 20:4ea5255c1b04 368 foundCrlf = false;
WiredHome 20:4ea5255c1b04 369 crlfPos=0;
WiredHome 20:4ea5255c1b04 370 buf[trfLen]=0;
WiredHome 20:4ea5255c1b04 371 if(trfLen >= 2) {
WiredHome 20:4ea5255c1b04 372 for(; crlfPos < trfLen - 2; crlfPos++) {
WiredHome 20:4ea5255c1b04 373 if( buf[crlfPos] == '\r' && buf[crlfPos + 1] == '\n' ) {
WiredHome 20:4ea5255c1b04 374 foundCrlf = true;
WiredHome 20:4ea5255c1b04 375 break;
WiredHome 20:4ea5255c1b04 376 }
WiredHome 20:4ea5255c1b04 377 }
WiredHome 20:4ea5255c1b04 378 }
WiredHome 20:4ea5255c1b04 379 if(!foundCrlf) { //Try to read more
WiredHome 20:4ea5255c1b04 380 if( trfLen < CHUNK_SIZE ) {
WiredHome 20:4ea5255c1b04 381 size_t newTrfLen;
WiredHome 20:4ea5255c1b04 382 ret = recv(buf + trfLen, 0, CHUNK_SIZE - trfLen - 1, &newTrfLen);
WiredHome 20:4ea5255c1b04 383 trfLen += newTrfLen;
WiredHome 20:4ea5255c1b04 384 CHECK_CONN_ERR(ret);
WiredHome 20:4ea5255c1b04 385 continue;
WiredHome 20:4ea5255c1b04 386 } else {
WiredHome 20:4ea5255c1b04 387 PRTCL_ERR();
WiredHome 20:4ea5255c1b04 388 }
WiredHome 20:4ea5255c1b04 389 }
WiredHome 20:4ea5255c1b04 390 } while(!foundCrlf);
WiredHome 20:4ea5255c1b04 391 buf[crlfPos] = '\0';
WiredHome 20:4ea5255c1b04 392 int n = sscanf(buf, "%x", &readLen);
WiredHome 20:4ea5255c1b04 393 if(n!=1) {
WiredHome 20:4ea5255c1b04 394 ERR("Could not read chunk length");
WiredHome 20:4ea5255c1b04 395 PRTCL_ERR();
donatien 14:2744e0c0e527 396 }
WiredHome 20:4ea5255c1b04 397
WiredHome 20:4ea5255c1b04 398 memmove(buf, &buf[crlfPos+2], trfLen - (crlfPos + 2)); //Not need to move NULL-terminating char any more
WiredHome 20:4ea5255c1b04 399 trfLen -= (crlfPos + 2);
WiredHome 20:4ea5255c1b04 400
WiredHome 20:4ea5255c1b04 401 if( readLen == 0 ) {
WiredHome 20:4ea5255c1b04 402 //Last chunk
WiredHome 20:4ea5255c1b04 403 break;
WiredHome 20:4ea5255c1b04 404 }
WiredHome 20:4ea5255c1b04 405 } else {
WiredHome 20:4ea5255c1b04 406 readLen = recvContentLength;
donatien 0:2ccb9960a044 407 }
WiredHome 20:4ea5255c1b04 408
WiredHome 20:4ea5255c1b04 409 DBG("Retrieving %d bytes", readLen);
WiredHome 20:4ea5255c1b04 410
WiredHome 20:4ea5255c1b04 411 do {
WiredHome 20:4ea5255c1b04 412 pDataIn->write(buf, MIN(trfLen, readLen));
WiredHome 20:4ea5255c1b04 413 if( trfLen > readLen ) {
WiredHome 20:4ea5255c1b04 414 memmove(buf, &buf[readLen], trfLen - readLen);
WiredHome 20:4ea5255c1b04 415 trfLen -= readLen;
WiredHome 20:4ea5255c1b04 416 readLen = 0;
WiredHome 20:4ea5255c1b04 417 } else {
WiredHome 20:4ea5255c1b04 418 readLen -= trfLen;
WiredHome 20:4ea5255c1b04 419 }
WiredHome 20:4ea5255c1b04 420
WiredHome 20:4ea5255c1b04 421 if(readLen) {
WiredHome 20:4ea5255c1b04 422 ret = recv(buf, 1, CHUNK_SIZE - trfLen - 1, &trfLen);
WiredHome 20:4ea5255c1b04 423 CHECK_CONN_ERR(ret);
WiredHome 20:4ea5255c1b04 424 }
WiredHome 20:4ea5255c1b04 425 } while(readLen);
WiredHome 20:4ea5255c1b04 426
WiredHome 20:4ea5255c1b04 427 if( recvChunked ) {
WiredHome 20:4ea5255c1b04 428 if(trfLen < 2) {
WiredHome 20:4ea5255c1b04 429 size_t newTrfLen;
WiredHome 20:4ea5255c1b04 430 //Read missing chars to find end of chunk
WiredHome 20:4ea5255c1b04 431 ret = recv(buf + trfLen, 2 - trfLen, CHUNK_SIZE - trfLen - 1, &newTrfLen);
WiredHome 20:4ea5255c1b04 432 CHECK_CONN_ERR(ret);
WiredHome 20:4ea5255c1b04 433 trfLen += newTrfLen;
WiredHome 20:4ea5255c1b04 434 }
WiredHome 20:4ea5255c1b04 435 if( (buf[0] != '\r') || (buf[1] != '\n') ) {
WiredHome 20:4ea5255c1b04 436 ERR("Format error");
WiredHome 20:4ea5255c1b04 437 PRTCL_ERR();
WiredHome 20:4ea5255c1b04 438 }
WiredHome 20:4ea5255c1b04 439 memmove(buf, &buf[2], trfLen - 2);
WiredHome 20:4ea5255c1b04 440 trfLen -= 2;
WiredHome 20:4ea5255c1b04 441 } else {
WiredHome 20:4ea5255c1b04 442 break;
donatien 0:2ccb9960a044 443 }
donatien 0:2ccb9960a044 444
donatien 0:2ccb9960a044 445 }
donatien 0:2ccb9960a044 446
WiredHome 20:4ea5255c1b04 447 m_sock.close();
WiredHome 20:4ea5255c1b04 448 DBG("Completed HTTP transaction");
donatien 0:2ccb9960a044 449
WiredHome 20:4ea5255c1b04 450 return HTTP_OK;
donatien 0:2ccb9960a044 451 }
donatien 0:2ccb9960a044 452
donatien 11:390362de8c3f 453 HTTPResult HTTPClient::recv(char* buf, size_t minLen, size_t maxLen, size_t* pReadLen) //0 on success, err code on failure
donatien 0:2ccb9960a044 454 {
WiredHome 20:4ea5255c1b04 455 DBG("Trying to read between %d and %d bytes", minLen, maxLen);
WiredHome 20:4ea5255c1b04 456 size_t readLen = 0;
WiredHome 20:4ea5255c1b04 457
WiredHome 20:4ea5255c1b04 458 if (!m_sock.is_connected()) {
WiredHome 20:4ea5255c1b04 459 WARN("Connection was closed by server");
WiredHome 20:4ea5255c1b04 460 return HTTP_CLOSED; //Connection was closed by server
donatien 7:4e39864f7b15 461 }
WiredHome 20:4ea5255c1b04 462
WiredHome 20:4ea5255c1b04 463 int ret;
WiredHome 20:4ea5255c1b04 464 while (readLen < maxLen) {
WiredHome 20:4ea5255c1b04 465 if (readLen < minLen) {
WiredHome 20:4ea5255c1b04 466 DBG("Trying to read at most %d bytes [Blocking]", minLen - readLen);
WiredHome 20:4ea5255c1b04 467 m_sock.set_blocking(false, m_timeout);
WiredHome 20:4ea5255c1b04 468 ret = m_sock.receive_all(buf + readLen, minLen - readLen);
WiredHome 20:4ea5255c1b04 469 } else {
WiredHome 20:4ea5255c1b04 470 DBG("Trying to read at most %d bytes [Not blocking]", maxLen - readLen);
WiredHome 20:4ea5255c1b04 471 m_sock.set_blocking(false, 0);
WiredHome 20:4ea5255c1b04 472 ret = m_sock.receive(buf + readLen, maxLen - readLen);
WiredHome 20:4ea5255c1b04 473 }
WiredHome 20:4ea5255c1b04 474
WiredHome 20:4ea5255c1b04 475 if (ret > 0) {
WiredHome 20:4ea5255c1b04 476 readLen += ret;
WiredHome 20:4ea5255c1b04 477 } else if ( ret == 0 ) {
WiredHome 20:4ea5255c1b04 478 break;
WiredHome 20:4ea5255c1b04 479 } else {
WiredHome 20:4ea5255c1b04 480 if (!m_sock.is_connected()) {
WiredHome 20:4ea5255c1b04 481 ERR("Connection error (recv returned %d)", ret);
WiredHome 20:4ea5255c1b04 482 *pReadLen = readLen;
WiredHome 20:4ea5255c1b04 483 return HTTP_CONN;
WiredHome 20:4ea5255c1b04 484 } else {
WiredHome 20:4ea5255c1b04 485 break;
WiredHome 20:4ea5255c1b04 486 }
WiredHome 20:4ea5255c1b04 487 }
WiredHome 20:4ea5255c1b04 488 if (!m_sock.is_connected()) {
WiredHome 20:4ea5255c1b04 489 break;
WiredHome 20:4ea5255c1b04 490 }
donatien 7:4e39864f7b15 491 }
WiredHome 20:4ea5255c1b04 492 DBG("Read %d bytes", readLen);
WiredHome 20:4ea5255c1b04 493 *pReadLen = readLen;
WiredHome 20:4ea5255c1b04 494 return HTTP_OK;
donatien 7:4e39864f7b15 495 }
donatien 7:4e39864f7b15 496
donatien 11:390362de8c3f 497 HTTPResult HTTPClient::send(char* buf, size_t len) //0 on success, err code on failure
donatien 7:4e39864f7b15 498 {
WiredHome 20:4ea5255c1b04 499 if(len == 0) {
WiredHome 20:4ea5255c1b04 500 len = strlen(buf);
WiredHome 20:4ea5255c1b04 501 }
WiredHome 20:4ea5255c1b04 502 DBG("send(%s,%d)", buf, len);
WiredHome 20:4ea5255c1b04 503 size_t writtenLen = 0;
WiredHome 20:4ea5255c1b04 504
WiredHome 20:4ea5255c1b04 505 if(!m_sock.is_connected()) {
WiredHome 20:4ea5255c1b04 506 WARN("Connection was closed by server");
WiredHome 20:4ea5255c1b04 507 return HTTP_CLOSED; //Connection was closed by server
WiredHome 20:4ea5255c1b04 508 }
WiredHome 20:4ea5255c1b04 509
WiredHome 20:4ea5255c1b04 510 m_sock.set_blocking(false, m_timeout);
WiredHome 20:4ea5255c1b04 511 int ret = m_sock.send_all(buf, len);
WiredHome 20:4ea5255c1b04 512 if(ret > 0) {
WiredHome 20:4ea5255c1b04 513 writtenLen += ret;
WiredHome 20:4ea5255c1b04 514 } else if( ret == 0 ) {
WiredHome 20:4ea5255c1b04 515 WARN("Connection was closed by server");
WiredHome 20:4ea5255c1b04 516 return HTTP_CLOSED; //Connection was closed by server
WiredHome 20:4ea5255c1b04 517 } else {
WiredHome 20:4ea5255c1b04 518 ERR("Connection error (send returned %d)", ret);
WiredHome 20:4ea5255c1b04 519 return HTTP_CONN;
WiredHome 20:4ea5255c1b04 520 }
WiredHome 20:4ea5255c1b04 521
WiredHome 20:4ea5255c1b04 522 DBG("Written %d bytes", writtenLen);
WiredHome 20:4ea5255c1b04 523 return HTTP_OK;
donatien 0:2ccb9960a044 524 }
donatien 0:2ccb9960a044 525
donatien 11:390362de8c3f 526 HTTPResult HTTPClient::parseURL(const char* url, char* scheme, size_t maxSchemeLen, char* host, size_t maxHostLen, uint16_t* port, char* path, size_t maxPathLen) //Parse URL
donatien 0:2ccb9960a044 527 {
WiredHome 20:4ea5255c1b04 528 char* schemePtr = (char*) url;
WiredHome 20:4ea5255c1b04 529 char* hostPtr = (char*) strstr(url, "://");
WiredHome 20:4ea5255c1b04 530 if(hostPtr == NULL) {
WiredHome 20:4ea5255c1b04 531 WARN("Could not find host");
WiredHome 20:4ea5255c1b04 532 return HTTP_PARSE; //URL is invalid
WiredHome 20:4ea5255c1b04 533 }
WiredHome 20:4ea5255c1b04 534
WiredHome 20:4ea5255c1b04 535 if( maxSchemeLen < hostPtr - schemePtr + 1 ) { //including NULL-terminating char
WiredHome 20:4ea5255c1b04 536 WARN("Scheme str is too small (%d >= %d)", maxSchemeLen, hostPtr - schemePtr + 1);
WiredHome 20:4ea5255c1b04 537 return HTTP_PARSE;
WiredHome 20:4ea5255c1b04 538 }
WiredHome 20:4ea5255c1b04 539 memcpy(scheme, schemePtr, hostPtr - schemePtr);
WiredHome 20:4ea5255c1b04 540 scheme[hostPtr - schemePtr] = '\0';
donatien 0:2ccb9960a044 541
WiredHome 20:4ea5255c1b04 542 hostPtr+=3;
donatien 0:2ccb9960a044 543
WiredHome 20:4ea5255c1b04 544 size_t hostLen = 0;
donatien 0:2ccb9960a044 545
WiredHome 20:4ea5255c1b04 546 char* portPtr = strchr(hostPtr, ':');
WiredHome 20:4ea5255c1b04 547 if( portPtr != NULL ) {
WiredHome 20:4ea5255c1b04 548 hostLen = portPtr - hostPtr;
WiredHome 20:4ea5255c1b04 549 portPtr++;
WiredHome 20:4ea5255c1b04 550 if( sscanf(portPtr, "%hu", port) != 1) {
WiredHome 20:4ea5255c1b04 551 WARN("Could not find port");
WiredHome 20:4ea5255c1b04 552 return HTTP_PARSE;
WiredHome 20:4ea5255c1b04 553 }
WiredHome 20:4ea5255c1b04 554 } else {
WiredHome 20:4ea5255c1b04 555 *port=0;
donatien 0:2ccb9960a044 556 }
WiredHome 20:4ea5255c1b04 557 char* pathPtr = strchr(hostPtr, '/');
WiredHome 20:4ea5255c1b04 558 if( hostLen == 0 ) {
WiredHome 20:4ea5255c1b04 559 hostLen = pathPtr - hostPtr;
WiredHome 20:4ea5255c1b04 560 }
donatien 0:2ccb9960a044 561
WiredHome 20:4ea5255c1b04 562 if( maxHostLen < hostLen + 1 ) { //including NULL-terminating char
WiredHome 20:4ea5255c1b04 563 WARN("Host str is too small (%d >= %d)", maxHostLen, hostLen + 1);
WiredHome 20:4ea5255c1b04 564 return HTTP_PARSE;
WiredHome 20:4ea5255c1b04 565 }
WiredHome 20:4ea5255c1b04 566 memcpy(host, hostPtr, hostLen);
WiredHome 20:4ea5255c1b04 567 host[hostLen] = '\0';
donatien 0:2ccb9960a044 568
WiredHome 20:4ea5255c1b04 569 size_t pathLen;
WiredHome 20:4ea5255c1b04 570 char* fragmentPtr = strchr(hostPtr, '#');
WiredHome 20:4ea5255c1b04 571 if(fragmentPtr != NULL) {
WiredHome 20:4ea5255c1b04 572 pathLen = fragmentPtr - pathPtr;
WiredHome 20:4ea5255c1b04 573 } else {
WiredHome 20:4ea5255c1b04 574 pathLen = strlen(pathPtr);
WiredHome 20:4ea5255c1b04 575 }
donatien 0:2ccb9960a044 576
WiredHome 20:4ea5255c1b04 577 if( maxPathLen < pathLen + 1 ) { //including NULL-terminating char
WiredHome 20:4ea5255c1b04 578 WARN("Path str is too small (%d >= %d)", maxPathLen, pathLen + 1);
WiredHome 20:4ea5255c1b04 579 return HTTP_PARSE;
WiredHome 20:4ea5255c1b04 580 }
WiredHome 20:4ea5255c1b04 581 memcpy(path, pathPtr, pathLen);
WiredHome 20:4ea5255c1b04 582 path[pathLen] = '\0';
donatien 0:2ccb9960a044 583
WiredHome 20:4ea5255c1b04 584 return HTTP_OK;
donatien 0:2ccb9960a044 585 }
kazushi2008 18:cf5d7427a9ec 586
WiredHome 20:4ea5255c1b04 587 void HTTPClient::createauth (const char *user, const char *pwd, char *buf, int len)
WiredHome 20:4ea5255c1b04 588 {
kazushi2008 18:cf5d7427a9ec 589 char tmp[80];
WiredHome 20:4ea5255c1b04 590
kazushi2008 18:cf5d7427a9ec 591 snprintf(tmp, sizeof(tmp), "%s:%s", user, pwd);
kazushi2008 18:cf5d7427a9ec 592 base64enc(tmp, strlen(tmp), &buf[strlen(buf)], len - strlen(buf));
kazushi2008 18:cf5d7427a9ec 593 }
kazushi2008 18:cf5d7427a9ec 594
kazushi2008 18:cf5d7427a9ec 595 // Copyright (c) 2010 Donatien Garnier (donatiengar [at] gmail [dot] com)
WiredHome 20:4ea5255c1b04 596 int HTTPClient::base64enc(const char *input, unsigned int length, char *output, int len)
WiredHome 20:4ea5255c1b04 597 {
WiredHome 20:4ea5255c1b04 598 static const char base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
WiredHome 20:4ea5255c1b04 599 unsigned int c, c1, c2, c3;
WiredHome 20:4ea5255c1b04 600
WiredHome 20:4ea5255c1b04 601 if (len < ((((length-1)/3)+1)<<2)) return -1;
WiredHome 20:4ea5255c1b04 602 for(unsigned int i = 0, j = 0; i<length; i+=3,j+=4) {
WiredHome 20:4ea5255c1b04 603 c1 = ((((unsigned char)*((unsigned char *)&input[i]))));
WiredHome 20:4ea5255c1b04 604 c2 = (length>i+1)?((((unsigned char)*((unsigned char *)&input[i+1])))):0;
WiredHome 20:4ea5255c1b04 605 c3 = (length>i+2)?((((unsigned char)*((unsigned char *)&input[i+2])))):0;
WiredHome 20:4ea5255c1b04 606
WiredHome 20:4ea5255c1b04 607 c = ((c1 & 0xFC) >> 2);
WiredHome 20:4ea5255c1b04 608 output[j+0] = base64[c];
WiredHome 20:4ea5255c1b04 609 c = ((c1 & 0x03) << 4) | ((c2 & 0xF0) >> 4);
WiredHome 20:4ea5255c1b04 610 output[j+1] = base64[c];
WiredHome 20:4ea5255c1b04 611 c = ((c2 & 0x0F) << 2) | ((c3 & 0xC0) >> 6);
WiredHome 20:4ea5255c1b04 612 output[j+2] = (length>i+1)?base64[c]:'=';
WiredHome 20:4ea5255c1b04 613 c = (c3 & 0x3F);
WiredHome 20:4ea5255c1b04 614 output[j+3] = (length>i+2)?base64[c]:'=';
WiredHome 20:4ea5255c1b04 615 }
WiredHome 20:4ea5255c1b04 616 output[(((length-1)/3)+1)<<2] = '\0';
WiredHome 20:4ea5255c1b04 617 return 0;
kazushi2008 18:cf5d7427a9ec 618 }