| /* 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. |
| * "plCtx" |
| * 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 *plCtx) |
| { |
| 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 **)©, plCtx), |
| 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, plCtx), |
| 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, plCtx), |
| 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. |
| * "plCtx" |
| * 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 *plCtx) |
| { |
| 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, |
| plCtx), |
| 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 plCtx argument in its |
| * function calls. Save it here. |
| */ |
| client->plContext = plCtx; |
| |
| *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 *plCtx) |
| { |
| PKIX_PL_HttpDefaultClient *client = NULL; |
| |
| PKIX_ENTER(HTTPDEFAULTCLIENT, "pkix_pl_HttpDefaultClient_Destroy"); |
| PKIX_NULLCHECK_ONE(object); |
| |
| PKIX_CHECK(pkix_CheckType |
| (object, PKIX_HTTPDEFAULTCLIENT_TYPE, plCtx), |
| PKIX_OBJECTNOTANHTTPDEFAULTCLIENT); |
| |
| client = (PKIX_PL_HttpDefaultClient *)object; |
| |
| if (client->rcvHeaders) { |
| PKIX_PL_Free(client->rcvHeaders, plCtx); |
| 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, plCtx); |
| client->POSTBuf = NULL; |
| } |
| if (client->rcvBuf != NULL) { |
| PKIX_PL_Free(client->rcvBuf, plCtx); |
| 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 *plCtx) |
| { |
| 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. |
| * "plCtx" |
| * 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 *plCtx) |
| { |
| 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, plCtx), |
| 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. |
| * "plCtx" |
| * 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 *plCtx) |
| { |
| 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, |
| plCtx), |
| 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. |
| * "plCtx" |
| * 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 *plCtx) |
| { |
| 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, plCtx), |
| 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. |
| * "plCtx" |
| * 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 *plCtx) |
| { |
| 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), |
| plCtx), |
| 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, |
| plCtx), |
| PKIX_SOCKETRECVFAILED); |
| |
| if (bytesRead > 0) { |
| /* client->filledupBytes will be adjusted by |
| * pkix_pl_HttpDefaultClient_HdrCheckComplete */ |
| PKIX_CHECK( |
| pkix_pl_HttpDefaultClient_HdrCheckComplete(client, bytesRead, |
| pKeepGoing, |
| plCtx), |
| 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. |
| * "plCtx" |
| * 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 *plCtx) |
| { |
| 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, plCtx), |
| PKIX_SOCKETPOLLFAILED); |
| |
| if (bytesRead > 0) { |
| client->filledupBytes += bytesRead; |
| |
| PKIX_CHECK(pkix_pl_HttpDefaultClient_HdrCheckComplete |
| (client, bytesRead, pKeepGoing, plCtx), |
| 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. |
| * "plCtx" |
| * 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 *plCtx) |
| { |
| 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, plCtx), |
| 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, plCtx), |
| PKIX_SOCKETPOLLFAILED); |
| } else { |
| PKIX_CHECK(callbackList->recvCallback |
| (client->socket, |
| (void *)&(client->rcvBuf[client->filledupBytes]), |
| bytesToRead, |
| &bytesRead, |
| plCtx), |
| 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. |
| * "plCtx" |
| * 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 *plCtx) |
| { |
| 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, plCtx), |
| PKIX_HTTPDEFAULTCLIENTCONNECTCONTINUEFAILED); |
| break; |
| case HTTP_CONNECTED: |
| PKIX_CHECK(pkix_pl_HttpDefaultClient_Send |
| (client, &keepGoing, &bytesTransferred, plCtx), |
| PKIX_HTTPDEFAULTCLIENTSENDFAILED); |
| break; |
| case HTTP_SEND_PENDING: |
| PKIX_CHECK(pkix_pl_HttpDefaultClient_SendContinue |
| (client, &keepGoing, &bytesTransferred, plCtx), |
| PKIX_HTTPDEFAULTCLIENTSENDCONTINUEFAILED); |
| break; |
| case HTTP_RECV_HDR: |
| PKIX_CHECK(pkix_pl_HttpDefaultClient_RecvHdr |
| (client, &keepGoing, plCtx), |
| PKIX_HTTPDEFAULTCLIENTRECVHDRFAILED); |
| break; |
| case HTTP_RECV_HDR_PENDING: |
| PKIX_CHECK(pkix_pl_HttpDefaultClient_RecvHdrContinue |
| (client, &keepGoing, plCtx), |
| PKIX_HTTPDEFAULTCLIENTRECVHDRCONTINUEFAILED); |
| break; |
| case HTTP_RECV_BODY: |
| case HTTP_RECV_BODY_PENDING: |
| PKIX_CHECK(pkix_pl_HttpDefaultClient_RecvBody |
| (client, &keepGoing, plCtx), |
| 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 *plCtx) |
| { |
| 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, plCtx), |
| 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 *plCtx) |
| { |
| PKIX_ENTER |
| (HTTPDEFAULTCLIENT, |
| "pkix_pl_HttpDefaultClient_KeepAliveSession"); |
| PKIX_NULLCHECK_TWO(session, pPollDesc); |
| |
| PKIX_CHECK(pkix_CheckType |
| ((PKIX_PL_Object *)session, |
| PKIX_HTTPDEFAULTCLIENT_TYPE, |
| plCtx), |
| 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 *plCtx) |
| { |
| 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, |
| plCtx), |
| 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, |
| plCtx), |
| PKIX_HTTPCERTSTOREFINDSOCKETCONNECTIONFAILED); |
| #else |
| PKIX_CHECK(pkix_HttpCertStore_FindSocketConnection |
| (timeout, |
| (char *)client->host, |
| client->portnum, |
| &status, |
| &socket, |
| plCtx), |
| PKIX_HTTPCERTSTOREFINDSOCKETCONNECTIONFAILED); |
| #endif |
| |
| client->socket = socket; |
| |
| PKIX_CHECK(pkix_pl_Socket_GetCallbackList |
| (socket, &callbackList, plCtx), |
| PKIX_SOCKETGETCALLBACKLISTFAILED); |
| |
| client->callbackList = (void *)callbackList; |
| |
| PKIX_CHECK(pkix_pl_Socket_GetPRFileDesc |
| (socket, &fileDesc, plCtx), |
| 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 *plCtx) |
| { |
| 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, |
| plCtx), |
| 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 *plCtx) |
| { |
| 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, |
| plCtx), |
| 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), |
| plCtx), |
| 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, plCtx), |
| 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 *plCtx) |
| { |
| PKIX_ENTER(HTTPDEFAULTCLIENT, "pkix_pl_HttpDefaultClient_Cancel"); |
| PKIX_NULLCHECK_ONE(request); |
| |
| PKIX_CHECK(pkix_CheckType |
| ((PKIX_PL_Object *)request, |
| PKIX_HTTPDEFAULTCLIENT_TYPE, |
| plCtx), |
| 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; |
| } |