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 **)©, 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: }