(file) Return to httpclient.c CVS log (file) (dir) Up to [OMI] / omi / http

   1 mike  1.1 /*
   2           **==============================================================================
   3           **
   4           ** Open Management Infrastructure (OMI)
   5           **
   6           ** Copyright (c) Microsoft Corporation
   7           **
   8 krisbash 1.3 ** Licensed under the Apache License, Version 2.0 (the "License"); you may not
   9              ** use this file except in compliance with the License. You may obtain a copy
  10              ** of the License at
  11              **
  12              **     http://www.apache.org/licenses/LICENSE-2.0
  13 mike     1.1 **
  14              ** THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  15 krisbash 1.3 ** KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
  16              ** WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
  17              ** MERCHANTABLITY OR NON-INFRINGEMENT.
  18 mike     1.1 **
  19 krisbash 1.3 ** See the Apache 2 License for the specific language governing permissions
  20 mike     1.1 ** and limitations under the License.
  21              **
  22              **==============================================================================
  23              */
  24              
  25              #include <assert.h>
  26              #include <ctype.h>
  27 krisbash 1.3 #include "httpcommon.h"
  28 mike     1.1 #include "httpclient.h"
  29              #include <sock/addr.h>
  30              #include <sock/sock.h>
  31              #include <sock/selector.h>
  32 krisbash 1.3 #include <pal/sleep.h>
  33 mike     1.1 #include <base/buf.h>
  34              #include <base/log.h>
  35              #include <base/result.h>
  36 krisbash 1.3 #include <pal/strings.h>
  37              #include <pal/format.h>
  38              #include <pal/sleep.h>
  39 mike     1.1 #include <base/paths.h>
  40              
  41 krisbash 1.3 // #define ENABLE_TRACING 1
  42              #ifdef ENABLE_TRACING
  43              # define TRACING_LEVEL 4
  44              # include <deprecated/logging/logging.h>
  45              #else
  46              # define LOGE2(a)
  47              # define LOGW2(a)
  48              # define LOGD2(a)
  49              # define LOGX2(a)
  50              #endif
  51              
  52 mike     1.1 #ifdef CONFIG_POSIX
  53              #include <openssl/ssl.h>
  54              #include <openssl/err.h>
  55              #else
  56              /* ssl not supported in this configuration; just make compiler happy */
  57              typedef void SSL;
  58              typedef void SSL_CTX;
  59              #define SSL_CTX_free(c)
  60              #define SSL_new(c) 0
  61              #define SSL_free(c)
  62              #define SSL_set_connect_state(c)
  63              #define SSL_set_fd(c,a) (a==a)
  64              #define SSL_read(c,a,b) 0
  65              #define SSL_write(c,a,b) 0
  66              #define SSL_get_error(c,e) e
  67              #define SSL_ERROR_WANT_WRITE 0
  68              #define SSL_ERROR_WANT_READ 1
  69              #define SSL_ERROR_SYSCALL 2
  70              
  71              #ifdef EWOULDBLOCK
  72              # undef EWOULDBLOCK
  73 mike     1.1 #endif
  74              
  75              #define EWOULDBLOCK 0
  76              
  77              #ifdef EINPROGRESS
  78              # undef EINPROGRESS
  79 krisbash 1.3 #endif
  80 mike     1.1 
  81              #define EINPROGRESS 0
  82              
  83              #define ERR_get_error() 0
  84              #define ERR_error_string_n(c,a,b) a[0]=0
  85              #define SSL_accept(c) 0
  86              #define SSL_connect(c) 0
  87              
  88              #endif
  89              
  90              /*
  91              **==============================================================================
  92              **
  93              ** Local definitions:
  94              **
  95              **==============================================================================
  96              */
  97              
  98              static const MI_Uint32 _MAGIC = 0x5FC7B966;
  99              static const MI_Uint32 MAX_HEADER_SIZE = 2 * 1024;
 100              static const MI_Uint32 INITIAL_BUFFER_SIZE = 2 * 1024;
 101 mike     1.1 static const MI_Uint32 DEFAULT_HTTP_TIMEOUT_USEC = 60 * 1000000;
 102              
 103              typedef enum _Http_RecvState
 104              {
 105                  RECV_STATE_HEADER,
 106                  RECV_STATE_CONTENT,
 107                  RECV_STATE_CHUNKHEADER,
 108                  RECV_STATE_CHUNKDATA
 109              }
 110              Http_RecvState;
 111              
 112 krisbash 1.3 typedef struct _HttpClient_SR_SocketData
 113 mike     1.1 {
 114                  /* based member*/
 115                  Handler     base;
 116              
 117                  /* timeout */
 118                  MI_Uint64   timeoutUsec;
 119              
 120                  /* ssl part */
 121                  SSL*  ssl;
 122                  MI_Boolean  reverseOperations;  /*reverse read/write Events/Handlers*/
 123                  MI_Boolean  connectDone;
 124              
 125                  /* receiving data */
 126 krisbash 1.3     __field_ecount(recvBufferSize) char* recvBuffer;
 127 mike     1.1     size_t      recvBufferSize;
 128 krisbash 1.3     size_t      receivedSize;
 129 mike     1.1     Http_RecvState      recvingState;
 130                  HttpClientHeaderField recvHeaderFields[64];
 131                  HttpClientResponseHeader   recvHeaders;
 132                  MI_Sint64   contentLength;
 133 krisbash 1.3     MI_Sint64   contentBegin;
 134                  MI_Sint64   contentEnd;
 135                  MI_Sint64   contentTotalLength;
 136 mike     1.1     Page*   recvPage;
 137              
 138 krisbash 1.3     /* flag for a response from a HEAD request */
 139                  MI_Boolean  headVerb;
 140              
 141 mike     1.1     /* sending part */
 142                  Page*   sendPage;
 143                  Page*   sendHeader;
 144                  size_t      sentSize;
 145                  Http_RecvState  sendingState;
 146              
 147                  /* general operation status */
 148                  MI_Result status;
 149              
 150              }
 151 krisbash 1.3 HttpClient_SR_SocketData;
 152 mike     1.1 
 153              struct _HttpClient
 154              {
 155                  MI_Uint32       magic;
 156                  Selector        internalSelector;
 157                  Selector*       selector;
 158                  HttpClientCallbackOnStatus     callbackOnStatus;
 159                  HttpClientCallbackOnResponse   callbackOnResponse;
 160                  void*                               callbackData;
 161                  SSL_CTX*    sslContext;
 162              
 163 krisbash 1.3     HttpClient_SR_SocketData* connector;
 164 mike     1.1 
 165                  MI_Boolean  internalSelectorUsed;
 166              };
 167              
 168              
 169              /* helper functions result */
 170              typedef enum _Http_CallbackResult
 171              {
 172 krisbash 1.3     PRT_RETURN_FALSE,
 173 mike     1.1     PRT_RETURN_TRUE,
 174 krisbash 1.3     PRT_CONTINUE
 175 mike     1.1 }
 176              Http_CallbackResult;
 177              
 178              
 179              MI_INLINE MI_Uint8 _ToLower(MI_Uint8 x)
 180              {
 181                  return (MI_Uint8)tolower(x);
 182              }
 183              
 184              #define _HashCode(first,last,len) ( (((MI_Uint8)first) << 16) | (((MI_Uint8)last) << 8)  | (((MI_Uint16)len)) )
 185              
 186 krisbash 1.3 _Return_type_success_(return == MI_TRUE)
 187 mike     1.1 static MI_Boolean _getNameValuePair(
 188 krisbash 1.3     _Inout_ CharPtr* line,
 189                  _Out_ CharPtr* value,
 190                  _Out_ int*  nameHashCode)
 191 mike     1.1 {
 192                  int len = 0;
 193                  char* p;
 194                  /* find name end /hash-code */
 195              
 196 krisbash 1.3     if ((*line)[0] == '\0')
 197                  {
 198                      return MI_FALSE;
 199                  }
 200              
 201 mike     1.1     *nameHashCode =  _ToLower((MI_Uint8)(*line)[0])<<16;
 202              
 203 krisbash 1.3     for (len = 1; (*line)[len] != '\0' && (*line)[len] != ':' && (*line)[len] != '\r'; len++)
 204 mike     1.1         ;
 205              
 206                  if ((*line)[len] != ':')
 207                      return MI_FALSE;
 208              
 209                  *nameHashCode |=  (len) | _ToLower((MI_Uint8)(*line)[len-1])<<8;
 210                  (*line)[len] = 0;
 211                  p = *line + len + 1;
 212              
 213                  /* skip spaces in value */
 214                  while (p[0] == ' ' || p[0] == '\t')
 215                      p++;
 216              
 217                  *value = p;
 218              
 219                  /* skip to end of line */
 220 krisbash 1.3     for ( ; p[0]; )
 221 mike     1.1     {
 222 krisbash 1.3         if (p[0] != '\0' && p[1] != '\0' && p[2] != '\0' &&
 223                          p[0] == '\r' && p[1] == '\n' &&
 224                          p[2] != ' '  && p[2] != '\t')
 225 mike     1.1         {
 226 krisbash 1.3             p[0] = '\0';
 227 mike     1.1             (*line) = p + 2;
 228                          break;
 229                      }
 230 krisbash 1.3         p++;
 231 mike     1.1     }
 232              
 233                  /* remove trailing spaces */
 234                  p--;
 235 krisbash 1.3 #ifdef _PREFAST_
 236              #pragma prefast (push)
 237              #pragma prefast (disable: 26001)
 238              #endif
 239                  /* disabling IPv6 OACR warnings - D3M bug 56 */
 240 mike     1.1     while (p[0] == ' ' || p[0] == '\t')
 241                      p--;
 242 krisbash 1.3 #ifdef _PREFAST_
 243              #pragma prefast (pop)
 244              #endif
 245 mike     1.1 
 246 krisbash 1.3     p[1] = '\0';
 247 mike     1.1 
 248                  return MI_TRUE;
 249              }
 250              
 251              static MI_Boolean _getHeaderField(
 252 krisbash 1.3     HttpClient_SR_SocketData* handler,
 253                  _Inout_ CharPtr* line)
 254 mike     1.1 {
 255                  /* expecting  Request-Line = Method SP Request-URI SP HTTP-Version CRLF
 256                      Read more: http://www.faqs.org/rfcs/rfc2616.html#ixzz0jKdjJdZv
 257                  */
 258                  char* name = *line;
 259                  char* value = NULL;
 260                  int nameHashCode;
 261              
 262                  if (!_getNameValuePair(line, &value, &nameHashCode))
 263                      return MI_FALSE;
 264              
 265 krisbash 1.3     if (nameHashCode == _HashCode('c', 'h', 14) && /*Content-Length*/
 266 mike     1.1         Strcasecmp(name,"Content-Length") == 0)
 267                  {
 268 krisbash 1.3         handler->contentLength = Strtoull(value, NULL, 10);
 269                      /*if (handler->contentLength > HTTP_MAX_CONTENT)
 270                      handler->contentBegin = -1;
 271                      handler->contentEnd = -1;
 272                      handler->contentTotalLength = -1;
 273                      return MI_FALSE;*/
 274 mike     1.1     }
 275 krisbash 1.3     else if (nameHashCode == _HashCode('t', 'g', 17) && /*Transfer-Encoding*/
 276 mike     1.1         Strcasecmp(name,"Transfer-Encoding") == 0)
 277                  {
 278 krisbash 1.3         handler->contentLength = -1;
 279                      handler->contentBegin = -1;
 280                      handler->contentEnd = -1;
 281                      handler->contentTotalLength = -1;
 282                  }
 283                  else if (nameHashCode == _HashCode('c','e',13) && /*Content-Range*/
 284                      Strcasecmp(name, "Content-Range") == 0)
 285                  {
 286                      char* delimptr;
 287                      char* endptr;
 288                      handler->contentEnd = -1;
 289                      handler->contentBegin = (MI_Sint64)Strtoull(value, NULL, 10);
 290                      delimptr = strchr(value, '-');
 291                      endptr = strchr(value, '\n');
 292                      if (delimptr != NULL && (endptr == NULL || endptr > delimptr))
 293                          handler->contentEnd = Strtoull(++delimptr, NULL, 10);
 294                      delimptr = strchr(value, '/');
 295                      if (delimptr != NULL && (endptr == NULL || endptr > delimptr))
 296                          handler->contentTotalLength = Strtoull(++delimptr, NULL, 10);
 297                  }
 298                  if (handler->recvHeaders.sizeHeaders < MI_COUNT(handler->recvHeaderFields))
 299 krisbash 1.3     {
 300                      handler->recvHeaderFields[handler->recvHeaders.sizeHeaders].name = name;
 301                      handler->recvHeaderFields[handler->recvHeaders.sizeHeaders].value = value;
 302                      handler->recvHeaders.sizeHeaders++;
 303 mike     1.1     }
 304                  else
 305                  {
 306 krisbash 1.3         trace_TooManyHttpHeaders(scs(name), scs(value));
 307 mike     1.1     }
 308              
 309                  return MI_TRUE;
 310              }
 311              
 312              static MI_Boolean _getChunkSize(
 313                  const char * line,
 314                  MI_Uint32* chunkSize)
 315              {
 316                  *chunkSize = 0;
 317              
 318 krisbash 1.3     while (*line)
 319 mike     1.1     {
 320                      char c = *line;
 321              
 322                      if (c >= '0' && c <= '9')
 323                          *chunkSize = *chunkSize * 16 + (c - '0');
 324                      else if (c >= 'a' && c <= 'f')
 325                          *chunkSize = *chunkSize * 16 + (c - 'a' + 10);
 326                      else if (c >= 'A' && c <= 'F')
 327                          *chunkSize = *chunkSize * 16 + (c - 'A' + 10);
 328                      else
 329                          break;
 330              
 331                      line++;
 332                  }
 333              
 334                  return MI_TRUE;
 335              }
 336              
 337              
 338              static MI_Boolean _getRequestLine(
 339 krisbash 1.3     HttpClient_SR_SocketData* handler,
 340                  _Inout_ CharPtr* line)
 341 mike     1.1 {
 342                  size_t index;
 343                  /* expecting  Request-Line = Method SP Request-URI SP HTTP-Version CRLF
 344                      Read more: http://www.faqs.org/rfcs/rfc2616.html#ixzz0jKdjJdZv
 345                  */
 346              
 347                  /* initialize header */
 348                  handler->recvHeaders.sizeHeaders = 0;
 349                  handler->recvHeaders.headers = handler->recvHeaderFields;
 350              
 351                  /* find http code */
 352                  {
 353                      /* skip http version, that is in format
 354                              HTTP-Version   = "HTTP" "/" 1*DIGIT "." 1*DIGIT
 355                              http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.1
 356              
 357                              Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF
 358                      */
 359 krisbash 1.3         const char* s = *line; /*+ HTTP/1.1<sp>*/
 360                      size_t skips = 9;
 361                      for (; skips > 0; s++)
 362                      {
 363                          skips--;
 364                          if (!*s)
 365                          {
 366                              return MI_FALSE;
 367                          }
 368                      }
 369 mike     1.1 
 370                      handler->recvHeaders.httpError = Strtoul(s, NULL, 10);
 371                  }
 372              
 373                  /* skip to end of line */
 374 krisbash 1.3     for (index = 1; (*line)[index] && index < handler->receivedSize; index++)
 375 mike     1.1     {
 376 krisbash 1.3         if ((*line)[index-1] == '\r' && (*line)[index] == '\n')
 377 mike     1.1         {
 378                          (*line) = (*line) + index + 1;
 379                          return MI_TRUE;
 380                      }
 381                  }
 382              
 383                  return MI_FALSE;
 384              }
 385              
 386              static MI_Result _Sock_Read(
 387 krisbash 1.3     HttpClient_SR_SocketData* handler,
 388 mike     1.1     void* buf,
 389                  size_t buf_size,
 390                  size_t* sizeRead)
 391              {
 392                  int res;
 393 krisbash 1.3     int sslError;
 394              
 395                  if (handler->ssl == NULL)
 396                  {
 397                      MI_Result res = Sock_Read(handler->base.sock, buf, buf_size, sizeRead);
 398 mike     1.1 
 399 krisbash 1.3         LOGD2((ZT("_Sock_Read - After regular read. socket: %d, result: %d (%s), bytes read: %u / %u"), handler->base.sock, (int)res, mistrerror(res), (unsigned int)*sizeRead, (unsigned int)buf_size));
 400                      return res;
 401                  }
 402 mike     1.1 
 403                  handler->base.mask &= ~SELECTOR_WRITE;
 404                  handler->base.mask |= SELECTOR_READ;
 405                  handler->reverseOperations = MI_FALSE;
 406              
 407                  *sizeRead = 0;
 408              
 409                  res = SSL_read(handler->ssl, buf, buf_size);
 410 krisbash 1.3     LOGD2((ZT("_Sock_Read - SSL_Read returned: %d (< 0 for error) / %u bytes read, errno: %d (%s)"), res, (unsigned int)buf_size, errno, strerror(errno)));
 411                  if (res == 0)
 412                  {
 413                      LOGW2((ZT("_Sock_Read - SSL socket connection closed. socket: %d"), handler->base.sock));
 414 mike     1.1         return MI_RESULT_OK;    /* connection closed */
 415 krisbash 1.3     }
 416 mike     1.1 
 417 krisbash 1.3     if (res > 0)
 418 mike     1.1     {
 419 krisbash 1.3         LOGD2((ZT("_Sock_read - Bytes read: %d"), res));
 420 mike     1.1         *sizeRead = res;
 421                      return MI_RESULT_OK;    /* ok */
 422                  }
 423              
 424 krisbash 1.3     sslError = SSL_get_error(handler->ssl, res);
 425                  switch (sslError)
 426 mike     1.1     {
 427                  case SSL_ERROR_WANT_WRITE:
 428                      handler->reverseOperations = MI_TRUE;   /* wait until write is allowed */
 429                      handler->base.mask &= ~SELECTOR_READ;
 430                      handler->base.mask |= SELECTOR_WRITE;
 431 krisbash 1.3         LOGD2((ZT("_Sock_Read - SSL_read/accept returned WANT_WRITE")));
 432 mike     1.1         return MI_RESULT_WOULD_BLOCK;
 433              
 434                  case SSL_ERROR_WANT_READ:
 435 krisbash 1.3         LOGD2((ZT("Sock_Read - SSL_read/accept returned WANT_READ")));
 436 mike     1.1         return MI_RESULT_WOULD_BLOCK;
 437              
 438                  case SSL_ERROR_SYSCALL:
 439                      if (EAGAIN == errno ||
 440                          EWOULDBLOCK == errno ||
 441                          EINPROGRESS == errno)
 442                          return MI_RESULT_WOULD_BLOCK;
 443              
 444 krisbash 1.3         LOGE2((ZT("Sock_Read - SSL_read returned OS error %d (%s)"), errno, strerror(errno)));
 445                      trace_SSLRead_UnexpectedSysError(errno);
 446 mike     1.1         break;
 447              
 448                  default:
 449 krisbash 1.3         /* print Open SSL error stack */
 450 mike     1.1         {
 451 krisbash 1.3             unsigned long err;
 452                          while ((err = ERR_get_error()) != 0)
 453 mike     1.1             {
 454                              char err_txt[200];
 455              
 456 krisbash 1.3                 ERR_error_string_n(err, err_txt, sizeof (err_txt));
 457                              LOGE2((ZT("_Sock_Read - SSL_read returned OpenSSL error: %lu (%s)"), err, err_txt));
 458 mike     1.1             }
 459                      }
 460                      break;
 461                  }
 462                  return MI_RESULT_FAILED;
 463              }
 464              
 465              static MI_Result _Sock_Write(
 466 krisbash 1.3     HttpClient_SR_SocketData* handler,
 467 mike     1.1     void* buf,
 468                  size_t buf_size,
 469                  size_t* sizeWritten)
 470              {
 471                  int res;
 472 krisbash 1.3     int sslError;
 473 mike     1.1 
 474                  if (!handler->ssl)
 475 krisbash 1.3     {
 476                      MI_Result res = Sock_Write(handler->base.sock, buf, buf_size, sizeWritten);
 477                      LOGD2((ZT("_Sock_Write - Non-SSS write. Sock_Write returned %d (%s). %u / %u bytes sent"), res, mistrerror(res), (unsigned int)*sizeWritten, (unsigned int)buf_size));
 478                      return res;
 479                  }
 480 mike     1.1 
 481                  /* Do not clear READ flag, since 'close' notification
 482 krisbash 1.3        delivered as READ event */
 483 mike     1.1     handler->base.mask &= ~SELECTOR_READ; 
 484                  handler->base.mask |= SELECTOR_WRITE;
 485                  handler->reverseOperations = MI_FALSE;
 486              
 487                  *sizeWritten = 0;
 488              
 489                  if (handler->connectDone)
 490                  {
 491                      res = SSL_write(handler->ssl, buf, buf_size);
 492 krisbash 1.3         LOGD2((ZT("_Sock_Write - SSL_write using socket %d returned %d (< 0 for error) / %u bytes written, errno: %d (%s)"), handler->base.sock, res, (unsigned int)buf_size, errno, strerror(errno)));
 493 mike     1.1     }
 494                  else
 495                  {
 496                      res = SSL_connect(handler->ssl);
 497 krisbash 1.3         LOGD2((ZT("_Sock_Write - SSL connect using socket %d returned result: %d, errno: %d (%s)"), handler->base.sock, res, errno, strerror(errno)));
 498                      if (res > 0)
 499 mike     1.1         {
 500 krisbash 1.3             /* we are done with accept */
 501 mike     1.1             handler->connectDone = MI_TRUE;
 502                          return _Sock_Write(handler,buf,buf_size,sizeWritten);
 503                      }
 504                      /* perform regular error checking */
 505                  }
 506              
 507              
 508 krisbash 1.3     if (res == 0)
 509                  {
 510                      LOGW2((ZT("_Sock_Write - SSL socket connection closed")));
 511 mike     1.1         return MI_RESULT_OK;    /* connection closed */
 512 krisbash 1.3     }
 513 mike     1.1 
 514 krisbash 1.3     if (res > 0)
 515 mike     1.1     {
 516                      *sizeWritten = res;
 517 krisbash 1.3         LOGD2((ZT("_Sock_Write - SSL socket successful write of %d / %u bytes"), res, (unsigned int)buf_size));
 518 mike     1.1         return MI_RESULT_OK;    /* ok */
 519                  }
 520              
 521 krisbash 1.3     sslError = SSL_get_error(handler->ssl, res);
 522                  switch (sslError)
 523 mike     1.1     {
 524                  case SSL_ERROR_WANT_WRITE:
 525 krisbash 1.3         LOGD2((ZT("_Sock_Write - SSL_write/connect returned WANT_WRITE")));
 526 mike     1.1         return MI_RESULT_WOULD_BLOCK;
 527              
 528                  case SSL_ERROR_WANT_READ:
 529 krisbash 1.3         LOGD2((ZT("_Sock_Write - SSL_write/connect returned WANT_READ")));
 530 mike     1.1         handler->reverseOperations = MI_TRUE;   /* wait until write is allowed */
 531                      handler->base.mask |= SELECTOR_READ;
 532                      handler->base.mask &= ~SELECTOR_WRITE; 
 533                      return MI_RESULT_WOULD_BLOCK;
 534              
 535                  case SSL_ERROR_SYSCALL:
 536                      if (EAGAIN == errno ||
 537                          EWOULDBLOCK == errno ||
 538                          EINPROGRESS == errno)
 539 krisbash 1.3         {
 540                          LOGD2((ZT("_Sock_Write - Returning WOULD_BLOCK. errno: %d (%s)"), errno, strerror(errno)));
 541 mike     1.1             return MI_RESULT_WOULD_BLOCK;
 542 krisbash 1.3         }
 543              
 544                      LOGE2((ZT("_Sock_Write - SSL_write/connect returned unexpected OS error %d (%s)"), errno, strerror(errno)));
 545                      trace_SSLWrite_UnexpectedSysError(errno);
 546                      break;
 547 mike     1.1 
 548 krisbash 1.3     case SSL_ERROR_SSL:
 549                      LOGE2((ZT("_Sock_Write - SSL_write/connect returned OpenSSL error %d (%s)"), sslError, ERR_error_string(sslError, NULL)));
 550 mike     1.1         break;
 551              
 552                  default:
 553 krisbash 1.3         LOGD2((ZT("_Sock_Write - SSL_write/connect returned uncategorized OpenSSL error: %d"), res));
 554 mike     1.1         break;
 555                  }
 556 krisbash 1.3 
 557 mike     1.1     return MI_RESULT_FAILED;
 558              }
 559              
 560              static Http_CallbackResult _ReadHeader(
 561 krisbash 1.3     HttpClient_SR_SocketData* handler)
 562 mike     1.1 {
 563                  char* buf;
 564                  char* currentLine;
 565                  char* data;
 566 krisbash 1.3     size_t contentSize;
 567 mike     1.1     size_t buf_size, received, index;
 568                  MI_Result r;
 569                  MI_Boolean fullHeaderReceived = MI_FALSE;
 570              
 571                  /* are we done with header? */
 572                  if (handler->recvingState != RECV_STATE_HEADER)
 573                      return PRT_CONTINUE;
 574              
 575 krisbash 1.3     buf = handler->recvBuffer + handler->receivedSize;
 576                  buf_size = handler->recvBufferSize - handler->receivedSize;
 577 mike     1.1     received = 0;
 578              
 579                  r = _Sock_Read(handler, buf, buf_size, &received);
 580 krisbash 1.3     LOGD2((ZT("_ReadHeader - Begin. _Sock_read result: %d (%s), socket: %d, %u / %u bytes read, reverse: %d"), (int)r, mistrerror(r), (int)handler->base.sock, (unsigned int)received, (unsigned int)buf_size, (int)handler->reverseOperations));
 581 mike     1.1 
 582 krisbash 1.3     if (r == MI_RESULT_OK && 0 == received)
 583                  {
 584                      LOGW2((ZT("_ReadHeader - 0 bytes received without error. Socket closed?")));
 585                      return PRT_RETURN_FALSE; /* connection closed */
 586                  }
 587 mike     1.1 
 588 krisbash 1.3     if (r != MI_RESULT_OK && r != MI_RESULT_WOULD_BLOCK)
 589                  {
 590                      LOGE2((ZT("_ReadHeader - Error %d (%s)"), r, mistrerror(r)));
 591 mike     1.1         return PRT_RETURN_FALSE;
 592 krisbash 1.3     }
 593 mike     1.1 
 594 krisbash 1.3     if (received == 0)
 595                  {
 596                      LOGD2((ZT("_ReadHeader - 0 bytes received. Waiting...")));
 597 mike     1.1         return PRT_RETURN_TRUE;
 598 krisbash 1.3     }
 599 mike     1.1 
 600 krisbash 1.3     handler->receivedSize += received;
 601 mike     1.1 
 602                  /* check header */
 603 krisbash 1.3     LOGD2((ZT("_ReadHeader - Received buffer: %s"), buf));
 604 mike     1.1 
 605                  /* did we get full header? */
 606                  buf = handler->recvBuffer;
 607 krisbash 1.3     LOGD2((ZT("_ReadHeader - Checking for full header...")));
 608                  for ( index = 3; index < handler->receivedSize; index++ )
 609 mike     1.1     {
 610 krisbash 1.3         _Analysis_assume_(handler->recvBufferSize > 3);
 611 mike     1.1         if (buf[index-3] == '\r' && buf[index-1] == '\r' &&
 612                          buf[index-2] == '\n' && buf[index] == '\n' )
 613                      {
 614                          fullHeaderReceived = MI_TRUE;
 615 krisbash 1.3             LOGD2((ZT("_ReadHeader - Full header has been received")));
 616 mike     1.1             break;
 617                      }
 618                  }
 619              
 620 krisbash 1.3     if (!fullHeaderReceived)
 621 mike     1.1     {
 622 krisbash 1.3         if (handler->receivedSize <  handler->recvBufferSize)
 623                      {
 624                          LOGD2((ZT("_ReadHeader - Full header not received. Waiting...")));
 625 mike     1.1             return PRT_RETURN_TRUE; /* continue reading */
 626 krisbash 1.3         }
 627 mike     1.1 
 628 krisbash 1.3         if (handler->recvBufferSize < MAX_HEADER_SIZE)
 629 mike     1.1         {
 630 krisbash 1.3             LOGD2((ZT("_ReadHeader - Reallocating buffer...")));
 631                          buf = PAL_Realloc(handler->recvBuffer, handler->recvBufferSize * 2);
 632 mike     1.1 
 633                          if (!buf)
 634 krisbash 1.3             {
 635                              LOGE2((ZT("_ReadHeader - Cannot allocate memory for larger header")));
 636 mike     1.1                 return PRT_RETURN_FALSE;
 637 krisbash 1.3             }
 638 mike     1.1 
 639                          handler->recvBufferSize *= 2;
 640                          handler->recvBuffer = buf;
 641 krisbash 1.3             LOGD2((ZT("_ReadHeader - Going recursive...")));
 642 mike     1.1             return _ReadHeader(handler);
 643                      }
 644                      else
 645                      {
 646 krisbash 1.3             /* Http header is too big - drop connection */
 647                          trace_HttpHeaderIsTooBig();
 648                          LOGE2((ZT("_ReadHeader - HTTP header is too big. Dropping connection")));
 649 mike     1.1             return PRT_RETURN_FALSE;
 650                      }
 651                  }
 652              
 653                  /* consume data */
 654                  currentLine = buf;
 655                  data = buf + index + 1; /* pointer to data in case we got some */
 656              
 657                  if (!_getRequestLine(handler, &currentLine))
 658 krisbash 1.3     {
 659                      LOGE2((ZT("_ReadHeader - Cannot find request line in HTTP header")));
 660 mike     1.1         return PRT_RETURN_FALSE;
 661 krisbash 1.3     }
 662 mike     1.1 
 663 krisbash 1.3     while ((data - currentLine) > 3)
 664 mike     1.1     {
 665                      if (!_getHeaderField(handler, &currentLine))
 666 krisbash 1.3         {
 667                          LOGE2((ZT("_ReadHeader - Cannot find HTTP header field")));
 668 mike     1.1             return PRT_RETURN_FALSE;
 669 krisbash 1.3         }
 670 mike     1.1     }
 671              
 672                  /* Check if we have to deal with chunked-encoded data */
 673                  if (handler->contentLength < 0)
 674                  {
 675 krisbash 1.3         handler->receivedSize -= index + 1;
 676 mike     1.1 
 677                      /* Invoke user's callback with header information */
 678                      {
 679                          HttpClient* self = (HttpClient*)handler->base.data;
 680              
 681 krisbash 1.3             if (!(*self->callbackOnResponse)(self, self->callbackData, &handler->recvHeaders, 
 682 mike     1.1                 handler->contentLength, handler->contentLength == 0, 0))
 683 krisbash 1.3             {
 684                              LOGD2((ZT("_ReadHeader - On response callback for chunked data header failed")));
 685 mike     1.1                 return PRT_RETURN_FALSE;
 686 krisbash 1.3             }
 687 mike     1.1         }
 688              
 689                      /* remove consumed header part */
 690 krisbash 1.3         memmove(handler->recvBuffer, data, handler->receivedSize);
 691 mike     1.1 
 692                      handler->recvingState = RECV_STATE_CHUNKHEADER;
 693                      return PRT_CONTINUE;
 694                  }
 695              
 696 krisbash 1.3     contentSize = (size_t)handler->contentLength;
 697                  if (handler->headVerb)
 698                  {
 699                      LOGD2((ZT("_ReadHeader - HEAD response received. Download will contain %u bytes"), (unsigned int)contentSize));
 700                      contentSize = 0;
 701                  }
 702              
 703 mike     1.1     /* Allocate zero-terminated buffer */
 704 krisbash 1.3     handler->recvPage = (Page*)PAL_Malloc(sizeof (Page) + (size_t)contentSize + 1);
 705 mike     1.1 
 706 krisbash 1.3     if (handler->recvPage == NULL)
 707                  {
 708                      LOGD2((ZT("_ReadHeader - Cannot allocate memory for received page")));
 709 mike     1.1         return PRT_RETURN_FALSE;
 710 krisbash 1.3     }
 711                  ((char*)(handler->recvPage + 1))[contentSize] = '\0';
 712 mike     1.1 
 713 krisbash 1.3     handler->recvPage->u.s.size = (unsigned int)contentSize;
 714 mike     1.1     handler->recvPage->u.s.next = 0;
 715 krisbash 1.3     handler->receivedSize -= index + 1;
 716 mike     1.1 
 717                  /* Verify that we have not more than 'content-length' bytes in buffer left 
 718                      If we have more, assuming http client is invalid and drop connection */
 719 krisbash 1.3     if (handler->receivedSize > contentSize)
 720 mike     1.1     {
 721 krisbash 1.3         trace_HttpPayloadIsBiggerThanContentLength();
 722                      LOGE2((ZT("_ReadHeader - HTTP payload is bigger than content-length (%u > %u bytes)"), (unsigned int)handler->receivedSize, (unsigned int)contentSize));
 723 mike     1.1         return PRT_RETURN_FALSE;
 724                  }
 725              
 726 krisbash 1.3     if (handler->receivedSize != 0)
 727                      memcpy(handler->recvPage + 1, data, handler->receivedSize);
 728 mike     1.1     handler->recvingState = RECV_STATE_CONTENT;
 729              
 730                  /* Invoke user's callback with header information */
 731                  {
 732                      HttpClient* self = (HttpClient*)handler->base.data;
 733              
 734 krisbash 1.3         if (!(*self->callbackOnResponse)(self, self->callbackData, &handler->recvHeaders, 
 735 mike     1.1             handler->contentLength, handler->contentLength == 0, 0))
 736 krisbash 1.3         {
 737                          LOGE2((ZT("_ReadHeader - On response callback for header failed")));
 738 mike     1.1             return PRT_RETURN_FALSE;
 739 krisbash 1.3         }
 740 mike     1.1     }
 741              
 742 krisbash 1.3     LOGD2((ZT("_ReadHeader - OK exit")));
 743 mike     1.1     return PRT_CONTINUE;
 744              }
 745              
 746              static Http_CallbackResult _ReadData(
 747 krisbash 1.3     HttpClient_SR_SocketData* handler)
 748 mike     1.1 {
 749 krisbash 1.3     /* HttpClient* self = (HttpClient*)handler->base.data; */
 750 mike     1.1     char* buf;
 751                  size_t buf_size, received;
 752                  MI_Result r;
 753              
 754                  /* are we in the right state? */
 755                  if (handler->recvingState != RECV_STATE_CONTENT)
 756                      return PRT_RETURN_FALSE;
 757              
 758 krisbash 1.3     LOGD2((ZT("_ReadData - Begin. Head? %d"), handler->headVerb));
 759                  if (!handler->headVerb)
 760                  {
 761                      buf = (char*)(handler->recvPage + 1) + handler->receivedSize;
 762                      buf_size = (size_t)(handler->contentLength - handler->receivedSize);
 763                      received = 0;
 764              
 765                      if (buf_size != 0)
 766                      {
 767                          r = _Sock_Read(handler, buf, buf_size, &received);
 768                          LOGD2((ZT("_ReadData - _Sock_Read result: %d (%s), socket: %d, recv: %u"), (int)r, mistrerror(r), (int)handler->base.sock, (unsigned int)received));
 769 mike     1.1 
 770 krisbash 1.3             if (r == MI_RESULT_OK && 0 == received)
 771                              return PRT_RETURN_FALSE; /* connection closed */
 772 mike     1.1 
 773 krisbash 1.3             if (r != MI_RESULT_OK && r != MI_RESULT_WOULD_BLOCK)
 774                              return PRT_RETURN_FALSE;
 775 mike     1.1 
 776 krisbash 1.3             handler->receivedSize += received;
 777 mike     1.1 
 778 krisbash 1.3             LOGD2((ZT("_RequestCallback - Called _ReadData. %d / %d bytes read"), (int)handler->receivedSize, (int)handler->contentLength));
 779 mike     1.1 
 780 krisbash 1.3             if (handler->contentLength > 0 && handler->receivedSize < (size_t)handler->contentLength)
 781                          {                           /* assume 500 bytes per millisecond transmission */
 782                                                      /* wait to avoid spinning on _Sock_Read */
 783                              unsigned int bytesLeft = (unsigned int)handler->contentLength - (unsigned int)handler->receivedSize;
 784                              unsigned long msec = (unsigned long)(bytesLeft / 500 + 1);
 785 mike     1.1 
 786 krisbash 1.3                 Sleep_Milliseconds(msec);
 787                          }
 788                      }
 789 mike     1.1 
 790 krisbash 1.3         /* did we get all data? */
 791                      LOGD2((ZT("_ReadData - Received size: %d / %d"), (int)handler->receivedSize, (int)handler->contentLength));
 792                      if (handler->receivedSize != (size_t)handler->contentLength)
 793                          return PRT_RETURN_TRUE;
 794                  }
 795 mike     1.1  
 796                  /* Invoke user's callback with header information */
 797                  {
 798                      HttpClient* self = (HttpClient*)handler->base.data;
 799 krisbash 1.3         MI_Boolean lastChunk = MI_TRUE;
 800 mike     1.1 
 801 krisbash 1.3         if (handler->contentEnd >= 0 &&
 802                          handler->contentEnd + 1 < handler->contentTotalLength)
 803                      {
 804                          lastChunk = MI_FALSE;
 805                      }
 806              
 807                      if (!(*self->callbackOnResponse)(self, self->callbackData, 0, 
 808                          handler->contentLength, lastChunk, &handler->recvPage))
 809 mike     1.1             return PRT_RETURN_FALSE;
 810              
 811                      /* status callback */
 812                      handler->status = MI_RESULT_OK;
 813                      (*self->callbackOnStatus)(
 814                          self, 
 815                          self->callbackData, 
 816 krisbash 1.3             MI_RESULT_OK);
 817 mike     1.1     }
 818              
 819 krisbash 1.3     if (handler->recvPage != NULL)
 820                  {
 821                      LOGD2((ZT("_ReadData - Freeing recvPage. socket: %d"), (int)handler->base.sock));
 822                      PAL_Free(handler->recvPage);
 823                  }
 824 mike     1.1 
 825 krisbash 1.3     handler->recvPage = NULL;
 826                  handler->receivedSize = 0;
 827 mike     1.1     memset(&handler->recvHeaders, 0, sizeof(handler->recvHeaders));
 828                  handler->recvingState = RECV_STATE_HEADER;
 829 krisbash 1.3     LOGD2((ZT("_ReadData - OK exit")));
 830 mike     1.1     return PRT_CONTINUE;
 831              }
 832              
 833              static Http_CallbackResult _ReadChunkHeader(
 834 krisbash 1.3     HttpClient_SR_SocketData* handler)
 835 mike     1.1 {
 836                  char* buf;
 837                  char* currentLine;
 838                  char* data;
 839                  size_t buf_size, received, index;
 840                  MI_Result r;
 841                  MI_Boolean fullHeaderReceived = MI_FALSE;
 842                  MI_Uint32   chunkSize = 0;
 843                  MI_Boolean connectionClosed = MI_FALSE;
 844              
 845                  /* are we done with header? */
 846                  if (handler->recvingState != RECV_STATE_CHUNKHEADER)
 847                      return PRT_CONTINUE;
 848              
 849 krisbash 1.3     buf = handler->recvBuffer + handler->receivedSize;
 850                  buf_size = handler->recvBufferSize - handler->receivedSize;
 851 mike     1.1     received = 0;
 852              
 853                  r = _Sock_Read(handler, buf, buf_size, &received);
 854              
 855 krisbash 1.3     if (r == MI_RESULT_OK && 0 == received)
 856 mike     1.1     {
 857                      if (!handler->recvBufferSize)
 858 krisbash 1.3             return PRT_RETURN_FALSE; /* connection closed */
 859 mike     1.1 
 860                      connectionClosed = MI_TRUE;
 861                  }
 862              
 863 krisbash 1.3     if (r != MI_RESULT_OK && r != MI_RESULT_WOULD_BLOCK)
 864 mike     1.1         return PRT_RETURN_FALSE;
 865              
 866                  if (!received && !handler->recvBufferSize)
 867                      return PRT_RETURN_TRUE;
 868              
 869 krisbash 1.3     handler->receivedSize += received;
 870 mike     1.1 
 871                  /* did we get full header? */
 872                  buf = handler->recvBuffer;
 873 krisbash 1.3 
 874                  _Analysis_assume_(handler->recvBufferSize > 2);
 875                  for (index = 1; index < handler->receivedSize && buf[index]; index++)
 876 mike     1.1     {
 877                      if (buf[index-1] == '\r' && buf[index] == '\n' )
 878                      {
 879                          fullHeaderReceived = MI_TRUE;
 880                          break;
 881                      }
 882                  }
 883              
 884 krisbash 1.3     if (!fullHeaderReceived)
 885 mike     1.1     {
 886                      if (connectionClosed)
 887 krisbash 1.3             return PRT_RETURN_FALSE; /* connection closed */
 888 mike     1.1 
 889 krisbash 1.3         if (handler->receivedSize <  handler->recvBufferSize)
 890 mike     1.1             return PRT_RETURN_TRUE; /* continue reading */
 891              
 892 krisbash 1.3         if (handler->recvBufferSize < MAX_HEADER_SIZE)
 893 mike     1.1         {
 894 krisbash 1.3             buf = PAL_Realloc(handler->recvBuffer, handler->recvBufferSize * 2);
 895 mike     1.1 
 896                          if (!buf)
 897                              return PRT_RETURN_FALSE;
 898              
 899                          handler->recvBufferSize *= 2;
 900                          handler->recvBuffer = buf;
 901                          return _ReadChunkHeader(handler);
 902                      }
 903                      else
 904                      {
 905                          /* http chunk header is too big - drop connection */
 906 krisbash 1.3             trace_HttpChunkHeaderIsTooBig();
 907 mike     1.1             return PRT_RETURN_FALSE;
 908                      }
 909                  }
 910              
 911                  /* consume data */
 912                  currentLine = buf;
 913                  data = buf + index + 1; /* pointer to data in case we got some */
 914              
 915                  if (!_getChunkSize(currentLine, &chunkSize))
 916                      return PRT_RETURN_FALSE;
 917              
 918                  if (0 == chunkSize)
 919                  {
 920                      /* last chunk received */
 921              
 922                      /* Invoke user's callback with header information */
 923                      {
 924                          HttpClient* self = (HttpClient*)handler->base.data;
 925              
 926 krisbash 1.3             if (!(*self->callbackOnResponse)( self, self->callbackData, 0,
 927 mike     1.1                 handler->contentLength, MI_TRUE, 0))
 928                              return PRT_RETURN_FALSE;
 929              
 930                          /* status callback */
 931                          handler->status = MI_RESULT_OK;
 932                          (*self->callbackOnStatus)(
 933 krisbash 1.3                 self,
 934                              self->callbackData,
 935                              MI_RESULT_OK);
 936 mike     1.1         }
 937              
 938                      /* clean up state */
 939                      handler->recvPage = 0;
 940 krisbash 1.3         handler->receivedSize = 0;
 941 mike     1.1         memset(&handler->recvHeaders, 0, sizeof(handler->recvHeaders));
 942                      handler->recvingState = RECV_STATE_HEADER;
 943              
 944                      if (connectionClosed)
 945 krisbash 1.3             return PRT_RETURN_FALSE; /* connection closed */
 946 mike     1.1 
 947                      return PRT_CONTINUE;
 948                  }
 949              
 950                  /* Allocate zero-terminated buffer */
 951 krisbash 1.3     handler->recvPage = (Page*)PAL_Malloc(sizeof(Page) + (size_t)chunkSize + 2 /*CR-LF*/ + 1 /* \0 */);
 952 mike     1.1 
 953                  if (!handler->recvPage)
 954                      return PRT_RETURN_FALSE;
 955              
 956                  ((char*)(handler->recvPage + 1))[chunkSize+2] = 0;
 957              
 958                  handler->recvPage->u.s.size = (unsigned int)chunkSize;
 959                  handler->recvPage->u.s.next = 0;
 960              
 961                  /* subtract header size */
 962 krisbash 1.3     handler->receivedSize -= index + 1;
 963 mike     1.1 
 964                  /* in case of small chunks we may receive more than one chunk already */
 965 krisbash 1.3     if (handler->receivedSize > (size_t)(chunkSize+2))
 966 mike     1.1     {
 967                      /* copy page size to page */
 968 krisbash 1.3         memcpy(handler->recvPage + 1, data, chunkSize+2);
 969 mike     1.1 
 970                      /* notify user */
 971                      {
 972                          HttpClient* self = (HttpClient*)handler->base.data;
 973              
 974 krisbash 1.3             if (!(*self->callbackOnResponse)( self, self->callbackData, 0,
 975 mike     1.1                 handler->contentLength, MI_FALSE, &handler->recvPage))
 976                              return PRT_RETURN_FALSE;
 977              
 978                          if (handler->recvPage)
 979 krisbash 1.3                 PAL_Free(handler->recvPage);
 980 mike     1.1 
 981                          handler->recvPage = 0;
 982                      }
 983              
 984                      /* remove consumed part */
 985 krisbash 1.3         memmove(handler->recvBuffer, data + chunkSize+2, handler->receivedSize - (chunkSize+2));
 986                      handler->receivedSize -= (chunkSize+2);
 987 mike     1.1 
 988                      /* consume next chunk */
 989                      return _ReadChunkHeader(handler);
 990                  }
 991              
 992 krisbash 1.3     memcpy(handler->recvPage + 1, data, handler->receivedSize);
 993 mike     1.1     handler->recvingState = RECV_STATE_CHUNKDATA;
 994              
 995                  if (connectionClosed)
 996 krisbash 1.3         return PRT_RETURN_FALSE; /* connection closed */
 997 mike     1.1 
 998                  return PRT_CONTINUE;
 999              }
