andre@0: /* This Source Code Form is subject to the terms of the Mozilla Public
andre@0:  * License, v. 2.0. If a copy of the MPL was not distributed with this
andre@0:  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
andre@0: /*
andre@0:  * pkix_pl_httpdefaultclient.c
andre@0:  *
andre@0:  * HTTPDefaultClient Function Definitions
andre@0:  *
andre@0:  */
andre@0: 
andre@0: #include "pkix_pl_httpdefaultclient.h"
andre@0: 
andre@0: static void *plContext = NULL;
andre@0: 
andre@0: /*
andre@0:  * The interface specification for an http client requires that it register
andre@0:  * a function table of type SEC_HttpClientFcn, which is defined as a union
andre@0:  * of tables, of which only version 1 is defined at present.
andre@0:  *
andre@0:  * Note: these functions violate the PKIX calling conventions, in that they
andre@0:  * return SECStatus rather than PKIX_Error*, and that they do not provide a
andre@0:  * plContext argument. They are implemented here as calls to PKIX functions,
andre@0:  * but the plContext value is circularly defined - a true kludge. Its value
andre@0:  * is saved at the time of the call to pkix_pl_HttpDefaultClient_Create for
andre@0:  * subsequent use, but since that initial call comes from the
andre@0:  * pkix_pl_HttpDefaultClient_CreateSessionFcn, it's not really getting saved.
andre@0:  */
andre@0: static SEC_HttpClientFcnV1 vtable = {
andre@0:         pkix_pl_HttpDefaultClient_CreateSessionFcn,
andre@0:         pkix_pl_HttpDefaultClient_KeepAliveSessionFcn,
andre@0:         pkix_pl_HttpDefaultClient_FreeSessionFcn,
andre@0:         pkix_pl_HttpDefaultClient_RequestCreateFcn,
andre@0:         pkix_pl_HttpDefaultClient_SetPostDataFcn,
andre@0:         pkix_pl_HttpDefaultClient_AddHeaderFcn,
andre@0:         pkix_pl_HttpDefaultClient_TrySendAndReceiveFcn,
andre@0:         pkix_pl_HttpDefaultClient_CancelFcn,
andre@0:         pkix_pl_HttpDefaultClient_FreeFcn
andre@0: };
andre@0: 
andre@0: static SEC_HttpClientFcn httpClient;
andre@0: 
andre@0: static const char *eohMarker = "\r\n\r\n";
andre@0: static const PKIX_UInt32 eohMarkLen = 4; /* strlen(eohMarker) */
andre@0: static const char *crlf = "\r\n";
andre@0: static const PKIX_UInt32 crlfLen = 2; /* strlen(crlf) */
andre@0: static const char *httpprotocol = "HTTP/";
andre@0: static const PKIX_UInt32 httpprotocolLen = 5; /* strlen(httpprotocol) */
andre@0: 
andre@0: 
andre@0: #define HTTP_UNKNOWN_CONTENT_LENGTH -1
andre@0: 
andre@0: /* --Private-HttpDefaultClient-Functions------------------------- */
andre@0: 
andre@0: /*
andre@0:  * FUNCTION: pkix_pl_HttpDefaultClient_HdrCheckComplete
andre@0:  * DESCRIPTION:
andre@0:  *
andre@0:  *  This function determines whether the headers in the current receive buffer
andre@0:  *  in the HttpDefaultClient pointed to by "client" are complete. If so, the
andre@0:  *  input data is checked for status code, content-type and content-length are
andre@0:  *  extracted, and the client is set up to read the body of the response.
andre@0:  *  Otherwise, the client is set up to continue reading header data.
andre@0:  *
andre@0:  * PARAMETERS:
andre@0:  *  "client"
andre@0:  *      The address of the HttpDefaultClient object. Must be non-NULL.
andre@0:  *  "bytesRead"
andre@0:  *      The UInt32 number of bytes received in the latest read.
andre@0:  *  "pKeepGoing"
andre@0:  *      The address at which the Boolean state machine flag is stored to
andre@0:  *      indicate whether processing can continue without further input.
andre@0:  *      Must be non-NULL.
andre@0:  *  "plContext"
andre@0:  *      Platform-specific context pointer.
andre@0:  * THREAD SAFETY:
andre@0:  *  Thread Safe (see Thread Safety Definitions in Programmer's Guide)
andre@0:  * RETURNS:
andre@0:  *  Returns NULL if the function succeeds.
andre@0:  *  Returns a HttpDefaultClient Error if the function fails in a
andre@0:  *      non-fatal way.
andre@0:  *  Returns a Fatal Error if the function fails in an unrecoverable way.
andre@0:  */
andre@0: static PKIX_Error *
andre@0: pkix_pl_HttpDefaultClient_HdrCheckComplete(
andre@0:         PKIX_PL_HttpDefaultClient *client,
andre@0:         PKIX_UInt32 bytesRead,
andre@0:         PKIX_Boolean *pKeepGoing,
andre@0:         void *plContext)
andre@0: {
andre@0:         PKIX_UInt32 alreadyScanned = 0;
andre@0:         PKIX_UInt32 comp = 0;
andre@0:         PKIX_UInt32 headerLength = 0;
andre@0:         PKIX_Int32 contentLength = HTTP_UNKNOWN_CONTENT_LENGTH;
andre@0:         char *eoh = NULL;
andre@0:         char *statusLineEnd = NULL;
andre@0:         char *space = NULL;
andre@0:         char *nextHeader = NULL;
andre@0:         const char *httpcode = NULL;
andre@0:         char *thisHeaderEnd = NULL;
andre@0:         char *value = NULL;
andre@0:         char *colon = NULL;
andre@0: 	char *copy = NULL;
andre@0:         char *body = NULL;
andre@0: 
andre@0:         PKIX_ENTER
andre@0:                 (HTTPDEFAULTCLIENT,
andre@0:                 "pkix_pl_HttpDefaultClient_HdrCheckComplete");
andre@0:         PKIX_NULLCHECK_TWO(client, pKeepGoing);
andre@0: 
andre@0:         *pKeepGoing = PKIX_FALSE;
andre@0: 
andre@0:         /* Does buffer contain end-of-header marker? */
andre@0: 
andre@0:         /* Copy number of scanned bytes into a variable. */
andre@0:         alreadyScanned = client->filledupBytes;
andre@0:         /*
andre@0:          * If this is the initial buffer, we have to scan from the beginning.
andre@0:          * If we scanned, failed to find eohMarker, and read some more, we
andre@0:          * only have to scan from where we left off.
andre@0:          */
andre@0:         if (alreadyScanned > eohMarkLen) {
andre@0:             /* Back up and restart scanning over a few bytes that were
andre@0:              * scanned before */
andre@0:             PKIX_UInt32 searchStartPos = alreadyScanned - eohMarkLen;
andre@0:             eoh = PL_strnstr(&(client->rcvBuf[searchStartPos]), eohMarker,
andre@0:                              bytesRead + searchStartPos);
andre@0:         } else {
andre@0:             /* A search from the beginning of the buffer. */
andre@0:             eoh = PL_strnstr(client->rcvBuf, eohMarker, bytesRead);
andre@0:         }
andre@0: 
andre@0:         client->filledupBytes += bytesRead;
andre@0: 
andre@0:         if (eoh == NULL) { /* did we see end-of-header? */
andre@0:                 /* No. Continue to read header data */
andre@0:                 client->connectStatus = HTTP_RECV_HDR;
andre@0:                 *pKeepGoing = PKIX_TRUE;
andre@0:                 goto cleanup;
andre@0:         }
andre@0: 
andre@0:         /* Yes. Calculate how many bytes in header (not counting eohMarker) */
andre@0:         headerLength = (eoh - client->rcvBuf);
andre@0: 
andre@0:         /* allocate space to copy header (and for the NULL terminator) */
andre@0:         PKIX_CHECK(PKIX_PL_Malloc(headerLength + 1, (void **)&copy, plContext),
andre@0:                 PKIX_MALLOCFAILED);
andre@0: 
andre@0:         /* copy header data before we corrupt it (by storing NULLs) */
andre@0:         PORT_Memcpy(copy, client->rcvBuf, headerLength);
andre@0: 	/* Store the NULL terminator */
andre@0: 	copy[headerLength] = '\0';
andre@0: 	client->rcvHeaders = copy;
andre@0: 
andre@0:         /* Did caller want a pointer to header? */
andre@0:         if (client->rcv_http_headers != NULL) {
andre@0:                 /* store pointer for caller */
andre@0:                 *(client->rcv_http_headers) = copy;
andre@0:         }
andre@0: 
andre@0:         /* Check that message status is okay. */
andre@0:         statusLineEnd = PL_strnstr(client->rcvBuf, crlf, client->capacity);
andre@0:         if (statusLineEnd == NULL) {
andre@0:                 client->connectStatus = HTTP_ERROR;
andre@0:                 PORT_SetError(SEC_ERROR_OCSP_BAD_HTTP_RESPONSE);
andre@0:                 goto cleanup;
andre@0:         }
andre@0: 
andre@0:         *statusLineEnd = '\0';
andre@0: 
andre@0:         space = strchr((const char *)client->rcvBuf, ' ');
andre@0:         if (space == NULL) {
andre@0:                 client->connectStatus = HTTP_ERROR;
andre@0:                 goto cleanup;
andre@0:         }
andre@0: 
andre@0:         comp = PORT_Strncasecmp((const char *)client->rcvBuf, httpprotocol,
andre@0:                                 httpprotocolLen);
andre@0:         if (comp != 0) {
andre@0:                 client->connectStatus = HTTP_ERROR;
andre@0:                 goto cleanup;
andre@0:         }
andre@0: 
andre@0:         httpcode = space + 1;
andre@0:         space = strchr(httpcode, ' ');
andre@0:         if (space == NULL) {
andre@0:                 client->connectStatus = HTTP_ERROR;
andre@0:                 goto cleanup;
andre@0:         }
andre@0:         *space = '\0';
andre@0: 
andre@0:         client->responseCode = atoi(httpcode);
andre@0:         if (client->responseCode != 200) {
andre@0:                 client->connectStatus = HTTP_ERROR;
andre@0:                 goto cleanup;
andre@0:         }
andre@0: 
andre@0:         /* Find the content-type and content-length */
andre@0:         nextHeader = statusLineEnd + crlfLen;
andre@0:         *eoh = '\0';
andre@0:         do {
andre@0:                 thisHeaderEnd = NULL;
andre@0:                 value = NULL;
andre@0: 
andre@0:                 colon = strchr(nextHeader, ':');
andre@0:                 if (colon == NULL) {
andre@0:                         client->connectStatus = HTTP_ERROR;
andre@0:                         goto cleanup;
andre@0:                 }
andre@0:                 *colon = '\0';
andre@0:                 value = colon + 1;
andre@0:                 if (*value != ' ') {
andre@0:                         client->connectStatus = HTTP_ERROR;
andre@0:                         goto cleanup;
andre@0:                 }
andre@0:                 value++;
andre@0:                 thisHeaderEnd = strstr(value, crlf);
andre@0:                 if (thisHeaderEnd != NULL) {
andre@0:                         *thisHeaderEnd = '\0';
andre@0:                 }
andre@0:                 comp = PORT_Strcasecmp(nextHeader, "content-type");
andre@0:                 if (comp == 0) {
andre@0:                         client->rcvContentType = PORT_Strdup(value);
andre@0:                 } else {
andre@0:                         comp = PORT_Strcasecmp(nextHeader, "content-length");
andre@0:                         if (comp == 0) {
andre@0:                                 contentLength = atoi(value);
andre@0:                         }
andre@0:                 }
andre@0:                 if (thisHeaderEnd != NULL) {
andre@0:                         nextHeader = thisHeaderEnd + crlfLen;
andre@0:                 } else {
andre@0:                         nextHeader = NULL;
andre@0:                 }
andre@0:         } while ((nextHeader != NULL) && (nextHeader < (eoh + crlfLen)));
andre@0:                 
andre@0:         /* Did caller provide a pointer to return content-type? */
andre@0:         if (client->rcv_http_content_type != NULL) {
andre@0:                 *(client->rcv_http_content_type) = client->rcvContentType;
andre@0:         }
andre@0: 
andre@0:         if (client->rcvContentType == NULL) {
andre@0:                 client->connectStatus = HTTP_ERROR;
andre@0:                 goto cleanup;
andre@0:         }
andre@0: 
andre@0:          /* How many bytes remain in current buffer, beyond the header? */
andre@0:          headerLength += eohMarkLen;
andre@0:          client->filledupBytes -= headerLength;
andre@0:  
andre@0:          /*
andre@0:           * The headers have passed validation. Now figure out whether the
andre@0:           * message is within the caller's size limit (if one was specified).
andre@0:           */
andre@0:          switch (contentLength) {
andre@0:          case 0:
andre@0:              client->rcv_http_data_len = 0;
andre@0:              client->connectStatus = HTTP_COMPLETE;
andre@0:              *pKeepGoing = PKIX_FALSE;
andre@0:              break;
andre@0:  
andre@0:          case HTTP_UNKNOWN_CONTENT_LENGTH:
andre@0:              /* Unknown contentLength indicator.Will be set by
andre@0:               * pkix_pl_HttpDefaultClient_RecvBody whey connection get closed */
andre@0:              client->rcv_http_data_len = HTTP_UNKNOWN_CONTENT_LENGTH;
andre@0:              contentLength =                 /* Try to reserve 4K+ buffer */
andre@0:                  client->filledupBytes + HTTP_DATA_BUFSIZE;
andre@0:              if (client->maxResponseLen > 0 &&
andre@0:                  contentLength > client->maxResponseLen) {
andre@0:                  if (client->filledupBytes < client->maxResponseLen) {
andre@0:                      contentLength = client->maxResponseLen;
andre@0:                  } else {
andre@0:                      client->connectStatus = HTTP_ERROR;
andre@0:                      goto cleanup;
andre@0:                  }
andre@0:              }
andre@0:              /* set available number of bytes in the buffer */
andre@0:              client->capacity = contentLength;
andre@0:              client->connectStatus = HTTP_RECV_BODY;
andre@0:              *pKeepGoing = PKIX_TRUE;
andre@0:              break;
andre@0:  
andre@0:          default:
andre@0:              client->rcv_http_data_len = contentLength;
andre@0:              if (client->maxResponseLen > 0 &&
andre@0:                  client->maxResponseLen < contentLength) {
andre@0:                  client->connectStatus = HTTP_ERROR;
andre@0:                  goto cleanup;
andre@0:              }
andre@0:              
andre@0:              /*
andre@0:               * Do we have all of the message body, or do we need to read some more?
andre@0:               */
andre@0:              if (client->filledupBytes < contentLength) {
andre@0:                  client->connectStatus = HTTP_RECV_BODY;
andre@0:                  *pKeepGoing = PKIX_TRUE;
andre@0:              } else {
andre@0:                  client->connectStatus = HTTP_COMPLETE;
andre@0:                  *pKeepGoing = PKIX_FALSE;
andre@0:              }
andre@0:          }
andre@0:  
andre@0:          if (contentLength > 0) {
andre@0:              /* allocate a buffer of size contentLength  for the content */
andre@0:              PKIX_CHECK(PKIX_PL_Malloc(contentLength, (void **)&body, plContext),
andre@0:                         PKIX_MALLOCFAILED);
andre@0:              
andre@0:              /* copy any remaining bytes in current buffer into new buffer */
andre@0:              if (client->filledupBytes > 0) {
andre@0:                  PORT_Memcpy(body, &(client->rcvBuf[headerLength]),
andre@0:                              client->filledupBytes);
andre@0:              }
andre@0:          }
andre@0:  
andre@0:          PKIX_CHECK(PKIX_PL_Free(client->rcvBuf, plContext),
andre@0:                     PKIX_FREEFAILED);
andre@0:          client->rcvBuf = body;
andre@0: 
andre@0: cleanup:
andre@0: 
andre@0:         PKIX_RETURN(HTTPDEFAULTCLIENT);
andre@0: }
andre@0: 
andre@0: /*
andre@0:  * FUNCTION: PKIX_PL_HttpDefaultClient_Create
andre@0:  * DESCRIPTION:
andre@0:  *
andre@0:  *  This function creates a new HttpDefaultClient, and stores the result at
andre@0:  *  "pClient".
andre@0:  *
andre@0:  *  The HttpClient API does not include a plContext argument in its
andre@0:  *  function calls. Its value at the time of this Create call must be the
andre@0:  *  same as when the client is invoked.
andre@0:  *
andre@0:  * PARAMETERS:
andre@0:  *  "host"
andre@0:  *      The name of the server with which we hope to exchange messages. Must
andre@0:  *      be non-NULL.
andre@0:  *  "portnum"
andre@0:  *      The port number to be used for our connection to the server.
andre@0:  *  "pClient"
andre@0:  *      The address at which the created HttpDefaultClient is to be stored.
andre@0:  *      Must be non-NULL.
andre@0:  *  "plContext"
andre@0:  *      Platform-specific context pointer.
andre@0:  * THREAD SAFETY:
andre@0:  *  Thread Safe (see Thread Safety Definitions in Programmer's Guide)
andre@0:  * RETURNS:
andre@0:  *  Returns NULL if the function succeeds.
andre@0:  *  Returns a HttpDefaultClient Error if the function fails in
andre@0:  *      a non-fatal way.
andre@0:  *  Returns a Fatal Error if the function fails in an unrecoverable way.
andre@0:  */
andre@0: static PKIX_Error *
andre@0: pkix_pl_HttpDefaultClient_Create(
andre@0:         const char *host,
andre@0:         PRUint16 portnum,
andre@0:         PKIX_PL_HttpDefaultClient **pClient,
andre@0:         void *plContext)
andre@0: {
andre@0:         PKIX_PL_HttpDefaultClient *client = NULL;
andre@0: 
andre@0:         PKIX_ENTER(HTTPDEFAULTCLIENT, "PKIX_PL_HttpDefaultClient_Create");
andre@0:         PKIX_NULLCHECK_TWO(pClient, host);
andre@0: 
andre@0:         /* allocate an HttpDefaultClient */
andre@0:         PKIX_CHECK(PKIX_PL_Object_Alloc
andre@0:                 (PKIX_HTTPDEFAULTCLIENT_TYPE,
andre@0:                 sizeof (PKIX_PL_HttpDefaultClient),
andre@0:                 (PKIX_PL_Object **)&client,
andre@0:                 plContext),
andre@0:                 PKIX_COULDNOTCREATEHTTPDEFAULTCLIENTOBJECT);
andre@0: 
andre@0:         /* Client timeout is overwritten in HttpDefaultClient_RequestCreate
andre@0:          * function. Default value will be ignored. */
andre@0:         client->timeout = 0;
andre@0:         client->connectStatus = HTTP_NOT_CONNECTED;
andre@0:         client->portnum = portnum;
andre@0:         client->bytesToWrite = 0;
andre@0:         client->send_http_data_len = 0;
andre@0:         client->rcv_http_data_len = 0;
andre@0:         client->capacity = 0;
andre@0:         client->filledupBytes = 0;
andre@0:         client->responseCode = 0;
andre@0:         client->maxResponseLen = 0;
andre@0:         client->GETLen = 0;
andre@0:         client->POSTLen = 0;
andre@0:         client->pRcv_http_data_len = NULL;
andre@0:         client->callbackList = NULL;
andre@0:         client->GETBuf = NULL;
andre@0:         client->POSTBuf = NULL;
andre@0:         client->rcvBuf = NULL;
andre@0:         /* "host" is a parsing result by CERT_GetURL function that adds
andre@0:          * "end of line" to the value. OK to dup the string. */
andre@0:         client->host = PORT_Strdup(host);
andre@0:         if (!client->host) {
andre@0:             PKIX_ERROR(PKIX_ALLOCERROR);
andre@0:         }
andre@0:         client->path = NULL;
andre@0:         client->rcvContentType = NULL;
andre@0:         client->rcvHeaders = NULL;
andre@0:         client->send_http_method = HTTP_POST_METHOD;
andre@0:         client->send_http_content_type = NULL;
andre@0:         client->send_http_data = NULL;
andre@0:         client->rcv_http_response_code = NULL;
andre@0:         client->rcv_http_content_type = NULL;
andre@0:         client->rcv_http_headers = NULL;
andre@0:         client->rcv_http_data = NULL;
andre@0:         client->socket = NULL;
andre@0: 
andre@0:         /*
andre@0:          * The HttpClient API does not include a plContext argument in its
andre@0:          * function calls. Save it here.
andre@0:          */
andre@0:         client->plContext = plContext;
andre@0: 
andre@0:         *pClient = client;
andre@0: 
andre@0: cleanup:
andre@0:         if (PKIX_ERROR_RECEIVED) {
andre@0:                 PKIX_DECREF(client);
andre@0:         }
andre@0: 
andre@0:         PKIX_RETURN(HTTPDEFAULTCLIENT);
andre@0: }
andre@0: 
andre@0: /*
andre@0:  * FUNCTION: pkix_pl_HttpDefaultClient_Destroy
andre@0:  * (see comments for PKIX_PL_DestructorCallback in pkix_pl_system.h)
andre@0:  */
andre@0: static PKIX_Error *
andre@0: pkix_pl_HttpDefaultClient_Destroy(
andre@0:         PKIX_PL_Object *object,
andre@0:         void *plContext)
andre@0: {
andre@0:         PKIX_PL_HttpDefaultClient *client = NULL;
andre@0: 
andre@0:         PKIX_ENTER(HTTPDEFAULTCLIENT, "pkix_pl_HttpDefaultClient_Destroy");
andre@0:         PKIX_NULLCHECK_ONE(object);
andre@0: 
andre@0:         PKIX_CHECK(pkix_CheckType
andre@0:                     (object, PKIX_HTTPDEFAULTCLIENT_TYPE, plContext),
andre@0:                     PKIX_OBJECTNOTANHTTPDEFAULTCLIENT);
andre@0: 
andre@0:         client = (PKIX_PL_HttpDefaultClient *)object;
andre@0: 
andre@0:         if (client->rcvHeaders) {
andre@0:             PKIX_PL_Free(client->rcvHeaders, plContext);
andre@0:             client->rcvHeaders = NULL;
andre@0:         }
andre@0:         if (client->rcvContentType) {
andre@0:             PORT_Free(client->rcvContentType);
andre@0:             client->rcvContentType = NULL;
andre@0:         }
andre@0:         if (client->GETBuf != NULL) {
andre@0:                 PR_smprintf_free(client->GETBuf);
andre@0:                 client->GETBuf = NULL;
andre@0:         }
andre@0:         if (client->POSTBuf != NULL) {
andre@0:                 PKIX_PL_Free(client->POSTBuf, plContext);
andre@0:                 client->POSTBuf = NULL;
andre@0:         }
andre@0:         if (client->rcvBuf != NULL) {
andre@0:                 PKIX_PL_Free(client->rcvBuf, plContext);
andre@0:                 client->rcvBuf = NULL;
andre@0:         }
andre@0:         if (client->host) {
andre@0:                 PORT_Free(client->host);
andre@0:                 client->host = NULL;
andre@0:         }
andre@0:         if (client->path) {
andre@0:                 PORT_Free(client->path);
andre@0:                 client->path = NULL;
andre@0:         }
andre@0:         PKIX_DECREF(client->socket);
andre@0: 
andre@0: cleanup:
andre@0: 
andre@0:         PKIX_RETURN(HTTPDEFAULTCLIENT);
andre@0: }
andre@0: 
andre@0: /*
andre@0:  * FUNCTION: pkix_pl_HttpDefaultClient_RegisterSelf
andre@0:  *
andre@0:  * DESCRIPTION:
andre@0:  *  Registers PKIX_PL_HTTPDEFAULTCLIENT_TYPE and its related
andre@0:  *  functions with systemClasses[]
andre@0:  *
andre@0:  * THREAD SAFETY:
andre@0:  *  Not Thread Safe - for performance and complexity reasons
andre@0:  *
andre@0:  *  Since this function is only called by PKIX_PL_Initialize, which should
andre@0:  *  only be called once, it is acceptable that this function is not
andre@0:  *  thread-safe.
andre@0:  */
andre@0: PKIX_Error *
andre@0: pkix_pl_HttpDefaultClient_RegisterSelf(void *plContext)
andre@0: {
andre@0:         extern pkix_ClassTable_Entry systemClasses[PKIX_NUMTYPES];
andre@0:         pkix_ClassTable_Entry *entry =
andre@0:             &systemClasses[PKIX_HTTPDEFAULTCLIENT_TYPE];
andre@0: 
andre@0:         PKIX_ENTER(HTTPDEFAULTCLIENT,
andre@0:                 "pkix_pl_HttpDefaultClient_RegisterSelf");
andre@0: 
andre@0:         entry->description = "HttpDefaultClient";
andre@0:         entry->typeObjectSize = sizeof(PKIX_PL_HttpDefaultClient);
andre@0:         entry->destructor = pkix_pl_HttpDefaultClient_Destroy;
andre@0:  
andre@0:         httpClient.version = 1;
andre@0:         httpClient.fcnTable.ftable1 = vtable;
andre@0:         (void)SEC_RegisterDefaultHttpClient(&httpClient);
andre@0: 
andre@0:         PKIX_RETURN(HTTPDEFAULTCLIENT);
andre@0: }
andre@0: 
andre@0: /* --Private-HttpDefaultClient-I/O-Functions---------------------------- */
andre@0: /*
andre@0:  * FUNCTION: pkix_pl_HttpDefaultClient_ConnectContinue
andre@0:  * DESCRIPTION:
andre@0:  *
andre@0:  *  This function determines whether a socket Connect initiated earlier for the
andre@0:  *  HttpDefaultClient "client" has completed, and stores in "pKeepGoing" a flag
andre@0:  *  indicating whether processing can continue without further input.
andre@0:  *
andre@0:  * PARAMETERS:
andre@0:  *  "client"
andre@0:  *      The address of the HttpDefaultClient object. Must be non-NULL.
andre@0:  *  "pKeepGoing"
andre@0:  *      The address at which the Boolean state machine flag is stored to
andre@0:  *      indicate whether processing can continue without further input.
andre@0:  *      Must be non-NULL.
andre@0:  *  "plContext"
andre@0:  *      Platform-specific context pointer.
andre@0:  * THREAD SAFETY:
andre@0:  *  Thread Safe (see Thread Safety Definitions in Programmer's Guide)
andre@0:  * RETURNS:
andre@0:  *  Returns NULL if the function succeeds.
andre@0:  *  Returns a HttpDefaultClient Error if the function fails in a
andre@0:  *      non-fatal way.
andre@0:  *  Returns a Fatal Error if the function fails in an unrecoverable way.
andre@0:  */
andre@0: static PKIX_Error *
andre@0: pkix_pl_HttpDefaultClient_ConnectContinue(
andre@0:         PKIX_PL_HttpDefaultClient *client,
andre@0:         PKIX_Boolean *pKeepGoing,
andre@0:         void *plContext)
andre@0: {
andre@0:         PRErrorCode status;
andre@0:         PKIX_Boolean keepGoing = PKIX_FALSE;
andre@0:         PKIX_PL_Socket_Callback *callbackList = NULL;
andre@0: 
andre@0:         PKIX_ENTER
andre@0:                 (HTTPDEFAULTCLIENT,
andre@0:                 "pkix_pl_HttpDefaultClient_ConnectContinue");
andre@0:         PKIX_NULLCHECK_ONE(client);
andre@0: 
andre@0:         callbackList = (PKIX_PL_Socket_Callback *)client->callbackList;
andre@0: 
andre@0:         PKIX_CHECK(callbackList->connectcontinueCallback
andre@0:                 (client->socket, &status, plContext),
andre@0:                 PKIX_SOCKETCONNECTCONTINUEFAILED);
andre@0: 
andre@0:         if (status == 0) {
andre@0:                 client->connectStatus = HTTP_CONNECTED;
andre@0:                 keepGoing = PKIX_TRUE;
andre@0:         } else if (status != PR_IN_PROGRESS_ERROR) {
andre@0:                 PKIX_ERROR(PKIX_UNEXPECTEDERRORINESTABLISHINGCONNECTION);
andre@0:         }
andre@0: 
andre@0:         *pKeepGoing = keepGoing;
andre@0: 
andre@0: cleanup:
andre@0:         PKIX_RETURN(HTTPDEFAULTCLIENT);
andre@0: }
andre@0: 
andre@0: /*
andre@0:  * FUNCTION: pkix_pl_HttpDefaultClient_Send
andre@0:  * DESCRIPTION:
andre@0:  *
andre@0:  *  This function creates and sends HTTP-protocol headers and, if applicable,
andre@0:  *  data, for the HttpDefaultClient "client", and stores in "pKeepGoing" a flag
andre@0:  *  indicating whether processing can continue without further input, and at
andre@0:  *  "pBytesTransferred" the number of bytes sent.
andre@0:  *
andre@0:  *  If "pBytesTransferred" is zero, it indicates that non-blocking I/O is in use
andre@0:  *  and that transmission has not completed.
andre@0:  *
andre@0:  * PARAMETERS:
andre@0:  *  "client"
andre@0:  *      The address of the HttpDefaultClient object. Must be non-NULL.
andre@0:  *  "pKeepGoing"
andre@0:  *      The address at which the Boolean state machine flag is stored to
andre@0:  *      indicate whether processing can continue without further input.
andre@0:  *      Must be non-NULL.
andre@0:  *  "pBytesTransferred"
andre@0:  *      The address at which the number of bytes sent is stored. Must be
andre@0:  *      non-NULL.
andre@0:  *  "plContext"
andre@0:  *      Platform-specific context pointer.
andre@0:  * THREAD SAFETY:
andre@0:  *  Thread Safe (see Thread Safety Definitions in Programmer's Guide)
andre@0:  * RETURNS:
andre@0:  *  Returns NULL if the function succeeds.
andre@0:  *  Returns a HttpDefaultClient Error if the function fails in a
andre@0:  *      non-fatal way.
andre@0:  *  Returns a Fatal Error if the function fails in an unrecoverable way.
andre@0:  */
andre@0: static PKIX_Error *
andre@0: pkix_pl_HttpDefaultClient_Send(
andre@0:         PKIX_PL_HttpDefaultClient *client,
andre@0:         PKIX_Boolean *pKeepGoing,
andre@0:         PKIX_UInt32 *pBytesTransferred,
andre@0:         void *plContext)
andre@0: {
andre@0:         PKIX_Int32 bytesWritten = 0;
andre@0:         PKIX_Int32 lenToWrite = 0;
andre@0:         PKIX_PL_Socket_Callback *callbackList = NULL;
andre@0:         char *dataToWrite = NULL;
andre@0: 
andre@0:         PKIX_ENTER(HTTPDEFAULTCLIENT, "pkix_pl_HttpDefaultClient_Send");
andre@0:         PKIX_NULLCHECK_THREE(client, pKeepGoing, pBytesTransferred);
andre@0: 
andre@0:         *pKeepGoing = PKIX_FALSE;
andre@0: 
andre@0:         /* Do we have anything waiting to go? */
andre@0:         if ((client->GETBuf) || (client->POSTBuf)) {
andre@0: 
andre@0:                 if (client->GETBuf) {
andre@0:                         dataToWrite = client->GETBuf;
andre@0:                         lenToWrite = client->GETLen;
andre@0:                 } else {
andre@0:                         dataToWrite = client->POSTBuf;
andre@0:                         lenToWrite = client->POSTLen;
andre@0:                 }
andre@0: 
andre@0:                 callbackList = (PKIX_PL_Socket_Callback *)client->callbackList;
andre@0: 
andre@0:                 PKIX_CHECK(callbackList->sendCallback
andre@0:                         (client->socket,
andre@0:                         dataToWrite,
andre@0:                         lenToWrite,
andre@0:                         &bytesWritten,
andre@0:                         plContext),
andre@0:                         PKIX_SOCKETSENDFAILED);
andre@0: 
andre@0:                 client->rcvBuf = NULL;
andre@0:                 client->capacity = 0;
andre@0:                 client->filledupBytes = 0;
andre@0: 
andre@0:                 /*
andre@0:                  * If the send completed we can proceed to try for the
andre@0:                  * response. If the send did not complete we will have
andre@0:                  * to poll for completion later.
andre@0:                  */
andre@0:                 if (bytesWritten >= 0) {
andre@0:                         client->connectStatus = HTTP_RECV_HDR;
andre@0:                         *pKeepGoing = PKIX_TRUE;
andre@0:                 } else {
andre@0:                         client->connectStatus = HTTP_SEND_PENDING;
andre@0:                         *pKeepGoing = PKIX_FALSE;
andre@0:                 }
andre@0: 
andre@0:         }
andre@0: 
andre@0:         *pBytesTransferred = bytesWritten;
andre@0: 
andre@0: cleanup:
andre@0:         PKIX_RETURN(HTTPDEFAULTCLIENT);
andre@0: }
andre@0: 
andre@0: /*
andre@0:  * FUNCTION: pkix_pl_HttpDefaultClient_SendContinue
andre@0:  * DESCRIPTION:
andre@0:  *
andre@0:  *  This function determines whether the sending of the HTTP message for the
andre@0:  *  HttpDefaultClient "client" has completed, and stores in "pKeepGoing" a
andre@0:  *  flag indicating whether processing can continue without further input, and
andre@0:  *  at "pBytesTransferred" the number of bytes sent.
andre@0:  *
andre@0:  *  If "pBytesTransferred" is zero, it indicates that non-blocking I/O is in use
andre@0:  *  and that transmission has not completed.
andre@0:  *
andre@0:  * PARAMETERS:
andre@0:  *  "client"
andre@0:  *      The address of the HttpDefaultClient object. Must be non-NULL.
andre@0:  *  "pKeepGoing"
andre@0:  *      The address at which the Boolean state machine flag is stored to
andre@0:  *      indicate whether processing can continue without further input.
andre@0:  *      Must be non-NULL.
andre@0:  *  "pBytesTransferred"
andre@0:  *      The address at which the number of bytes sent is stored. Must be
andre@0:  *      non-NULL.
andre@0:  *  "plContext"
andre@0:  *      Platform-specific context pointer.
andre@0:  * THREAD SAFETY:
andre@0:  *  Thread Safe (see Thread Safety Definitions in Programmer's Guide)
andre@0:  * RETURNS:
andre@0:  *  Returns NULL if the function succeeds.
andre@0:  *  Returns a HttpDefaultClient Error if the function fails in a
andre@0:  *      non-fatal way.
andre@0:  *  Returns a Fatal Error if the function fails in an unrecoverable way.
andre@0:  */
andre@0: static PKIX_Error *
andre@0: pkix_pl_HttpDefaultClient_SendContinue(
andre@0:         PKIX_PL_HttpDefaultClient *client,
andre@0:         PKIX_Boolean *pKeepGoing,
andre@0:         PKIX_UInt32 *pBytesTransferred,
andre@0:         void *plContext)
andre@0: {
andre@0:         PKIX_Int32 bytesWritten = 0;
andre@0:         PKIX_PL_Socket_Callback *callbackList = NULL;
andre@0: 
andre@0:         PKIX_ENTER(HTTPDEFAULTCLIENT, "pkix_pl_HttpDefaultClient_SendContinue");
andre@0:         PKIX_NULLCHECK_THREE(client, pKeepGoing, pBytesTransferred);
andre@0: 
andre@0:         *pKeepGoing = PKIX_FALSE;
andre@0: 
andre@0:         callbackList = (PKIX_PL_Socket_Callback *)client->callbackList;
andre@0: 
andre@0:         PKIX_CHECK(callbackList->pollCallback
andre@0:                 (client->socket, &bytesWritten, NULL, plContext),
andre@0:                 PKIX_SOCKETPOLLFAILED);
andre@0: 
andre@0:         /*
andre@0:          * If the send completed we can proceed to try for the
andre@0:          * response. If the send did not complete we will have
andre@0:          * continue to poll.
andre@0:          */
andre@0:         if (bytesWritten >= 0) {
andre@0:                        client->connectStatus = HTTP_RECV_HDR;
andre@0:                 *pKeepGoing = PKIX_TRUE;
andre@0:         }
andre@0: 
andre@0:         *pBytesTransferred = bytesWritten;
andre@0: 
andre@0: cleanup:
andre@0:         PKIX_RETURN(HTTPDEFAULTCLIENT);
andre@0: }
andre@0: 
andre@0: /*
andre@0:  * FUNCTION: pkix_pl_HttpDefaultClient_RecvHdr
andre@0:  * DESCRIPTION:
andre@0:  *
andre@0:  *  This function receives HTTP headers for the HttpDefaultClient "client", and
andre@0:  *  stores in "pKeepGoing" a flag indicating whether processing can continue
andre@0:  *  without further input.
andre@0:  *
andre@0:  * PARAMETERS:
andre@0:  *  "client"
andre@0:  *      The address of the HttpDefaultClient object. Must be non-NULL.
andre@0:  *  "pKeepGoing"
andre@0:  *      The address at which the Boolean state machine flag is stored to
andre@0:  *      indicate whether processing can continue without further input.
andre@0:  *      Must be non-NULL.
andre@0:  *  "plContext"
andre@0:  *      Platform-specific context pointer.
andre@0:  * THREAD SAFETY:
andre@0:  *  Thread Safe (see Thread Safety Definitions in Programmer's Guide)
andre@0:  * RETURNS:
andre@0:  *  Returns NULL if the function succeeds.
andre@0:  *  Returns a HttpDefaultClient Error if the function fails in a
andre@0:  *      non-fatal way.
andre@0:  *  Returns a Fatal Error if the function fails in an unrecoverable way.
andre@0:  */
andre@0: static PKIX_Error *
andre@0: pkix_pl_HttpDefaultClient_RecvHdr(
andre@0:         PKIX_PL_HttpDefaultClient *client,
andre@0:         PKIX_Boolean *pKeepGoing,
andre@0:         void *plContext)
andre@0: {
andre@0:         PKIX_UInt32 bytesToRead = 0;
andre@0:         PKIX_Int32 bytesRead = 0;
andre@0:         PKIX_PL_Socket_Callback *callbackList = NULL;
andre@0: 
andre@0:         PKIX_ENTER(HTTPDEFAULTCLIENT, "pkix_pl_HttpDefaultClient_RecvHdr");
andre@0:         PKIX_NULLCHECK_TWO(client, pKeepGoing);
andre@0: 
andre@0:         /*
andre@0:          * rcvbuf, capacity, and filledupBytes were
andre@0:          * initialized when we wrote the headers. We begin by reading
andre@0:          * HTTP_HEADER_BUFSIZE bytes, repeatedly increasing the buffersize and
andre@0:          * reading again if necessary, until we have read the end-of-header
andre@0:          * marker, "\r\n\r\n", or have reached our maximum.
andre@0:          */
andre@0:         client->capacity += HTTP_HEADER_BUFSIZE;
andre@0:         PKIX_CHECK(PKIX_PL_Realloc
andre@0:                 (client->rcvBuf,
andre@0:                 client->capacity,
andre@0:                 (void **)&(client->rcvBuf),
andre@0:                 plContext),
andre@0:                 PKIX_REALLOCFAILED);
andre@0: 
andre@0:         bytesToRead = client->capacity - client->filledupBytes;
andre@0: 
andre@0:         callbackList = (PKIX_PL_Socket_Callback *)client->callbackList;
andre@0: 
andre@0:         PKIX_CHECK(callbackList->recvCallback
andre@0:                 (client->socket,
andre@0:                 (void *)&(client->rcvBuf[client->filledupBytes]),
andre@0:                 bytesToRead,
andre@0:                 &bytesRead,
andre@0:                 plContext),
andre@0:                 PKIX_SOCKETRECVFAILED);
andre@0: 
andre@0:         if (bytesRead > 0) {
andre@0:             /* client->filledupBytes will be adjusted by
andre@0:              * pkix_pl_HttpDefaultClient_HdrCheckComplete */
andre@0:             PKIX_CHECK(
andre@0:                 pkix_pl_HttpDefaultClient_HdrCheckComplete(client, bytesRead,
andre@0:                                                            pKeepGoing,
andre@0:                                                            plContext),
andre@0:                        PKIX_HTTPDEFAULTCLIENTHDRCHECKCOMPLETEFAILED);
andre@0:         } else {
andre@0:             client->connectStatus = HTTP_RECV_HDR_PENDING;
andre@0:             *pKeepGoing = PKIX_FALSE;
andre@0:         }
andre@0: 
andre@0: cleanup:
andre@0:         PKIX_RETURN(HTTPDEFAULTCLIENT);
andre@0: }
andre@0: 
andre@0: /*
andre@0:  * FUNCTION: pkix_pl_HttpDefaultClient_RecvHdrContinue
andre@0:  * DESCRIPTION:
andre@0:  *
andre@0:  *  This function determines whether the receiving of the HTTP headers for the
andre@0:  *  HttpDefaultClient "client" has completed, and stores in "pKeepGoing" a flag
andre@0:  *  indicating whether processing can continue without further input.
andre@0:  *
andre@0:  * PARAMETERS:
andre@0:  *  "client"
andre@0:  *      The address of the HttpDefaultClient object. Must be non-NULL.
andre@0:  *  "pKeepGoing"
andre@0:  *      The address at which the Boolean state machine flag is stored to
andre@0:  *      indicate whether processing can continue without further input.
andre@0:  *      Must be non-NULL.
andre@0:  *  "plContext"
andre@0:  *      Platform-specific context pointer.
andre@0:  * THREAD SAFETY:
andre@0:  *  Thread Safe (see Thread Safety Definitions in Programmer's Guide)
andre@0:  * RETURNS:
andre@0:  *  Returns NULL if the function succeeds.
andre@0:  *  Returns a HttpDefaultClient Error if the function fails in a
andre@0:  *      non-fatal way.
andre@0:  *  Returns a Fatal Error if the function fails in an unrecoverable way.
andre@0:  */
andre@0: static PKIX_Error *
andre@0: pkix_pl_HttpDefaultClient_RecvHdrContinue(
andre@0:         PKIX_PL_HttpDefaultClient *client,
andre@0:         PKIX_Boolean *pKeepGoing,
andre@0:         void *plContext)
andre@0: {
andre@0:         PKIX_Int32 bytesRead = 0;
andre@0:         PKIX_PL_Socket_Callback *callbackList = NULL;
andre@0: 
andre@0:         PKIX_ENTER
andre@0:                 (HTTPDEFAULTCLIENT,
andre@0:                 "pkix_pl_HttpDefaultClient_RecvHdrContinue");
andre@0:         PKIX_NULLCHECK_TWO(client, pKeepGoing);
andre@0: 
andre@0:         callbackList = (PKIX_PL_Socket_Callback *)client->callbackList;
andre@0: 
andre@0:         PKIX_CHECK(callbackList->pollCallback
andre@0:                 (client->socket, NULL, &bytesRead, plContext),
andre@0:                 PKIX_SOCKETPOLLFAILED);
andre@0: 
andre@0:         if (bytesRead > 0) {
andre@0:                 client->filledupBytes += bytesRead;
andre@0: 
andre@0:                 PKIX_CHECK(pkix_pl_HttpDefaultClient_HdrCheckComplete
andre@0:                         (client, bytesRead, pKeepGoing, plContext),
andre@0:                         PKIX_HTTPDEFAULTCLIENTHDRCHECKCOMPLETEFAILED);
andre@0: 
andre@0:         } else {
andre@0: 
andre@0:                 *pKeepGoing = PKIX_FALSE;
andre@0: 
andre@0:         }
andre@0: 
andre@0: cleanup:
andre@0:         PKIX_RETURN(HTTPDEFAULTCLIENT);
andre@0: }
andre@0: 
andre@0: /*
andre@0:  * FUNCTION: pkix_pl_HttpDefaultClient_RecvBody
andre@0:  * DESCRIPTION:
andre@0:  *
andre@0:  *  This function processes the contents of the first buffer of a received
andre@0:  *  HTTP-protocol message for the HttpDefaultClient "client", and stores in
andre@0:  *  "pKeepGoing" a flag indicating whether processing can continue without
andre@0:  *  further input.
andre@0:  *
andre@0:  * PARAMETERS:
andre@0:  *  "client"
andre@0:  *      The address of the HttpDefaultClient object. Must be non-NULL.
andre@0:  *  "pKeepGoing"
andre@0:  *      The address at which the Boolean state machine flag is stored to
andre@0:  *      indicate whether processing can continue without further input.
andre@0:  *      Must be non-NULL.
andre@0:  *  "plContext"
andre@0:  *      Platform-specific context pointer.
andre@0:  * THREAD SAFETY:
andre@0:  *  Thread Safe (see Thread Safety Definitions in Programmer's Guide)
andre@0:  * RETURNS:
andre@0:  *  Returns NULL if the function succeeds.
andre@0:  *  Returns a HttpDefaultClient Error if the function fails in a
andre@0:  *      non-fatal way.
andre@0:  *  Returns a Fatal Error if the function fails in an unrecoverable way.
andre@0:  */
andre@0: static PKIX_Error *
andre@0: pkix_pl_HttpDefaultClient_RecvBody(
andre@0:         PKIX_PL_HttpDefaultClient *client,
andre@0:         PKIX_Boolean *pKeepGoing,
andre@0:         void *plContext)
andre@0: {
andre@0:         PKIX_Int32 bytesRead = 0;
andre@0:         PKIX_Int32 bytesToRead = 0;
andre@0:         PKIX_PL_Socket_Callback *callbackList = NULL;
andre@0: 
andre@0:         PKIX_ENTER(HTTPDEFAULTCLIENT, "pkix_pl_HttpDefaultClient_RecvBody");
andre@0:         PKIX_NULLCHECK_TWO(client, pKeepGoing);
andre@0: 
andre@0:         callbackList = (PKIX_PL_Socket_Callback *)client->callbackList;
andre@0: 
andre@0:         if (client->rcv_http_data_len != HTTP_UNKNOWN_CONTENT_LENGTH) {
andre@0:             bytesToRead = client->rcv_http_data_len -
andre@0:                 client->filledupBytes;
andre@0:         } else {
andre@0:             /* Reading till the EOF. Context length is not known.*/
andre@0:             /* Check the buffer capacity: increase and
andre@0:              * reallocate if it is low. */
andre@0:             int freeBuffSize = client->capacity - client->filledupBytes;
andre@0:             if (freeBuffSize < HTTP_MIN_AVAILABLE_BUFFER_SIZE) {
andre@0:                 /* New length will be consist of available(downloaded) bytes,
andre@0:                  * plus remaining capacity, plus new expansion. */
andre@0:                 int currBuffSize = client->capacity;
andre@0:                 /* Try to increase the buffer by 4K */
andre@0:                 int newLength = currBuffSize + HTTP_DATA_BUFSIZE;
andre@0:                 if (client->maxResponseLen > 0 &&
andre@0:                     newLength > client->maxResponseLen) {
andre@0:                         newLength = client->maxResponseLen;
andre@0:                 }
andre@0:                 /* Check if we can grow the buffer and report an error if
andre@0:                  * new size is not larger than the current size of the buffer.*/
andre@0:                 if (newLength <= client->filledupBytes) {
andre@0:                     client->rcv_http_data_len = client->filledupBytes;
andre@0:                     client->connectStatus = HTTP_ERROR;
andre@0:                     *pKeepGoing = PKIX_FALSE;
andre@0:                     goto cleanup;
andre@0:                 }
andre@0:                 if (client->capacity < newLength) {
andre@0:                     client->capacity = newLength;
andre@0:                     PKIX_CHECK(
andre@0:                         PKIX_PL_Realloc(client->rcvBuf, newLength,
andre@0:                                         (void**)&client->rcvBuf, plContext),
andre@0:                         PKIX_REALLOCFAILED);
andre@0:                     freeBuffSize = client->capacity -
andre@0:                         client->filledupBytes;
andre@0:                 }
andre@0:             }
andre@0:             bytesToRead = freeBuffSize;
andre@0:         }
andre@0: 
andre@0:         /* Use poll callback if waiting on non-blocking IO */
andre@0:         if (client->connectStatus == HTTP_RECV_BODY_PENDING) {
andre@0:             PKIX_CHECK(callbackList->pollCallback
andre@0:                        (client->socket, NULL, &bytesRead, plContext),
andre@0:                        PKIX_SOCKETPOLLFAILED);
andre@0:         } else {
andre@0:             PKIX_CHECK(callbackList->recvCallback
andre@0:                        (client->socket,
andre@0:                         (void *)&(client->rcvBuf[client->filledupBytes]),
andre@0:                         bytesToRead,
andre@0:                         &bytesRead,
andre@0:                         plContext),
andre@0:                        PKIX_SOCKETRECVFAILED);
andre@0:         }
andre@0: 
andre@0:         /* If bytesRead < 0, an error will be thrown by recvCallback, so
andre@0:          * need to handle >= 0 cases. */
andre@0: 
andre@0:         /* bytesRead == 0 - IO was blocked. */
andre@0:         if (bytesRead == 0) {
andre@0:             client->connectStatus = HTTP_RECV_BODY_PENDING;
andre@0:             *pKeepGoing = PKIX_TRUE;
andre@0:             goto cleanup;
andre@0:         }
andre@0:         
andre@0:         /* We got something. Did we get it all? */
andre@0:         client->filledupBytes += bytesRead;
andre@0: 
andre@0:         /* continue if not enough bytes read or if complete size of
andre@0:          * transfer is unknown */
andre@0:         if (bytesToRead > bytesRead ||
andre@0:             client->rcv_http_data_len == HTTP_UNKNOWN_CONTENT_LENGTH) {
andre@0:             *pKeepGoing = PKIX_TRUE;
andre@0:             goto cleanup;
andre@0:         }
andre@0:         client->connectStatus = HTTP_COMPLETE;
andre@0:         *pKeepGoing = PKIX_FALSE;
andre@0: 
andre@0: cleanup:
andre@0:         if (pkixErrorResult && pkixErrorResult->errCode ==
andre@0:             PKIX_PRRECVREPORTSNETWORKCONNECTIONCLOSED) {
andre@0:             if (client->rcv_http_data_len == HTTP_UNKNOWN_CONTENT_LENGTH) {
andre@0:                 client->rcv_http_data_len = client->filledupBytes;
andre@0:                 client->connectStatus = HTTP_COMPLETE;
andre@0:                 *pKeepGoing = PKIX_FALSE;
andre@0:                 PKIX_DECREF(pkixErrorResult);
andre@0:             } else {
andre@0:                 client->connectStatus = HTTP_ERROR;
andre@0:             }
andre@0:         }
andre@0: 
andre@0:         PKIX_RETURN(HTTPDEFAULTCLIENT);
andre@0: }
andre@0: 
andre@0: /*
andre@0:  * FUNCTION: pkix_pl_HttpDefaultClient_Dispatch
andre@0:  * DESCRIPTION:
andre@0:  *
andre@0:  *  This function is the state machine dispatcher for the HttpDefaultClient
andre@0:  *  pointed to by "client". Results are returned by changes to various fields
andre@0:  *  in the context.
andre@0:  *
andre@0:  * PARAMETERS:
andre@0:  *  "client"
andre@0:  *      The address of the HttpDefaultClient object. Must be non-NULL.
andre@0:  *  "plContext"
andre@0:  *      Platform-specific context pointer.
andre@0:  * THREAD SAFETY:
andre@0:  *  Thread Safe (see Thread Safety Definitions in Programmer's Guide)
andre@0:  * RETURNS:
andre@0:  *  Returns NULL if the function succeeds.
andre@0:  *  Returns a HttpDefaultClient Error if the function fails in a
andre@0:  *      non-fatal way.
andre@0:  *  Returns a Fatal Error if the function fails in an unrecoverable way.
andre@0:  */
andre@0: static PKIX_Error *
andre@0: pkix_pl_HttpDefaultClient_Dispatch(
andre@0:         PKIX_PL_HttpDefaultClient *client,
andre@0:         void *plContext)
andre@0: {
andre@0:         PKIX_UInt32 bytesTransferred = 0;
andre@0:         PKIX_Boolean keepGoing = PKIX_TRUE;
andre@0: 
andre@0:         PKIX_ENTER(HTTPDEFAULTCLIENT, "pkix_pl_HttpDefaultClient_Dispatch");
andre@0:         PKIX_NULLCHECK_ONE(client);
andre@0: 
andre@0:         while (keepGoing) {
andre@0:                 switch (client->connectStatus) {
andre@0:                 case HTTP_CONNECT_PENDING:
andre@0:                     PKIX_CHECK(pkix_pl_HttpDefaultClient_ConnectContinue
andre@0:                         (client, &keepGoing, plContext),
andre@0:                         PKIX_HTTPDEFAULTCLIENTCONNECTCONTINUEFAILED);
andre@0:                     break;
andre@0:                 case HTTP_CONNECTED:
andre@0:                     PKIX_CHECK(pkix_pl_HttpDefaultClient_Send
andre@0:                         (client, &keepGoing, &bytesTransferred, plContext),
andre@0:                         PKIX_HTTPDEFAULTCLIENTSENDFAILED);
andre@0:                     break;
andre@0:                 case HTTP_SEND_PENDING:
andre@0:                     PKIX_CHECK(pkix_pl_HttpDefaultClient_SendContinue
andre@0:                         (client, &keepGoing, &bytesTransferred, plContext),
andre@0:                         PKIX_HTTPDEFAULTCLIENTSENDCONTINUEFAILED);
andre@0:                     break;
andre@0:                 case HTTP_RECV_HDR:
andre@0:                     PKIX_CHECK(pkix_pl_HttpDefaultClient_RecvHdr
andre@0:                         (client, &keepGoing, plContext),
andre@0:                         PKIX_HTTPDEFAULTCLIENTRECVHDRFAILED);
andre@0:                     break;
andre@0:                 case HTTP_RECV_HDR_PENDING:
andre@0:                     PKIX_CHECK(pkix_pl_HttpDefaultClient_RecvHdrContinue
andre@0:                         (client, &keepGoing, plContext),
andre@0:                         PKIX_HTTPDEFAULTCLIENTRECVHDRCONTINUEFAILED);
andre@0:                     break;
andre@0:                 case HTTP_RECV_BODY:
andre@0:                 case HTTP_RECV_BODY_PENDING:
andre@0:                     PKIX_CHECK(pkix_pl_HttpDefaultClient_RecvBody
andre@0:                         (client, &keepGoing, plContext),
andre@0:                         PKIX_HTTPDEFAULTCLIENTRECVBODYFAILED);
andre@0:                     break;
andre@0:                 case HTTP_ERROR:
andre@0:                 case HTTP_COMPLETE:
andre@0:                     keepGoing = PKIX_FALSE;
andre@0:                     break;
andre@0:                 case HTTP_NOT_CONNECTED:
andre@0:                 default:
andre@0:                     PKIX_ERROR(PKIX_HTTPDEFAULTCLIENTINILLEGALSTATE);
andre@0:                 }
andre@0:         }
andre@0: 
andre@0: cleanup:
andre@0: 
andre@0:         PKIX_RETURN(HTTPDEFAULTCLIENT);
andre@0: }
andre@0: 
andre@0: /*
andre@0:  * --HttpClient vtable functions
andre@0:  * See comments in ocspt.h for the function (wrappers) that return SECStatus.
andre@0:  * The functions that return PKIX_Error* are the libpkix implementations.
andre@0:  */
andre@0: 
andre@0: PKIX_Error *
andre@0: pkix_pl_HttpDefaultClient_CreateSession(
andre@0:         const char *host,
andre@0:         PRUint16 portnum,
andre@0:         SEC_HTTP_SERVER_SESSION *pSession,
andre@0:         void *plContext)
andre@0: {
andre@0:         PKIX_PL_HttpDefaultClient *client = NULL;
andre@0: 
andre@0:         PKIX_ENTER
andre@0:                 (HTTPDEFAULTCLIENT, "pkix_pl_HttpDefaultClient_CreateSession");
andre@0:         PKIX_NULLCHECK_TWO(host, pSession);
andre@0: 
andre@0:         PKIX_CHECK(pkix_pl_HttpDefaultClient_Create
andre@0:                 (host, portnum, &client, plContext),
andre@0:                 PKIX_HTTPDEFAULTCLIENTCREATEFAILED);
andre@0: 
andre@0:         *pSession = (SEC_HTTP_SERVER_SESSION)client;
andre@0: 
andre@0: cleanup:
andre@0: 
andre@0:         PKIX_RETURN(HTTPDEFAULTCLIENT);
andre@0: 
andre@0: }
andre@0: 
andre@0: PKIX_Error *
andre@0: pkix_pl_HttpDefaultClient_KeepAliveSession(
andre@0:         SEC_HTTP_SERVER_SESSION session,
andre@0:         PRPollDesc **pPollDesc,
andre@0:         void *plContext)
andre@0: {
andre@0:         PKIX_PL_HttpDefaultClient *client = NULL;
andre@0: 
andre@0:         PKIX_ENTER
andre@0:                 (HTTPDEFAULTCLIENT,
andre@0:                 "pkix_pl_HttpDefaultClient_KeepAliveSession");
andre@0:         PKIX_NULLCHECK_TWO(session, pPollDesc);
andre@0: 
andre@0:         PKIX_CHECK(pkix_CheckType
andre@0:                 ((PKIX_PL_Object *)session,
andre@0:                 PKIX_HTTPDEFAULTCLIENT_TYPE,
andre@0:                 plContext),
andre@0:                 PKIX_SESSIONNOTANHTTPDEFAULTCLIENT);
andre@0: 
andre@0:         client = (PKIX_PL_HttpDefaultClient *)session;
andre@0: 
andre@0:         /* XXX Not implemented */
andre@0: 
andre@0: cleanup:
andre@0: 
andre@0:         PKIX_RETURN(HTTPDEFAULTCLIENT);
andre@0: 
andre@0: }
andre@0: 
andre@0: PKIX_Error *
andre@0: pkix_pl_HttpDefaultClient_RequestCreate(
andre@0:         SEC_HTTP_SERVER_SESSION session,
andre@0:         const char *http_protocol_variant, /* usually "http" */
andre@0:         const char *path_and_query_string,
andre@0:         const char *http_request_method, 
andre@0:         const PRIntervalTime timeout, 
andre@0:         SEC_HTTP_REQUEST_SESSION *pRequest,
andre@0:         void *plContext)
andre@0: {
andre@0:         PKIX_PL_HttpDefaultClient *client = NULL;
andre@0:         PKIX_PL_Socket *socket = NULL;
andre@0:         PKIX_PL_Socket_Callback *callbackList = NULL;
andre@0:         PRFileDesc *fileDesc = NULL;
andre@0:         PRErrorCode status = 0;
andre@0: 
andre@0:         PKIX_ENTER
andre@0:                 (HTTPDEFAULTCLIENT, "pkix_pl_HttpDefaultClient_RequestCreate");
andre@0:         PKIX_NULLCHECK_TWO(session, pRequest);
andre@0: 
andre@0:         PKIX_CHECK(pkix_CheckType
andre@0:                 ((PKIX_PL_Object *)session,
andre@0:                 PKIX_HTTPDEFAULTCLIENT_TYPE,
andre@0:                 plContext),
andre@0:                 PKIX_SESSIONNOTANHTTPDEFAULTCLIENT);
andre@0: 
andre@0:         client = (PKIX_PL_HttpDefaultClient *)session;
andre@0: 
andre@0:         /* We only know how to do http */
andre@0:         if (PORT_Strncasecmp(http_protocol_variant, "http", 4) != 0) {
andre@0:                 PKIX_ERROR(PKIX_UNRECOGNIZEDPROTOCOLREQUESTED);
andre@0:         }
andre@0: 
andre@0:         if (PORT_Strncasecmp(http_request_method, "POST", 4) == 0) {
andre@0:                 client->send_http_method = HTTP_POST_METHOD;
andre@0:         } else if (PORT_Strncasecmp(http_request_method, "GET", 3) == 0) {
andre@0:                 client->send_http_method = HTTP_GET_METHOD;
andre@0:         } else {
andre@0:                 /* We only know how to do POST and GET */
andre@0:                 PKIX_ERROR(PKIX_UNRECOGNIZEDREQUESTMETHOD);
andre@0:         }
andre@0: 
andre@0:         if (path_and_query_string) {
andre@0:             /* "path_and_query_string" is a parsing result by CERT_GetURL
andre@0:              * function that adds "end of line" to the value. OK to dup
andre@0:              * the string. */
andre@0:             client->path = PORT_Strdup(path_and_query_string);
andre@0:             if (!client->path) {
andre@0:                 PKIX_ERROR(PKIX_ALLOCERROR);
andre@0:             }
andre@0:         }
andre@0: 
andre@0:         client->timeout = timeout;
andre@0: 
andre@0: #if 0
andre@0: 	PKIX_CHECK(pkix_HttpCertStore_FindSocketConnection
andre@0:                 (timeout,
andre@0:                 "variation.red.iplanet.com", /* (char *)client->host, */
andre@0:                 2001,   /* client->portnum, */
andre@0:                 &status,
andre@0:                 &socket,
andre@0:                 plContext),
andre@0: 		PKIX_HTTPCERTSTOREFINDSOCKETCONNECTIONFAILED);
andre@0: #else
andre@0: 	PKIX_CHECK(pkix_HttpCertStore_FindSocketConnection
andre@0:                 (timeout,
andre@0:                 (char *)client->host,
andre@0:                 client->portnum,
andre@0:                 &status,
andre@0:                 &socket,
andre@0:                 plContext),
andre@0: 		PKIX_HTTPCERTSTOREFINDSOCKETCONNECTIONFAILED);
andre@0: #endif
andre@0: 
andre@0:         client->socket = socket;
andre@0: 
andre@0:         PKIX_CHECK(pkix_pl_Socket_GetCallbackList
andre@0:                 (socket, &callbackList, plContext),
andre@0:                 PKIX_SOCKETGETCALLBACKLISTFAILED);
andre@0: 
andre@0:         client->callbackList = (void *)callbackList;
andre@0: 
andre@0:         PKIX_CHECK(pkix_pl_Socket_GetPRFileDesc
andre@0:                 (socket, &fileDesc, plContext),
andre@0:                 PKIX_SOCKETGETPRFILEDESCFAILED);
andre@0: 
andre@0:         client->pollDesc.fd = fileDesc;
andre@0:         client->pollDesc.in_flags = 0;
andre@0:         client->pollDesc.out_flags = 0;
andre@0: 
andre@0:         client->send_http_data = NULL;
andre@0:         client->send_http_data_len = 0;
andre@0:         client->send_http_content_type = NULL;
andre@0: 
andre@0:         client->connectStatus =
andre@0:                  ((status == 0) ? HTTP_CONNECTED : HTTP_CONNECT_PENDING);
andre@0: 
andre@0:         /* Request object is the same object as Session object */
andre@0:         PKIX_INCREF(client);
andre@0:         *pRequest = client;
andre@0: 
andre@0: cleanup:
andre@0: 
andre@0:         PKIX_RETURN(HTTPDEFAULTCLIENT);
andre@0: 
andre@0: }
andre@0: 
andre@0: PKIX_Error *
andre@0: pkix_pl_HttpDefaultClient_SetPostData(
andre@0:         SEC_HTTP_REQUEST_SESSION request,
andre@0:         const char *http_data, 
andre@0:         const PRUint32 http_data_len,
andre@0:         const char *http_content_type,
andre@0:         void *plContext)
andre@0: {
andre@0:         PKIX_PL_HttpDefaultClient *client = NULL;
andre@0: 
andre@0:         PKIX_ENTER
andre@0:                 (HTTPDEFAULTCLIENT,
andre@0:                 "pkix_pl_HttpDefaultClient_SetPostData");
andre@0:         PKIX_NULLCHECK_ONE(request);
andre@0: 
andre@0:         PKIX_CHECK(pkix_CheckType
andre@0:                 ((PKIX_PL_Object *)request,
andre@0:                 PKIX_HTTPDEFAULTCLIENT_TYPE,
andre@0:                 plContext),
andre@0:                 PKIX_REQUESTNOTANHTTPDEFAULTCLIENT);
andre@0: 
andre@0:         client = (PKIX_PL_HttpDefaultClient *)request;
andre@0: 
andre@0:         client->send_http_data = http_data;
andre@0:         client->send_http_data_len = http_data_len;
andre@0:         client->send_http_content_type = http_content_type;
andre@0: 
andre@0:         /* Caller is allowed to give NULL or empty string for content_type */
andre@0:         if ((client->send_http_content_type == NULL) ||
andre@0:             (*(client->send_http_content_type) == '\0')) {
andre@0:                 client->send_http_content_type = "application/ocsp-request";
andre@0:         }
andre@0: 
andre@0: cleanup:
andre@0: 
andre@0:         PKIX_RETURN(HTTPDEFAULTCLIENT);
andre@0: 
andre@0: }
andre@0: 
andre@0: PKIX_Error *
andre@0: pkix_pl_HttpDefaultClient_TrySendAndReceive(
andre@0:         SEC_HTTP_REQUEST_SESSION request,
andre@0:         PRUint16 *http_response_code, 
andre@0:         const char **http_response_content_type, 
andre@0:         const char **http_response_headers, 
andre@0:         const char **http_response_data, 
andre@0:         PRUint32 *http_response_data_len, 
andre@0:         PRPollDesc **pPollDesc,
andre@0:         SECStatus *pSECReturn,
andre@0:         void *plContext)        
andre@0: {
andre@0:         PKIX_PL_HttpDefaultClient *client = NULL;
andre@0:         PKIX_UInt32 postLen = 0;
andre@0:         PRPollDesc *pollDesc = NULL;
andre@0:         char *sendbuf = NULL;
andre@0:         char portstr[16];
andre@0: 
andre@0:         PKIX_ENTER
andre@0:                 (HTTPDEFAULTCLIENT,
andre@0:                 "pkix_pl_HttpDefaultClient_TrySendAndReceive");
andre@0: 
andre@0:         PKIX_NULLCHECK_ONE(request);
andre@0: 
andre@0:         PKIX_CHECK(pkix_CheckType
andre@0:                 ((PKIX_PL_Object *)request,
andre@0:                 PKIX_HTTPDEFAULTCLIENT_TYPE,
andre@0:                 plContext),
andre@0:                 PKIX_REQUESTNOTANHTTPDEFAULTCLIENT);
andre@0: 
andre@0:         client = (PKIX_PL_HttpDefaultClient *)request;
andre@0: 
andre@0:         if (!pPollDesc && client->timeout == 0) {
andre@0:             PKIX_ERROR_FATAL(PKIX_NULLARGUMENT);
andre@0:         }
andre@0: 
andre@0:         if (pPollDesc) {
andre@0:             pollDesc = *pPollDesc;
andre@0:         }
andre@0: 
andre@0:         /* if not continuing from an earlier WOULDBLOCK return... */
andre@0:         if (pollDesc == NULL) {
andre@0: 
andre@0:                 if (!((client->connectStatus == HTTP_CONNECTED) ||
andre@0:                      (client->connectStatus == HTTP_CONNECT_PENDING))) {
andre@0:                         PKIX_ERROR(PKIX_HTTPCLIENTININVALIDSTATE);
andre@0:                 }
andre@0: 
andre@0:                 /* Did caller provide a value for response length? */
andre@0:                 if (http_response_data_len != NULL) {
andre@0:                         client->pRcv_http_data_len = http_response_data_len;
andre@0:                         client->maxResponseLen = *http_response_data_len;
andre@0:                 }
andre@0: 
andre@0:                 client->rcv_http_response_code = http_response_code;
andre@0:                 client->rcv_http_content_type = http_response_content_type;
andre@0:                 client->rcv_http_headers = http_response_headers;
andre@0:                 client->rcv_http_data = http_response_data;
andre@0: 
andre@0:                 /* prepare the message */
andre@0:                 portstr[0] = '\0';
andre@0:                 if (client->portnum != 80) {
andre@0:                         PR_snprintf(portstr, sizeof(portstr), ":%d", 
andre@0:                                     client->portnum);
andre@0:                 }
andre@0:                 
andre@0:                 if (client->send_http_method == HTTP_POST_METHOD) {
andre@0:                         sendbuf = PR_smprintf
andre@0:                             ("POST %s HTTP/1.0\r\nHost: %s%s\r\n"
andre@0:                             "Content-Type: %s\r\nContent-Length: %u\r\n\r\n",
andre@0:                             client->path,
andre@0:                             client->host,
andre@0:                             portstr,
andre@0:                             client->send_http_content_type,
andre@0:                             client->send_http_data_len);
andre@0:                         postLen = PORT_Strlen(sendbuf);
andre@0:                             
andre@0:                         client->POSTLen = postLen + client->send_http_data_len;
andre@0: 
andre@0:                         /* allocate postBuffer big enough for header + data */
andre@0:                         PKIX_CHECK(PKIX_PL_Malloc
andre@0:                                 (client->POSTLen,
andre@0:                                 (void **)&(client->POSTBuf),
andre@0:                                 plContext),
andre@0:                                 PKIX_MALLOCFAILED);
andre@0: 
andre@0:                         /* copy header into postBuffer */
andre@0:                         PORT_Memcpy(client->POSTBuf, sendbuf, postLen);
andre@0: 
andre@0:                         /* append data after header */
andre@0:                         PORT_Memcpy(&client->POSTBuf[postLen],
andre@0:                                     client->send_http_data,
andre@0:                                     client->send_http_data_len);
andre@0: 
andre@0:                         /* PR_smprintf_free original header buffer */
andre@0:                         PR_smprintf_free(sendbuf);
andre@0:                         sendbuf = NULL;
andre@0:                         
andre@0:                 } else if (client->send_http_method == HTTP_GET_METHOD) {
andre@0:                         client->GETBuf = PR_smprintf
andre@0:                             ("GET %s HTTP/1.0\r\nHost: %s%s\r\n\r\n",
andre@0:                             client->path,
andre@0:                             client->host,
andre@0:                             portstr);
andre@0:                         client->GETLen = PORT_Strlen(client->GETBuf);
andre@0:                 }
andre@0: 
andre@0:         }
andre@0: 
andre@0:         /* continue according to state */
andre@0:         PKIX_CHECK(pkix_pl_HttpDefaultClient_Dispatch(client, plContext),
andre@0:                 PKIX_HTTPDEFAULTCLIENTDISPATCHFAILED);
andre@0: 
andre@0:         switch (client->connectStatus) {
andre@0:                 case HTTP_CONNECT_PENDING:
andre@0:                 case HTTP_SEND_PENDING:
andre@0:                 case HTTP_RECV_HDR_PENDING:
andre@0:                 case HTTP_RECV_BODY_PENDING:
andre@0:                         pollDesc = &(client->pollDesc);
andre@0:                         *pSECReturn = SECWouldBlock;
andre@0:                         break;
andre@0:                 case HTTP_ERROR:
andre@0:                         /* Did caller provide a pointer for length? */
andre@0:                         if (client->pRcv_http_data_len != NULL) {
andre@0:                                 /* Was error "response too big?" */
andre@0:                                 if (client->rcv_http_data_len !=
andre@0:                                             HTTP_UNKNOWN_CONTENT_LENGTH &&
andre@0:                                     client->maxResponseLen >=
andre@0:                                                 client->rcv_http_data_len) {
andre@0:                                         /* Yes, report needed space */
andre@0:                                         *(client->pRcv_http_data_len) =
andre@0:                                                  client->rcv_http_data_len;
andre@0:                                 } else {
andre@0:                                         /* No, report problem other than size */
andre@0:                                         *(client->pRcv_http_data_len) = 0;
andre@0:                                 }
andre@0:                         }
andre@0: 
andre@0:                         pollDesc = NULL;
andre@0:                         *pSECReturn = SECFailure;
andre@0:                         break;
andre@0:                 case HTTP_COMPLETE:
andre@0:                         *(client->rcv_http_response_code) =
andre@0:                                 client->responseCode;
andre@0:                         if (client->pRcv_http_data_len != NULL) {
andre@0:                                 *http_response_data_len =
andre@0:                                          client->rcv_http_data_len;
andre@0:                         }
andre@0:                         if (client->rcv_http_data != NULL) {
andre@0:                                 *(client->rcv_http_data) = client->rcvBuf;
andre@0:                         }
andre@0:                         pollDesc = NULL;
andre@0:                         *pSECReturn = SECSuccess;
andre@0:                         break;
andre@0:                 case HTTP_NOT_CONNECTED:
andre@0:                 case HTTP_CONNECTED:
andre@0:                 case HTTP_RECV_HDR:
andre@0:                 case HTTP_RECV_BODY:
andre@0:                 default:
andre@0:                         pollDesc = NULL;
andre@0:                         *pSECReturn = SECFailure;
andre@0:                         PKIX_ERROR(PKIX_HTTPCLIENTININVALIDSTATE);
andre@0:                         break;
andre@0:         }
andre@0: 
andre@0:         if (pPollDesc) {
andre@0:             *pPollDesc = pollDesc;
andre@0:         }
andre@0: 
andre@0: cleanup:
andre@0:         if (sendbuf) {
andre@0:             PR_smprintf_free(sendbuf);
andre@0:         }
andre@0: 
andre@0:         PKIX_RETURN(HTTPDEFAULTCLIENT);
andre@0: 
andre@0: }
andre@0: 
andre@0: PKIX_Error *
andre@0: pkix_pl_HttpDefaultClient_Cancel(
andre@0:         SEC_HTTP_REQUEST_SESSION request,
andre@0:         void *plContext)
andre@0: {
andre@0:         PKIX_PL_HttpDefaultClient *client = NULL;
andre@0: 
andre@0:         PKIX_ENTER(HTTPDEFAULTCLIENT, "pkix_pl_HttpDefaultClient_Cancel");
andre@0:         PKIX_NULLCHECK_ONE(request);
andre@0: 
andre@0:         PKIX_CHECK(pkix_CheckType
andre@0:                 ((PKIX_PL_Object *)request,
andre@0:                 PKIX_HTTPDEFAULTCLIENT_TYPE,
andre@0:                 plContext),
andre@0:                 PKIX_REQUESTNOTANHTTPDEFAULTCLIENT);
andre@0: 
andre@0:         client = (PKIX_PL_HttpDefaultClient *)request;
andre@0: 
andre@0:         /* XXX Not implemented */
andre@0: 
andre@0: cleanup:
andre@0: 
andre@0:         PKIX_RETURN(HTTPDEFAULTCLIENT);
andre@0: 
andre@0: }
andre@0: 
andre@0: SECStatus
andre@0: pkix_pl_HttpDefaultClient_CreateSessionFcn(
andre@0:         const char *host,
andre@0:         PRUint16 portnum,
andre@0:         SEC_HTTP_SERVER_SESSION *pSession)
andre@0: {
andre@0:         PKIX_Error *err = pkix_pl_HttpDefaultClient_CreateSession
andre@0:                 (host, portnum, pSession, plContext);
andre@0: 
andre@0:         if (err) {
andre@0:                 PKIX_PL_Object_DecRef((PKIX_PL_Object *)err, plContext);
andre@0:                 return SECFailure;
andre@0:         }
andre@0:         return SECSuccess;
andre@0: }
andre@0: 
andre@0: SECStatus
andre@0: pkix_pl_HttpDefaultClient_KeepAliveSessionFcn(
andre@0:         SEC_HTTP_SERVER_SESSION session,
andre@0:         PRPollDesc **pPollDesc)
andre@0: {
andre@0:         PKIX_Error *err = pkix_pl_HttpDefaultClient_KeepAliveSession
andre@0:                 (session, pPollDesc, plContext);
andre@0: 
andre@0:         if (err) {
andre@0:                 PKIX_PL_Object_DecRef((PKIX_PL_Object *)err, plContext);
andre@0:                 return SECFailure;
andre@0:         }
andre@0:         return SECSuccess;
andre@0: }
andre@0: 
andre@0: SECStatus
andre@0: pkix_pl_HttpDefaultClient_FreeSessionFcn(
andre@0:         SEC_HTTP_SERVER_SESSION session)
andre@0: {
andre@0:         PKIX_Error *err =
andre@0:             PKIX_PL_Object_DecRef((PKIX_PL_Object *)(session), plContext);
andre@0: 
andre@0:         if (err) {
andre@0:                 PKIX_PL_Object_DecRef((PKIX_PL_Object *)err, plContext);
andre@0:                 return SECFailure;
andre@0:         }
andre@0:         return SECSuccess;
andre@0: }
andre@0: 
andre@0: SECStatus
andre@0: pkix_pl_HttpDefaultClient_RequestCreateFcn(
andre@0:         SEC_HTTP_SERVER_SESSION session,
andre@0:         const char *http_protocol_variant, /* usually "http" */
andre@0:         const char *path_and_query_string,
andre@0:         const char *http_request_method, 
andre@0:         const PRIntervalTime timeout, 
andre@0:         SEC_HTTP_REQUEST_SESSION *pRequest)
andre@0: {
andre@0:         PKIX_Error *err = pkix_pl_HttpDefaultClient_RequestCreate
andre@0:                 (session,
andre@0:                 http_protocol_variant,
andre@0:                 path_and_query_string,
andre@0:                 http_request_method,
andre@0:                 timeout,
andre@0:                 pRequest,
andre@0:                 plContext);
andre@0: 
andre@0:         if (err) {
andre@0:                 PKIX_PL_Object_DecRef((PKIX_PL_Object *)err, plContext);
andre@0:                 return SECFailure;
andre@0:         }
andre@0:         return SECSuccess;
andre@0: }
andre@0: 
andre@0: SECStatus
andre@0: pkix_pl_HttpDefaultClient_SetPostDataFcn(
andre@0:         SEC_HTTP_REQUEST_SESSION request,
andre@0:         const char *http_data, 
andre@0:         const PRUint32 http_data_len,
andre@0:         const char *http_content_type)
andre@0: {
andre@0:         PKIX_Error *err =
andre@0:             pkix_pl_HttpDefaultClient_SetPostData(request, http_data,
andre@0:                                                   http_data_len,
andre@0:                                                   http_content_type,
andre@0:                                                   plContext);
andre@0:         if (err) {
andre@0:                 PKIX_PL_Object_DecRef((PKIX_PL_Object *)err, plContext);
andre@0:                 return SECFailure;
andre@0:         }
andre@0:         return SECSuccess;
andre@0: }
andre@0: 
andre@0: SECStatus
andre@0: pkix_pl_HttpDefaultClient_AddHeaderFcn(
andre@0:         SEC_HTTP_REQUEST_SESSION request,
andre@0:         const char *http_header_name, 
andre@0:         const char *http_header_value)
andre@0: {
andre@0:         /* Not supported */
andre@0:         return SECFailure;
andre@0: }
andre@0: 
andre@0: SECStatus
andre@0: pkix_pl_HttpDefaultClient_TrySendAndReceiveFcn(
andre@0:         SEC_HTTP_REQUEST_SESSION request,
andre@0:         PRPollDesc **pPollDesc,
andre@0:         PRUint16 *http_response_code, 
andre@0:         const char **http_response_content_type, 
andre@0:         const char **http_response_headers, 
andre@0:         const char **http_response_data, 
andre@0:         PRUint32 *http_response_data_len) 
andre@0: {
andre@0:         SECStatus rv = SECFailure;
andre@0: 
andre@0:         PKIX_Error *err = pkix_pl_HttpDefaultClient_TrySendAndReceive
andre@0:                 (request,
andre@0:                 http_response_code, 
andre@0:                 http_response_content_type, 
andre@0:                 http_response_headers, 
andre@0:                 http_response_data, 
andre@0:                 http_response_data_len,
andre@0:                 pPollDesc,
andre@0:                 &rv,
andre@0:                 plContext);
andre@0: 
andre@0:         if (err) {
andre@0:                 PKIX_PL_Object_DecRef((PKIX_PL_Object *)err, plContext);
andre@0:                 return rv;
andre@0:         }
andre@0:         return SECSuccess;
andre@0: }
andre@0: 
andre@0: SECStatus
andre@0: pkix_pl_HttpDefaultClient_CancelFcn(
andre@0:         SEC_HTTP_REQUEST_SESSION request)
andre@0: {
andre@0:         PKIX_Error *err = pkix_pl_HttpDefaultClient_Cancel(request, plContext);
andre@0: 
andre@0:         if (err) {
andre@0:                 PKIX_PL_Object_DecRef((PKIX_PL_Object *)err, plContext);
andre@0:                 return SECFailure;
andre@0:         }
andre@0:         return SECSuccess;
andre@0: }
andre@0: 
andre@0: SECStatus
andre@0: pkix_pl_HttpDefaultClient_FreeFcn(
andre@0:         SEC_HTTP_REQUEST_SESSION request)
andre@0: {
andre@0:         PKIX_Error *err =
andre@0:             PKIX_PL_Object_DecRef((PKIX_PL_Object *)(request), plContext);
andre@0: 
andre@0:         if (err) {
andre@0:                 PKIX_PL_Object_DecRef((PKIX_PL_Object *)err, plContext);
andre@0:                 return SECFailure;
andre@0:         }
andre@0:         return SECSuccess;
andre@0: }