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