1000              static Http_CallbackResult _ReadChunkData(
1001 krisbash 1.3     HttpClient_SR_SocketData* handler)
1002 mike     1.1 {
1003                  //HttpClient* self = (HttpClient*)handler->base.data;
1004                  char* buf;
1005                  size_t buf_size, received;
1006                  MI_Result r;
1007              
1008                  /* are we in the right state? */
1009                  if (handler->recvingState != RECV_STATE_CHUNKDATA)
1010                      return PRT_RETURN_FALSE;
1011              
1012 krisbash 1.3     buf = ((char*)(handler->recvPage + 1)) + handler->receivedSize;
1013                  buf_size = (size_t)(handler->recvPage->u.s.size + 2 /* CR-LF */ - handler->receivedSize);
1014 mike     1.1     received = 0;
1015              
1016                  if (buf_size)
1017                  {
1018                      r = _Sock_Read(handler, buf, buf_size, &received);
1019              
1020 krisbash 1.3         if (r == MI_RESULT_OK && 0 == received)
1021                          return PRT_RETURN_FALSE; /* connection closed */
1022 mike     1.1 
1023 krisbash 1.3         if (r != MI_RESULT_OK && r != MI_RESULT_WOULD_BLOCK)
1024 mike     1.1             return PRT_RETURN_FALSE;
1025              
1026 krisbash 1.3         handler->receivedSize += received;
1027 mike     1.1     }
1028              
1029 krisbash 1.3     if (handler->receivedSize != (size_t)(handler->recvPage->u.s.size + 2 /* CR-LF */))
1030                      return PRT_RETURN_TRUE;
1031 mike     1.1 
1032                  /* Invoke user's callback with header information */
1033                  {
1034                      HttpClient* self = (HttpClient*)handler->base.data;
1035              
1036 krisbash 1.3         if (!(*self->callbackOnResponse)( self, self->callbackData, 0,
1037 mike     1.1             handler->contentLength, MI_FALSE, &handler->recvPage))
1038                          return PRT_RETURN_FALSE;
1039              
1040                  }
1041              
1042                  if (handler->recvPage)
1043 krisbash 1.3         PAL_Free(handler->recvPage);
1044 mike     1.1 
1045                  handler->recvPage = 0;
1046 krisbash 1.3     handler->receivedSize = 0;
1047 mike     1.1     memset(&handler->recvHeaders, 0, sizeof(handler->recvHeaders));
1048                  handler->recvingState = RECV_STATE_CHUNKHEADER;
1049                  return PRT_CONTINUE;
1050              }
1051              
1052              static Http_CallbackResult _WriteHeader(
1053 krisbash 1.3     HttpClient_SR_SocketData* handler)
1054 mike     1.1 {
1055                  char* buf;
1056                  size_t buf_size, sent;
1057                  MI_Result r;
1058              
1059                  /* Do we have any data to send? */
1060                  if (!handler->sendHeader)
1061                      return PRT_RETURN_TRUE;
1062              
1063                  /* are we done with header? */
1064                  if (handler->sendingState == RECV_STATE_CONTENT)
1065                      return PRT_CONTINUE;
1066              
1067 krisbash 1.3     LOGD2((ZT("_WriteHeader - Begin")));
1068              
1069 mike     1.1     buf = ((char*)(handler->sendHeader + 1)) + handler->sentSize;
1070                  buf_size = handler->sendHeader->u.s.size - handler->sentSize;
1071                  sent = 0;
1072              
1073                  r = _Sock_Write(handler, buf, buf_size, &sent);
1074 krisbash 1.3     LOGD2((ZT("_WriteHeader - _Sock_Write result: %d (%s), socket: %d, sent: %d"), (int)r, mistrerror(r), (int)handler->base.sock, (int)sent));
1075 mike     1.1 
1076 krisbash 1.3     if (r == MI_RESULT_OK && 0 == sent)
1077                  {
1078                      LOGE2((ZT("_WriteHeader - Connection closed")));
1079                      return PRT_RETURN_FALSE; /* connection closed */
1080                  }
1081 mike     1.1 
1082 krisbash 1.3     if (r != MI_RESULT_OK && r != MI_RESULT_WOULD_BLOCK)
1083                  {
1084                      LOGE2((ZT("_WriteHeader - _Sock_Write returned error: %d (%s)"), (int)r, mistrerror(r)));
1085 mike     1.1         return PRT_RETURN_FALSE;
1086 krisbash 1.3     }
1087 mike     1.1 
1088                  handler->sentSize += sent;
1089 krisbash 1.3     handler->headVerb = buf_size > 4 && Strncasecmp(buf, "HEAD", 4) == 0;
1090 mike     1.1 
1091 krisbash 1.3     /* did we send all data? */
1092                  if (handler->sentSize != handler->sendHeader->u.s.size)
1093                  {
1094                      LOGD2((ZT("_WriteHeader - Partial write. %u sent this time, %u / %d written, result: %d (%s)"), (unsigned int)sent, (unsigned int)handler->sentSize, (unsigned int)handler->sendHeader->u.s.size, r, mistrerror(r)));
1095                      return PRT_RETURN_TRUE;
1096                  }
1097 mike     1.1 
1098 krisbash 1.3     PAL_Free(handler->sendHeader);
1099 mike     1.1     handler->sendHeader = 0;
1100                  handler->sentSize = 0;
1101                  handler->sendingState = RECV_STATE_CONTENT;
1102              
1103 krisbash 1.3     LOGD2((ZT("_WriteHeader - OK exit")));
1104 mike     1.1     return PRT_CONTINUE;
1105              }
1106              
1107              static Http_CallbackResult _WriteData(
1108 krisbash 1.3     HttpClient_SR_SocketData* handler)
1109 mike     1.1 {
1110                  char* buf;
1111                  size_t buf_size, sent;
1112                  MI_Result r;
1113              
1114 krisbash 1.3     LOGD2((ZT("_WriteData - Begin")));
1115 mike     1.1     /* are we in the right state? */
1116                  if (handler->sendingState != RECV_STATE_CONTENT)
1117 krisbash 1.3     {
1118                      LOGE2((ZT("_WriteData - Wrong state. state: %d"), handler->sendingState));
1119 mike     1.1         return PRT_RETURN_FALSE;
1120 krisbash 1.3     }
1121 mike     1.1 
1122                  if (!handler->sendPage)
1123                  {   /* no content*/
1124                      handler->sentSize = 0;
1125                      handler->sendingState = RECV_STATE_HEADER;
1126                      handler->base.mask &= ~SELECTOR_WRITE;
1127                      handler->base.mask |= SELECTOR_READ;
1128              
1129 krisbash 1.3         LOGW2((ZT("_WriteData - Content is empty. Continuing")));
1130 mike     1.1         return PRT_CONTINUE;
1131                  }
1132              
1133                  buf = ((char*)(handler->sendPage + 1)) + handler->sentSize;
1134                  buf_size = handler->sendPage->u.s.size - handler->sentSize;
1135                  sent = 0;
1136              
1137                  r = _Sock_Write(handler, buf, buf_size, &sent);
1138 krisbash 1.3     LOGD2((ZT("_WriteData - HTTPClient sent %u / %u bytes with result %d (%s)"), (unsigned int)sent, (unsigned int)buf_size, (int)r, mistrerror(r)));
1139 mike     1.1 
1140 krisbash 1.3     if (r == MI_RESULT_OK && 0 == sent)
1141                  {
1142                      LOGE2((ZT("_WriteData exit. Connection closed")));
1143                      return PRT_RETURN_FALSE; /* connection closed */
1144                  }
1145 mike     1.1 
1146 krisbash 1.3     if (r != MI_RESULT_OK && r != MI_RESULT_WOULD_BLOCK)
1147                  {
1148                      LOGE2((ZT("_WriteData exit - Error: %d (%s)"), r, mistrerror(r)));
1149 mike     1.1         return PRT_RETURN_FALSE;
1150 krisbash 1.3     }
1151 mike     1.1 
1152                  handler->sentSize += sent;
1153              
1154 krisbash 1.3     /* did we send all data? */
1155 mike     1.1 
1156 krisbash 1.3     if (handler->sentSize != handler->sendPage->u.s.size)
1157                  {
1158                      LOGD2((ZT("_WriteData - Exit. Partial write. %u / %u bytes written"), (unsigned int)handler->sentSize, (unsigned int)handler->sendPage->u.s.size));
1159 mike     1.1         return PRT_RETURN_TRUE;
1160 krisbash 1.3     }
1161              
1162                  LOGD2((ZT("_WriteData - %u / %u bytes sent"), (unsigned int)handler->sentSize, (unsigned int)handler->sendPage->u.s.size));
1163                  PAL_Free(handler->sendPage);
1164                  handler->sendPage = NULL;
1165 mike     1.1     handler->sentSize = 0;
1166                  handler->sendingState = RECV_STATE_HEADER;
1167                  handler->base.mask &= ~SELECTOR_WRITE;
1168                  handler->base.mask |= SELECTOR_READ;
1169              
1170 krisbash 1.3     LOGD2((ZT("_WriteData - OK exit. returning: %d"), PRT_CONTINUE));
1171              
1172 mike     1.1     return PRT_CONTINUE;
1173              }
1174              
1175              static MI_Boolean _RequestCallbackRead(
1176 krisbash 1.3     HttpClient_SR_SocketData* handler)
1177 mike     1.1 {
1178                  switch (_ReadHeader(handler))
1179                  {
1180                  case PRT_CONTINUE: break;
1181                  case PRT_RETURN_TRUE: return MI_TRUE;
1182                  case PRT_RETURN_FALSE: return MI_FALSE;
1183                  }
1184              
1185                  if (handler->recvingState == RECV_STATE_CONTENT)
1186                  {
1187                      switch (_ReadData(handler))
1188                      {
1189                      case PRT_CONTINUE: break;
1190                      case PRT_RETURN_TRUE: return MI_TRUE;
1191                      case PRT_RETURN_FALSE: return MI_FALSE;
1192                      }
1193                  }
1194              
1195                  if (handler->recvingState == RECV_STATE_CHUNKHEADER)
1196                  {
1197                      switch (_ReadChunkHeader(handler))
1198 mike     1.1         {
1199                      case PRT_CONTINUE: break;
1200                      case PRT_RETURN_TRUE: return MI_TRUE;
1201                      case PRT_RETURN_FALSE: return MI_FALSE;
1202                      }
1203                  }
1204                  if (handler->recvingState == RECV_STATE_CHUNKDATA)
1205                  {
1206                      switch (_ReadChunkData(handler))
1207                      {
1208                      case PRT_CONTINUE: break;
1209                      case PRT_RETURN_TRUE: return MI_TRUE;
1210                      case PRT_RETURN_FALSE: return MI_FALSE;
1211                      }
1212                  }
1213              
1214                  return MI_TRUE;
1215              }
1216              
1217              static MI_Boolean _RequestCallbackWrite(
1218 krisbash 1.3     HttpClient_SR_SocketData* handler)
1219 mike     1.1 {
1220                  switch (_WriteHeader(handler))
1221                  {
1222                  case PRT_CONTINUE: break;
1223                  case PRT_RETURN_TRUE: return MI_TRUE;
1224                  case PRT_RETURN_FALSE: return MI_FALSE;
1225                  }
1226              
1227                  switch (_WriteData(handler))
1228                  {
1229                  case PRT_CONTINUE: break;
1230                  case PRT_RETURN_TRUE: return MI_TRUE;
1231                  case PRT_RETURN_FALSE: return MI_FALSE;
1232                  }
1233                  return MI_TRUE;
1234              }
1235              
1236              
1237              static MI_Boolean _RequestCallback(
1238                  Selector* sel,
1239                  Handler* handlerIn,
1240 krisbash 1.3     MI_Uint32 mask,
1241 mike     1.1     MI_Uint64 currentTimeUsec)
1242              {
1243 krisbash 1.3     HttpClient_SR_SocketData* handler = (HttpClient_SR_SocketData*)handlerIn;
1244                  MI_UNUSED(sel);
1245 mike     1.1 
1246 krisbash 1.3     if (((mask & SELECTOR_READ) != 0 && !handler->reverseOperations) ||
1247                      ((mask & SELECTOR_WRITE) != 0 && handler->reverseOperations))
1248 mike     1.1     {
1249                      if (!_RequestCallbackRead(handler))
1250 krisbash 1.3         {
1251                          LOGE2((ZT("_RequestCallback - RequestCallbackRead failed")));
1252 mike     1.1             return MI_FALSE;
1253 krisbash 1.3         }
1254                      LOGD2((ZT("_RequestCallback - Called _RequestCallbackRead. %u / %u bytes read"), (unsigned int)handler->receivedSize, handler->recvPage == NULL ? 0 : (unsigned int)handler->recvPage->u.s.size));
1255 mike     1.1     }
1256              
1257 krisbash 1.3     if (((mask & SELECTOR_WRITE) != 0 && !handler->reverseOperations) ||
1258                      ((mask & SELECTOR_READ) != 0 && handler->reverseOperations))
1259 mike     1.1     {
1260                      if (!_RequestCallbackWrite(handler))
1261 krisbash 1.3         {
1262                          LOGE2((ZT("_RequestCallback - _RequestCallbackWrite failed")));
1263 mike     1.1             return MI_FALSE;
1264 krisbash 1.3         }
1265                      LOGD2((ZT("_RequestCallback - Called _RequestCallbackWrite. %u / %u bytes sent"), (unsigned int)handler->sentSize, handler->sendPage == NULL ? 0 : (unsigned int)handler->sendPage->u.s.size));
1266                      while (handler->sendPage != NULL && handler->sentSize < handler->sendPage->u.s.size)
1267                      {                               /* assume 500 bytes per millisecond transmission */
1268                                                      /* wait after to avoid spinning too much on _WriteData */
1269                          unsigned int bytesLeft = (unsigned int)handler->sendPage->u.s.size - (unsigned int)handler->sentSize;
1270                          unsigned long msec = (unsigned long)(bytesLeft / 500 + 1);
1271              
1272                          LOGD2((ZT("_RequestCallback - Called _WriteData. %u / %u bytes sent"), (unsigned int)handler->sentSize, handler->sendPage == NULL ? 0 : (unsigned int)handler->sendPage->u.s.size));
1273                          if (_WriteData(handler) == MI_FALSE)
1274                          {
1275                              LOGE2((ZT("_RequestCallback - _WriteData failed")));
1276                              return MI_FALSE;
1277                          }
1278                          LOGD2((ZT("_RequestCallback - Called _WriteData. %u bytes written, %u bytes left"), (unsigned int)handler->sentSize, handler->sendPage == NULL ? 0 : (unsigned int)handler->sendPage->u.s.size));
1279                          Sleep_Milliseconds(msec);
1280                      }
1281                      LOGD2((ZT("_RequestCallback - Called _RequestCallbackWrite. %u / %u bytes sent"), (unsigned int)handler->sentSize, handler->sendPage == NULL ? 0 : (unsigned int)handler->sendPage->u.s.size));
1282 mike     1.1     }
1283                  
1284                  /* re-set timeout - if we performed R/W operation, set timeout depending where we are in communication */
1285                  if (mask & (SELECTOR_READ | SELECTOR_WRITE))
1286                  {
1287                      handler->base.fireTimeoutAt = currentTimeUsec + handler->timeoutUsec;
1288                  }
1289              
1290 krisbash 1.3     /* Close connection by timeout */
1291                  if ((mask & SELECTOR_TIMEOUT) != 0)
1292 mike     1.1     {
1293 krisbash 1.3         if (handler->status != MI_RESULT_OK)
1294                      {
1295                          handler->recvHeaders.httpError = 408;
1296 mike     1.1             handler->status = MI_RESULT_TIME_OUT;
1297 krisbash 1.3         }
1298                      LOGE2((ZT("_RequestCallback - Timed out. socket: %d, result: %d (%s)"), handler->base.sock, handler->status, mistrerror(handler->status)));
1299 mike     1.1         return MI_FALSE;
1300                  }
1301              
1302 krisbash 1.3     if ((mask & SELECTOR_REMOVE) != 0 || (mask & SELECTOR_DESTROY) != 0)
1303 mike     1.1     {
1304                      HttpClient* self = (HttpClient*)handler->base.data;
1305              
1306                      /* notify next stack layer */
1307 krisbash 1.3         if (handler->status != MI_RESULT_OK)
1308                          (*self->callbackOnStatus)(self, self->callbackData, handler->status);
1309              
1310                      /* Yeah, this is hokey, but we need to sleep here to let the */
1311              		/* subsystems have the opportunity to send the data before we close */
1312              		/* the socket, or we'll get a broken pipe/connection reset */
1313              #if defined(CONFIG_OS_WINDOWS)
1314                      Sleep_Milliseconds(1);
1315              #else
1316                      usleep(50);
1317              #endif
1318              
1319                      if (handler == NULL)
1320                      {
1321                          LOGE2((ZT("_RequestCallback - The handler object was free'd under us!")));
1322                          return MI_TRUE;
1323                      }
1324 mike     1.1 
1325 krisbash 1.3         self->connector = NULL;
1326 mike     1.1 
1327                      if (handler->ssl)
1328                          SSL_free(handler->ssl);
1329              
1330                      Sock_Close(handler->base.sock);
1331              
1332                      if (handler->recvPage)
1333 krisbash 1.3             PAL_Free(handler->recvPage);
1334 mike     1.1 
1335                      if (handler->sendPage)
1336 krisbash 1.3             PAL_Free(handler->sendPage);
1337 mike     1.1 
1338                      if (handler->sendHeader)
1339 krisbash 1.3             PAL_Free(handler->sendHeader);
1340 mike     1.1 
1341 krisbash 1.3         PAL_Free(handler->recvBuffer);
1342                      PAL_Free(handler);
1343 mike     1.1     }
1344              
1345                  return MI_TRUE;
1346              }
1347              
1348              #ifdef CONFIG_POSIX
1349 krisbash 1.3 
1350              /*
1351               Verify callback when the server authentication certificate's chain of trust is checked. This
1352               is the same as the Open SSL verify callback function (return preverify_ok), except that it
1353               logs a diagnostic message when preverify_ok has a failed status.
1354              */
1355              static int _ctxVerify(
1356                  int preverify_ok,
1357                  X509_STORE_CTX* ctx)
1358 mike     1.1 {
1359 krisbash 1.3     if (preverify_ok <= 0)
1360                  {
1361                      X509* certWithError = X509_STORE_CTX_get_current_cert(ctx);
1362                      int error = X509_STORE_CTX_get_error(ctx);
1363                      char nameBuf[256];
1364 mike     1.1 
1365 krisbash 1.3         X509_NAME_oneline(X509_get_subject_name(certWithError), nameBuf, 256);
1366                      trace_SSL_VerifyFailed(error, nameBuf);
1367                  }
1368                  return preverify_ok;
1369              }
1370 mike     1.1 
1371 krisbash 1.3 /*
1372               Create an Open SSL context that will be used for secure communication. Set up server and client
1373               certificate authentication if specified.
1374              */
1375              static MI_Result _CreateSSLContext(
1376                  HttpClient* self,
1377                  const char* trustedCertsDir,
1378                  const char* certFile,
1379                  const char* privateKeyFile)
1380              {
1381                  SSL_CTX* sslContext = SSL_CTX_new(SSLv23_method());
1382                  if (sslContext == NULL)
1383 mike     1.1     {
1384 krisbash 1.3         LOGE2((ZT("_CreateSSLContext - Cannot create SSL context")));
1385                      trace_SSL_CannotCreateContext();
1386 mike     1.1         return MI_RESULT_FAILED;
1387                  }
1388                  SSL_CTX_set_quiet_shutdown(sslContext, 1);
1389 krisbash 1.3     (void)SSL_CTX_set_mode(sslContext, SSL_MODE_AUTO_RETRY | SSL_MODE_ENABLE_PARTIAL_WRITE);
1390 mike     1.1     SSL_CTX_set_session_cache_mode(sslContext, SSL_SESS_CACHE_OFF);
1391 krisbash 1.3     SSL_CTX_sess_set_remove_cb(sslContext, NULL);
1392              
1393                  if (trustedCertsDir != NULL)
1394 mike     1.1     {
1395 krisbash 1.3         /* Cause Open SSL to check the server certificate Subject against its FQDN and
1396                      ** to check the server certificate chain against the contents of *trustedCertsDir.
1397                      */
1398                      if (SSL_CTX_load_verify_locations(sslContext, NULL, trustedCertsDir) < 0)
1399 mike     1.1         {
1400 krisbash 1.3             LOGE2((ZT("_CreateSSLContext - Cannot set directory containing trusted certificate(s) to %s"), trustedCertsDir));
1401                          trace_SSL_BadTrustDir(trustedCertsDir);
1402 mike     1.1         }
1403 krisbash 1.3         SSL_CTX_set_verify(sslContext, SSL_VERIFY_PEER, _ctxVerify);
1404 mike     1.1     }
1405              
1406 krisbash 1.3     /* Check if there is a client certificate file (file containing client authentication
1407                  ** certificate) specified. If specified, validate and load the certificate.
1408 mike     1.1     */
1409 krisbash 1.3     if (certFile != NULL && *certFile != '\0')
1410 mike     1.1     {
1411 krisbash 1.3         int err;
1412              
1413                      /* load the specified client certificates */
1414                      LOGD2((ZT("_CreateSSLContext - Loading server certificate from: %s"), certFile));
1415              
1416                      err = SSL_CTX_use_certificate_file(sslContext,
1417                                                         certFile,
1418                                                         strcmp(certFile + strlen(certFile) - 4, ".pem") == 0 ? SSL_FILETYPE_PEM :  SSL_FILETYPE_ASN1);
1419                      if (err <= 0)
1420                      {
1421              #if defined(ENABLE_TRACING)
1422                          unsigned long error = ERR_peek_last_error();
1423              #endif
1424              
1425                          LOGE2((ZT("_CreateSSLContext - No client certificate found in %s"), certFile));
1426                          LOGE2((ZT("_CreateSSLContext - OpenSSL Error 0x%lX (%s) in SSL_CTX_use_certificate_file"), error, sslstrerror(error)));
1427 mike     1.1             SSL_CTX_free(sslContext);
1428 krisbash 1.3             sslContext = NULL;
1429 mike     1.1             return MI_RESULT_FAILED;
1430                      }
1431 krisbash 1.3 
1432                      if (privateKeyFile != NULL && *privateKeyFile != '\0')
1433                      {
1434                          /* load the specified private key */
1435                          LOGD2((ZT("_CreateSSLContext - SSL Loading client private key from: %s"), privateKeyFile));
1436              
1437                          err = SSL_CTX_use_RSAPrivateKey_file(sslContext,
1438                                                               privateKeyFile,
1439                                                               strcmp(privateKeyFile + strlen(privateKeyFile) - 4, ".pem") == 0 ? SSL_FILETYPE_PEM :  SSL_FILETYPE_ASN1);
1440                          if (err <= 0)
1441                          {
1442              #if defined(ENABLE_TRACING)
1443                              unsigned long error = ERR_peek_last_error();
1444              #endif
1445                              LOGE2((ZT("_CreateSSLContext - Invalid private key found in %s"), privateKeyFile));
1446                              LOGE2((ZT("_CreateSSLContext - OpenSSL error 0x%lX (%s) in SSL_CTX_use_PrivateKey_file"), error, sslstrerror(error)));
1447                              SSL_CTX_free(sslContext);
1448                              sslContext = NULL;
1449                              return MI_RESULT_FAILED;
1450                          }
1451                      }
1452 mike     1.1     }
1453 krisbash 1.3 
1454 mike     1.1     self->sslContext = sslContext;
1455                  return MI_RESULT_OK;
1456              }
1457 krisbash 1.3 
1458 mike     1.1 #endif
1459              
1460 krisbash 1.3 static MI_Result _CreateSocketAndConnect(
1461                  Sock* s,
1462                  Addr* addr)
1463              {
1464                  MI_Result r;
1465              
1466                  LOGD2((ZT("_CreateSocketAndConnect - Begin")));
1467              
1468                  /* Create client socket. */
1469                  r = Sock_Create(s, addr->is_ipv6);
1470                  if (r != MI_RESULT_OK)
1471                  {
1472                      LOGE2((ZT("_CreateSocketAndConnect - Sock_Create failed. result: %d (%s)"), r, mistrerror(r)));
1473                      return r;
1474                  }
1475              
1476                  /* set the socket to be non-blocking */
1477                  r = Sock_SetBlocking(*s, MI_FALSE);
1478                  if (r != MI_RESULT_OK)
1479                  {
1480                      LOGE2((ZT("_CreateSocketAndConnect - Sock_SetBlocking failed. result: %d (%s)"), r, mistrerror(r)));
1481 krisbash 1.3         return r;
1482                  }
1483              
1484                  /* connect the socket to the IP address */
1485                  r = Sock_Connect(*s, addr);
1486                  if (r != MI_RESULT_OK)
1487                  {
1488                      LOGE2((ZT("_CreateSocketAndConnect - Sock_Connect failed. result: %d (%s)"), r, mistrerror(r)));
1489                      return r;
1490                  }
1491              
1492                  LOGD2((ZT("_CreateSocketAndConnect - OK exit")));
1493              
1494                  return MI_RESULT_WOULD_BLOCK;
1495              }
1496              
1497 mike     1.1 static MI_Result _CreateConnectorSocket(
1498                  HttpClient* self,
1499                  const char* host,
1500                  unsigned short port,
1501                  MI_Boolean secure)
1502              {
1503                  Addr addr;
1504                  MI_Result r;
1505                  Sock s;
1506 krisbash 1.3     HttpClient_SR_SocketData* h;
1507 mike     1.1     MI_Uint64 currentTimeUsec;
1508              
1509 krisbash 1.3     LOGD2((ZT("_CreateConnectorSocket - Begin. host: %s, port: %d, secure? %d"), host, port, secure));
1510              
1511 mike     1.1     /* timeout calculation */
1512 krisbash 1.3     if (PAL_Time(&currentTimeUsec) != PAL_TRUE)
1513 mike     1.1     {
1514 krisbash 1.3         LOGE2((ZT("_CreateConnectorSocket - PAL_Time failed")));
1515 mike     1.1         return MI_RESULT_FAILED;
1516                  }
1517              
1518 krisbash 1.3     /* This code tries to connect using the preferred addressing family (IPv4 */
1519              	/* or IPv6). If that fails and Addr_Init has a secondary addressing */
1520              	/* family, a connection with the secondary family, it tries using the */
1521              	/* secondary family next. */
1522              
1523                  /* Initialize preferred address */
1524                  r = Addr_Init(&addr, host, port, MI_FALSE);
1525 mike     1.1     if (r != MI_RESULT_OK)
1526                  {
1527 krisbash 1.3         LOGE2((ZT("_CreateConnectorSocket - Addr_Init failed. result: %d (%s)"), r, mistrerror(r)));
1528                      return r;
1529 mike     1.1     }
1530              
1531 krisbash 1.3     /* Connect to the server */
1532                  r = _CreateSocketAndConnect(&s, &addr);
1533 mike     1.1     if (r != MI_RESULT_OK && r != MI_RESULT_WOULD_BLOCK)
1534                  {
1535 krisbash 1.3         MI_Result r2;
1536              
1537 mike     1.1         Sock_Close(s);
1538 krisbash 1.3 
1539                      LOGW2((ZT("_CreateConnectorSocket - _CreateSocketAndConnect of primary address failed. result: %d (%s)"), r, mistrerror(r)));
1540              
1541                      /* Initialize secondary address */
1542                      r2 = Addr_Init(&addr, host, port, MI_TRUE);
1543                      if (r2 != MI_RESULT_OK)
1544                          return r;                   /* on error, return original failure */
1545                      r2 = _CreateSocketAndConnect(&s, &addr);
1546                      if (r2 != MI_RESULT_OK && r2 != MI_RESULT_WOULD_BLOCK)
1547                      {
1548                          Sock_Close(s);
1549              
1550                          LOGE2((ZT("_CreateConnectorSocket - Addr_Init failed. result: %d (%s)"), r, mistrerror(r)));
1551               
1552                          return r;                   /* on error, return original failure */
1553                      }
1554                      r = r2;
1555 mike     1.1     }
1556              
1557                  /* Create handler */
1558 krisbash 1.3     h = (HttpClient_SR_SocketData*)PAL_Calloc(1, sizeof(HttpClient_SR_SocketData));
1559 mike     1.1 
1560 krisbash 1.3     if (h == NULL)
1561 mike     1.1     {
1562                      Sock_Close(s);
1563 krisbash 1.3         LOGE2((ZT("_CreateConnectorSocket - calloc failed")));
1564 mike     1.1         return MI_RESULT_FAILED;
1565                  }
1566              
1567                  h->recvBufferSize = INITIAL_BUFFER_SIZE;
1568 krisbash 1.3     h->recvBuffer = (char*)PAL_Calloc(1, h->recvBufferSize);
1569 mike     1.1     if (!h->recvBuffer)
1570                  {
1571 krisbash 1.3         PAL_Free(h);
1572 mike     1.1         Sock_Close(s);
1573 krisbash 1.3         LOGE2((ZT("_CreateConnectorSocket - calloc failed")));
1574 mike     1.1         return MI_RESULT_FAILED;
1575                  }
1576              
1577                  h->base.sock = s;
1578                  h->base.mask = SELECTOR_EXCEPTION;
1579                  h->base.callback = _RequestCallback;
1580                  h->base.data = self;
1581                  h->timeoutUsec = DEFAULT_HTTP_TIMEOUT_USEC;
1582                  h->base.fireTimeoutAt = currentTimeUsec + h->timeoutUsec;
1583              
1584 krisbash 1.3     /* SSL support */
1585 mike     1.1     if (secure)
1586                  {
1587                      h->ssl = SSL_new(self->sslContext);
1588              
1589                      if (!h->ssl)
1590                      {
1591 krisbash 1.3             LOGW2((ZT("_CreateConnectorSocket - SSL_new failed")));
1592                          trace_SSLNew_Failed();
1593                          PAL_Free(h);
1594 mike     1.1             Sock_Close(s);
1595                          return MI_RESULT_FAILED;
1596                      }
1597              
1598 krisbash 1.3         Sock_SetBlocking(s, MI_TRUE);
1599 mike     1.1         if (!(SSL_set_fd(h->ssl, s) ))
1600                      {
1601 krisbash 1.3             LOGW2((ZT("_CreateConnectorSocket - SSL_set_fd failed")));
1602                          trace_SSL_setfd_Failed();
1603 mike     1.1             SSL_free(h->ssl);
1604 krisbash 1.3             PAL_Free(h);
1605 mike     1.1             Sock_Close(s);
1606                          return MI_RESULT_FAILED;
1607                      }
1608              
1609                      SSL_set_connect_state(h->ssl);
1610                  }
1611              
1612                  /* Watch for read events on the incoming connection */
1613                  r = Selector_AddHandler(self->selector, &h->base);
1614              
1615                  if (r != MI_RESULT_OK)
1616                  {
1617 krisbash 1.3         LOGE2((ZT("_CreateConnectorSocket - Selector_AddHandler failed with error: %d (%s)"), (int)r, mistrerror(r)));
1618                      trace_SelectorAddHandler_Failed();
1619 mike     1.1         if (secure)
1620                          SSL_free(h->ssl);
1621 krisbash 1.3         PAL_Free(h);
1622 mike     1.1         Sock_Close(s);
1623                      return MI_RESULT_FAILED;
1624                  }
1625              
1626                  self->connector = h;
1627              
1628 krisbash 1.3     LOGD2((ZT("_CreateConnectorSocket - OK exit. socket: %d, secure: %d, timeout: %s"), h->base.sock, secure, FmtInterval(h->base.fireTimeoutAt - currentTimeUsec)));
1629              
1630 mike     1.1     return MI_RESULT_OK;
1631              }
1632              
1633              static MI_Result _New_Http(
1634                  HttpClient** selfOut,
1635                  Selector* selector, /*optional, maybe NULL*/
1636                  HttpClientCallbackOnStatus statusCallback,
1637                  HttpClientCallbackOnResponse  responseCallback,
1638                  void* callbackData)
1639              {
1640                  HttpClient* self;
1641              
1642                  /* Check parameters */
1643                  if (!selfOut)
1644                      return MI_RESULT_INVALID_PARAMETER;
1645              
1646                  /* Clear output parameter */
1647                  *selfOut = NULL;
1648              
1649                  /* Allocate structure */
1650                  {
1651 krisbash 1.3         self = (HttpClient*)PAL_Calloc(1, sizeof(HttpClient));
1652 mike     1.1 
1653                      if (!self)
1654                          return MI_RESULT_FAILED;
1655                  }
1656              
1657                  if (selector)
1658                  {   /* attach the exisiting selector */
1659                      self->selector = selector;
1660                      self->internalSelectorUsed = MI_FALSE;
1661                  }
1662                  else
1663                  {   /* creaet a new selector */
1664                      /* Initialize the network */
1665                      Sock_Start();
1666              
1667                      /* Initialize the selector */
1668                      if (Selector_Init(&self->internalSelector) != MI_RESULT_OK)
1669                      {
1670 krisbash 1.3             PAL_Free(self);
1671                          LOGE2((ZT("_NewHttp - Selector_Init failed")));
1672 mike     1.1             return MI_RESULT_FAILED;
1673                      }
1674                      self->selector = &self->internalSelector;
1675                      self->internalSelectorUsed = MI_TRUE;
1676                  }
1677              
1678                  /* Save the callback and callbackData */
1679                  self->callbackOnResponse = responseCallback;
1680                  self->callbackOnStatus = statusCallback;
1681                  self->callbackData = callbackData;
1682              
1683                  /* Set the magic number */
1684                  self->magic = _MAGIC;
1685              
1686                  /* Set output parameter */
1687                  *selfOut = self;
1688                  return MI_RESULT_OK;
1689              }
1690              
1691              
1692              
1693 mike     1.1 static size_t _GetHeadersSize(
1694                  const HttpClientRequestHeaders* headers)
1695              {
1696                  size_t res = 0;
1697                  size_t index = 0;
1698              
1699                  while (index < headers->size)
1700                  {
1701                      res += Strlen(headers->data[index]);
1702                      res += 2; /* \r \n pair */
1703                      index++;
1704                  }
1705              
1706                  return res;
1707              }
1708              
1709              static Page* _CreateHttpHeader(
1710                  const char* verb,
1711                  const char* uri,
1712                  const HttpClientRequestHeaders* headers,
1713                  size_t size)
1714 mike     1.1 {
1715                  Page* page = 0;
1716                  size_t pageSize = 0;
1717                  int r;
1718                  char* p;
1719              
1720              #define HTTP_HEADER_FORMAT "%s %s HTTP/1.1\r\n" \
1721                  "Content-Length: %d\r\n"\
1722                  "Connection: Keep-Alive\r\n" \
1723                  "Host: host\r\n"
1724              
1725              #define HTTP_HEADER_FORMAT_NOCL "%s %s HTTP/1.1\r\n" \
1726                  "Connection: Keep-Alive\r\n" \
1727                  "Host: host\r\n"
1728              
1729                  /* calculate approximate page size */
1730                  if (!verb)
1731                      verb = "POST";
1732                  pageSize += sizeof(HTTP_HEADER_FORMAT) + 10; /* format + 10 digits of content length */
1733                  pageSize += Strlen(verb);
1734                  pageSize += Strlen(uri);
1735 mike     1.1 
1736                  if (headers)
1737                      pageSize += _GetHeadersSize(headers);
1738              
1739 krisbash 1.3     page = (Page*)PAL_Malloc(pageSize + sizeof(Page));
1740 mike     1.1 
1741                  if (!page)
1742                      return 0;
1743              
1744                  /* clear header */
1745                  memset(page, 0, sizeof(Page));
1746              
1747                  p = (char*)(page + 1);
1748              
1749                  if (size)
1750                      r = Snprintf(p, pageSize, HTTP_HEADER_FORMAT, verb, uri, (int)size);
1751                  else
1752                      r = Snprintf(p, pageSize, HTTP_HEADER_FORMAT_NOCL, verb, uri);
1753              
1754                  if (r < 0)
1755                  {
1756 krisbash 1.3         PAL_Free(page);
1757 mike     1.1         return 0;
1758                  }
1759              
1760                  p += r;
1761                  pageSize -= r;
1762              
1763                  if (headers)
1764                  {
1765                      size_t index = 0;
1766              
1767                      while (index < headers->size)
1768                      {
1769                          r = (int)Strlcpy(p,headers->data[index], pageSize);
1770                          p += r;
1771                          pageSize -= r;
1772                          r = (int)Strlcpy(p,"\r\n", pageSize);
1773                          p += r;
1774                          pageSize -= r;
1775              
1776                          index++;
1777                      }
1778 mike     1.1     }
1779              
1780                  /* add trailing \r\n */
1781                  r = (int)Strlcpy(p,"\r\n", pageSize);
1782                  p += r;
1783                  pageSize -= r;
1784              
1785                  page->u.s.size = (unsigned int)(p - (char*)(page+1));
1786              
1787                  return page;
1788              }
1789              
1790              /* ************************************************************************* *\
1791                                              HTTP CLIENT
1792              \* ************************************************************************* */
1793              /*
1794                  Creates new http client.
1795              
1796                  Parameters:
1797                  selfOut - [out] newly created http object (or null if failed).
1798                  selector - [opt] selector to use for socket monitoring. If selector not specified,
1799 mike     1.1             private one is created.
1800                  host - host address
1801                  port - port number
1802                  secure - flag that indicates if http or https conneciton is required
1803              
1804                  Returns:
1805                  'OK' on success or error code otherwise
1806              */
1807              MI_Result HttpClient_New_Connector(
1808                  HttpClient** selfOut,
1809                  Selector* selector, /*optional, maybe NULL*/
1810                  const char* host,
1811                  unsigned short port,
1812                  MI_Boolean secure,
1813                  HttpClientCallbackOnStatus statusCallback,
1814                  HttpClientCallbackOnResponse  responseCallback,
1815 krisbash 1.3     void* callbackData,
1816                  const char* trustedCertsDir,
1817                  const char* certFile,
1818                  const char* privateKeyFile)
1819 mike     1.1 {
1820                  HttpClient* self;
1821                  MI_Result r;
1822              
1823                  /* allocate this, inits selector */
1824 krisbash 1.3     r = _New_Http(selfOut, selector, statusCallback,
1825                                responseCallback, callbackData);
1826 mike     1.1 
1827                  if (MI_RESULT_OK != r)
1828 krisbash 1.3     {
1829                      LOGE2((ZT("HttpClient_New_Connector - _New_Http failed. result: %d (%s)"), r, mistrerror(r)));
1830 mike     1.1         return r;
1831 krisbash 1.3     }
1832 mike     1.1     self = *selfOut;
1833              
1834              #ifdef CONFIG_POSIX
1835 krisbash 1.3     /* Allocate SSL context */
1836 mike     1.1     if (secure)
1837                  {
1838                      /* init ssl */
1839                      SSL_library_init();
1840              
1841                      /* create context */
1842 krisbash 1.3         r = _CreateSSLContext(self, trustedCertsDir, certFile, privateKeyFile);
1843 mike     1.1 
1844                      if (r != MI_RESULT_OK)
1845                      {
1846                          HttpClient_Delete(self);
1847 krisbash 1.3             *selfOut = NULL;
1848                          LOGE2((ZT("HttpClient_New_Connector - _CreateSSLContext failed. result: %d (%s)"), r, mistrerror(r)));
1849 mike     1.1             return r;
1850                      }
1851                  }
1852              #else
1853                  MI_UNUSED(secure);
1854              #endif
1855              
1856                  /* Create http connector socket */
1857                  {
1858                      r = _CreateConnectorSocket(self, host, port, secure);
1859              
1860                      if (r != MI_RESULT_OK)
1861                      {
1862                          HttpClient_Delete(self);
1863 krisbash 1.3             LOGE2((ZT("HttpClient_New_Connector - _CreateConnectorSocket failed failed. result: %d (%s)"), r, mistrerror(r)));
1864 mike     1.1             return r;
1865                      }
1866                  }
1867              
1868                  return MI_RESULT_OK;
1869              }
1870              
1871              /*
1872                  Deletes http object, disconnects form the server
1873                  and frees all related resources.
1874 krisbash 1.3 
1875 mike     1.1     Parameters:
1876                  self - http object
1877              
1878                  Returns:
1879                  OK
1880              */
1881              MI_Result HttpClient_Delete(
1882                  HttpClient* self)
1883              {
1884                  /* Check parameters */
1885                  if (!self)
1886                      return MI_RESULT_INVALID_PARAMETER;
1887              
1888                  /* Check magic number */
1889                  if (self->magic != _MAGIC)
1890 krisbash 1.3     {
1891                      LOGE2((ZT("HttpClient_Delete - Bad magic number")));
1892 mike     1.1         return MI_RESULT_INVALID_PARAMETER;
1893 krisbash 1.3     }
1894 mike     1.1 
1895                  if (self->internalSelectorUsed)
1896                  {
1897                      /* Release selector;
1898 krisbash 1.3         Note: Selector_Destroy closes all sockets in a list including connector and listener */
1899 mike     1.1         Selector_Destroy(self->selector);
1900              
1901                      /* Shutdown the network */
1902                      Sock_Stop();
1903                  }
1904                  else
1905                  {
1906                      /* remove connector from handler */
1907                      if (self->connector)
1908                          Selector_RemoveHandler(self->selector, &self->connector->base);
1909                  }
1910              
1911                  if (self->sslContext)
1912                      SSL_CTX_free(self->sslContext);
1913              
1914                  /* Clear magic number */
1915                  self->magic = 0xDDDDDDDD;
1916              
1917                  /* Free self pointer */
1918 krisbash 1.3     PAL_Free(self);
1919 mike     1.1 
1920                  return MI_RESULT_OK;
1921              }
1922              
1923              
1924 krisbash 1.3 /*
1925 mike     1.1     Sends http request.
1926              
1927                  Parameters:
1928                  self - http object
1929                  uri - request's URI
1930 krisbash 1.3     headers - [opt] extra headers for request.
1931                  data - [opt] content to send. if message is accepted to be sent,
1932 mike     1.1         on return *data == null (taking memory ownership)
1933              
1934                  Returns:
1935                  OK or appropriate error
1936               */
1937              MI_Result HttpClient_StartRequest(
1938                  HttpClient* self,
1939                  const char* verb,
1940                  const char* uri,
1941                  const HttpClientRequestHeaders* headers,
1942                  Page** data)
1943              {
1944 krisbash 1.3     LOGD2((ZT("HttpClient_StartRequest - Begin. verb: %s, URI: %s"), verb, uri));
1945 mike     1.1 
1946                  /* check params */
1947                  if (!self || !uri)
1948                      return MI_RESULT_INVALID_PARAMETER;
1949              
1950                  if (self->magic != _MAGIC)
1951                  {
1952 krisbash 1.3         LOGE2((ZT("HttpClient_Delete - Bad magic number")));
1953                      trace_StartRequest_InvalidMagic();
1954 mike     1.1         return MI_RESULT_INVALID_PARAMETER;
1955                  }
1956              
1957 krisbash 1.3     if (self->connector == NULL)
1958 mike     1.1     {
1959 krisbash 1.3         LOGE2((ZT("HttpClient_Delete - Connection is not open")));
1960                      trace_StartRequest_ConnectionClosed();
1961 mike     1.1         return MI_RESULT_FAILED;
1962                  }
1963              
1964                  /* create header page */
1965                  self->connector->sendHeader =
1966                      _CreateHttpHeader(verb, uri, headers, (data && *data) ? (*data)->u.s.size : 0);
1967 krisbash 1.3 
1968                  if (data != NULL)
1969 mike     1.1     {
1970                      self->connector->sendPage = *data;
1971                      *data = 0;
1972                  }
1973                  else
1974 krisbash 1.3         self->connector->sendPage = NULL;
1975 mike     1.1 
1976                  /* set status to failed, until we know more details */
1977                  self->connector->status = MI_RESULT_FAILED;
1978                  self->connector->sentSize = 0;
1979                  self->connector->sendingState = RECV_STATE_HEADER;
1980                  self->connector->base.mask |= SELECTOR_WRITE;
1981              
1982                  _RequestCallbackWrite(self->connector);
1983              
1984                  return MI_RESULT_OK;
1985              }
1986              
1987              MI_Result HttpClient_Run(
1988                  HttpClient* self,
1989                  MI_Uint64 timeoutUsec)
1990              {
1991                  /* Run the selector */
1992 krisbash 1.3     return Selector_Run(self->selector, timeoutUsec, MI_FALSE);
1993 mike     1.1 }
1994              
1995              MI_Result HttpClient_SetTimeout(
1996                  HttpClient* self,
1997                  MI_Uint64 timeoutUsec)
1998              {
1999                  MI_Uint64 currentTimeUsec = 0;
2000              
2001 krisbash 1.3     PAL_Time(&currentTimeUsec);
2002 mike     1.1 
2003                  /* check params */
2004                  if (!self)
2005                      return MI_RESULT_INVALID_PARAMETER;
2006              
2007                  if (self->magic != _MAGIC)
2008                  {
2009 krisbash 1.3         trace_Timeout_InvalidMagic();
2010 mike     1.1         return MI_RESULT_INVALID_PARAMETER;
2011                  }
2012              
2013                  if (!self->connector)
2014                      return MI_RESULT_INVALID_PARAMETER;
2015              
2016                  /* create header page */
2017                  self->connector->timeoutUsec = timeoutUsec;
2018                  self->connector->base.fireTimeoutAt = currentTimeUsec + self->connector->timeoutUsec;
2019              
2020                  return MI_RESULT_OK;
2021              }

ViewCVS 0.9.2