📄 minihttp.c
字号:
numbytes = PR_Read(fl, buf, 256); if (numbytes <= 0) break; /* done reading */ frv = PR_Write(req->sock, buf, numbytes); PR_ASSERT(numbytes == frv); } if (numbytes == 0) rv = SSM_SUCCESS; done: if (fl) PR_Close(fl); if (path) PR_Free(path); return rv;}/* Based on the file requested, do some function within PSM. ### mwelch This NEEDS to be changed to use a string-keyed hash table, so that people adding code can add their own functionality independently of this module. */static SSMStatushttp_dispatch(HTTPRequest *req){ SSMStatus rv = SSM_SUCCESS; SSMCommandHandlerFunc handlerFunc = NULL; /* Look up the command handler in the registry. */ rv = SSM_HTTPFindCommandHandler(req->handlerName, &handlerFunc); if (rv != SSM_SUCCESS) { SSM_HTTPReportSpecificError(req, "Could not find handler for command '%s' (error %d).", req->handlerName, rv); } if (!handlerFunc) { /* Attempt to fetch a file from the doc directory. */ rv = http_spool_file(req); if (rv != SSM_SUCCESS) handlerFunc = SSM_HTTPDefaultCommandHandler; /* fallback */ else req->sentResponse = PR_TRUE; } if (handlerFunc) rv = (*handlerFunc)(req); return rv;}static voidhttp_find_target(HTTPRequest *req){ SSMResource *target = NULL; char *targetID = NULL; SSMStatus rv; /* Attempt to find a target resource. If one is not available, use the authenticated control connection. */ rv = SSM_HTTPParamValue(req, "target", &targetID); if (rv == SSM_SUCCESS){ rv = SSM_RIDTextToResource(req, targetID, &target); } else if (req->referer != NULL){ /* See if the referer had a target. If it did, then use it's * target as this request's target. */ char *refTarget = strstr(req->referer, "target"); if (refTarget != NULL) { refTarget = strchr(refTarget, '='); if (refTarget != NULL) { char ridText[20]; char *nextParam; refTarget++; nextParam = strchr(refTarget, '&'); if (nextParam != NULL) { memcpy(ridText, refTarget, nextParam-refTarget); ridText[nextParam-refTarget] = '\0'; } else { memcpy(ridText, refTarget, strlen(refTarget)+1); } SSM_RIDTextToResource(req, ridText, &target); } } } /* * SSM_RIDTextToResource gets a reference for us, so there's no * need to get another one here. */ req->target = target;}/* Take care of an incoming request. */static voidhttp_handle_request(void *arg){ SSMStatus rv = SSM_SUCCESS; HTTPRequest *req; PRFileDesc *sock = (PRFileDesc *) arg; char *name = NULL; SSM_RegisterThread("http server", NULL); SSM_DEBUG("Responding to request.\n"); /* Read in the request. */ if ((rv = http_read_request(sock, &req)) != SSM_SUCCESS) goto loser;#ifdef DEBUG name = PR_smprintf("http server %lx", (long) req); if (name) SSM_RegisterThread(name, NULL);#endif /* Parse the buffer, get interesting bits */ if ((rv = http_parse_request(req)) != SSM_SUCCESS) goto loser; /* Attempt to authenticate the connection. */ if ((rv = http_authenticate(req)) != SSM_SUCCESS) goto loser; /* Find the target object, if one has been explicitly specified. */ http_find_target(req);#ifdef XP_MAC /* Test pattern for now. */ rv = SSM_HTTPSendUTF8String(req, "Hi, I got all of your request.\r\n"); rv = SSM_HTTPSendUTF8String(req, req->rawreqbuf); goto done;#endif /* Do the right thing depending on what has been requested. */ if ((rv = http_dispatch(req)) != SSM_SUCCESS) goto loser; goto done; loser: if (rv == SSM_SUCCESS) rv = SSM_FAILURE;done: if (req && !req->sentResponse) { /* An error occurred along the way. Send the appropriate error response, or an internal server error if nothing was set along the way. */ if (req->httprv <= HTTP_OK) req->httprv = HTTP_NOT_IMPLEMENTED; SSM_HTTPReportError(req, req->httprv); } if (req) http_request_destroy(req); else SSM_DEBUG("Would destroy request, but it doesn't exist!\n"); SSM_DEBUG("Closing, status %ld.\n", rv);#ifdef DEBUG PR_Free(name);#endif }/* SSM_HTTPThread listens to the HTTP socket for connections from one or more clients. (arg) should be an open socket (PRFileDesc*), therefore the port needs to have been created before being passed to this thread. */void SSM_HTTPThread(void *arg){ PRFileDesc *listenSock = (PRFileDesc *) arg; PRFileDesc *acceptSock = NULL; PRNetAddr clientaddr; PRBool alive = PR_TRUE; SSMStatus rv = SSM_SUCCESS; SSM_RegisterThread("http listener", NULL); /* Wait for connections. */ while(alive) { SSM_DEBUG("Ready for connection on port %ld.\n", (long) httpListenPort); acceptSock = PR_Accept(listenSock, &clientaddr, PR_INTERVAL_NO_TIMEOUT); if (acceptSock) { PRThread *handler; handler = PR_CreateThread(PR_USER_THREAD, http_handle_request, (void *)acceptSock, PR_PRIORITY_NORMAL, PR_LOCAL_THREAD, PR_UNJOINABLE_THREAD, 0); if (!handler) { SSM_DEBUG("Couldn't create handler for new socket %p.", acceptSock); alive = PR_FALSE; } } else alive = PR_FALSE; } rv = PR_GetError(); /* Close down in an orderly fashion. */#ifdef DEBUG SSM_DEBUG("http: ** Closing, status = %d **\n", rv);#endif }SSMStatusSSM_HTTPSendUTF8String(HTTPRequest *req, char *str){ PRInt32 len; PRInt32 numSent; SSMStatus rv = SSM_FAILURE; /* Extract the text from the Unicode string and send it. */ if (str) { len = (PRInt32)PL_strlen(str); numSent = PR_Send(req->sock, str, len, 0, PR_INTERVAL_NO_TIMEOUT); if (numSent == len) rv = SSM_SUCCESS; else { SSM_DEBUG("Error %d trying to send unicode string.\n", PR_GetError()); } } return rv;}SSMStatusSSM_RIDTextToResource(HTTPRequest *req, const char *ridText, SSMResource **res){ SSMResourceID rid = 0; SSMStatus rv = PR_FAILURE; *res = NULL; /* in case we fail */ /* scan the text for a number. */ PR_sscanf(ridText, "%ld", &rid); /* search for the resource in the authenticated control connection. */ if (req->ctrlconn) { rv = SSMControlConnection_GetResource(req->ctrlconn, rid, res); } else { SSMControlConnection *ctrl; rv = SSM_GetControlConnection(rid, &ctrl); *res = &ctrl->super.super; } return ((SSMStatus) rv);}SSMStatusSSM_HTTPReportError(HTTPRequest *req, HTTPErrorCode statusCode){ char key[256]; char *name = NULL, *hdrs = NULL, *desc = NULL; char *tmpl = NULL, *out = NULL; char *errmsg = req->errormsg; char *noSpecificErr = NULL; SSMStatus rv = SSM_SUCCESS; SSMTextGenContext *cx=NULL; if (statusCode == HTTP_OK) goto done; rv = SSMTextGen_NewTopLevelContext(req, &cx); if (rv != SSM_SUCCESS) goto loser; /* Grab "http_error_%d_name", "http_error_%d_hdrs", and "http_error_%d_desc" for the specific text. */ PR_snprintf(key, 256, "http_error_%d_name", statusCode); rv = SSM_GetAndExpandText(cx, key, &name); if (rv != SSM_SUCCESS) goto loser; if (statusCode != HTTP_NO_CONTENT) { PR_snprintf(key, 256, "http_error_%d_hdrs", statusCode); rv = SSM_GetAndExpandText(cx, key, &hdrs); if (rv != SSM_SUCCESS) goto loser; PR_snprintf(key, 256, "http_error_%d_desc", statusCode); rv = SSM_GetAndExpandText(cx, key, &desc); if (rv != SSM_SUCCESS) goto loser; } else { desc = ""; hdrs = ""; } /* Now that we have the strings to insert, get the header template. */ rv = SSM_GetAndExpandText(cx, "http_error_header_template", &tmpl); if (rv != SSM_SUCCESS) goto loser; /* If no specific error message was reported, report generically. */ if (!errmsg) { rv = SSM_GetAndExpandText(cx, "http_error_no_spec_err", &noSpecificErr); if (rv != SSM_SUCCESS) { errmsg = ""; } else { errmsg = noSpecificErr; } } out = PR_smprintf(tmpl, (long)statusCode, name, hdrs, desc, errmsg); /* Send the result string. */ rv = SSM_HTTPSendUTF8String(req, out); PR_Free(tmpl); tmpl = NULL; /* So we don't free it twice. */ if (rv != SSM_SUCCESS) goto loser; /* Empty the string in preparation for the next part of the page */ SSMTextGen_UTF8StringClear(&out); if (statusCode != HTTP_NO_CONTENT) { /* Get and send the appropriate description. */ /* Format with the same arguments as before. */ rv = SSM_GetAndExpandText(cx, "http_error_content", &tmpl); if (rv != SSM_SUCCESS) goto loser; out = PR_smprintf(tmpl, statusCode, name, hdrs, desc, errmsg); rv = SSM_HTTPSendUTF8String(req, out); if (rv != SSM_SUCCESS) goto loser; } req->sentResponse = PR_TRUE; goto done; loser: if (rv == SSM_SUCCESS) rv = SSM_FAILURE; done: PR_FREEIF(name); if (statusCode != HTTP_NO_CONTENT) { PR_FREEIF(desc); PR_FREEIF(hdrs); } PR_FREEIF(tmpl); PR_FREEIF(noSpecificErr); PR_FREEIF(out); if (cx != NULL) { SSMTextGen_DestroyContext(cx); } return rv;}SSMStatusSSM_HTTPSendOKHeader(HTTPRequest *req, char *addtlHeaders, char *contentType){ char *tmpl = NULL, *out = NULL; char *hdrs = addtlHeaders, *type = contentType; SSMStatus rv = SSM_SUCCESS; SSMTextGenContext *cx; req->httprv = HTTP_OK; /* Create a textgen context so that we can process the templates. */ rv = SSMTextGen_NewTopLevelContext(req, &cx); if (rv != SSM_SUCCESS) goto loser; /* Get the header template. */ rv = SSM_GetAndExpandText(cx, "http_header_template", &tmpl); if (rv != SSM_SUCCESS) goto loser; /* prevent null headers from interfering with content-type header */ if (hdrs == NULL) hdrs = ""; if (type == NULL) type = "text/html"; out = PR_smprintf(tmpl, hdrs, type); /* Send the result string. */ rv = SSM_HTTPSendUTF8String(req, out); goto done; loser: if (rv == SSM_SUCCESS) rv = SSM_FAILURE; done: if (tmpl) PR_Free(tmpl); if (out) PR_Free(out); if (cx) SSMTextGen_DestroyContext(cx); return rv;}/* Simple Hello World command handler. I've deliberately left Unicode out of this example, in order to illustrate the use of command handlers in isolation. Most of the time, you will not even need to explicitly send anything out on the HTTP connection. Most of the time, you will probably want to write simple handlers that perform specific actions inside PSM (such as making changes to the cert/key db, performing some operation such as form signing, etc), then sending back a generic response by calling SSM_HTTPDefaultCommandHandler. This should even be true for special content that you construct programmatically, such as cert lists; this is because you will want to write keyword handlers (see nlsutil.[ch]) to perform such processing inline. See SSM_HTTPMonitorResourceHandler for an example of the kind of handler you will typically want to write. (I've tried to comment that routine extensively so that you will get an idea of what it is doing.) */SSMStatusSSM_HTTPHelloWorld(HTTPRequest *req){ size_t sent; char outbuf[256]; /* Send HTTP headers first. req->sock is an NSPR socket on which we send back the response. Note: Normally you would not send things back down the raw socket. If you want to send something
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -