/* handles requests sent by connection.c and tells connection.c to forward another request void * sockfd : socket of the client */ void handle(void *sockfd){ char buffer[256]; int clientSockfd = *(int*)sockfd; int n, serverSockfd; char WebServerName[50]; char WebServerPortNum[50]; bzero(buffer,256); uint clilen; struct sockaddr_in cli_addr; clilen = sizeof(cli_addr); /* Read */ n = read(clientSockfd,buffer,255); getWebServerName(buffer, WebServerName, WebServerPortNum); /* Send request to web server */ serverSockfd = connectServer(WebServerName, WebServerPortNum); // The given host name cannot be found // Send erroe message to the client side if (serverSockfd < 0){ char buffer[256]; bzero(buffer,256); int cn; strcat(buffer,"No such host\r\n\r\n"); cn = write(clientSockfd,buffer,strlen(buffer)); logRequest(clientSockfd, 0, WebServerName); close(clientSockfd); pthread_exit(0); } sendRequest(WebServerName, WebServerPortNum, serverSockfd); /* Return */ int size = getAndSendReturn(clientSockfd, serverSockfd); logRequest(clientSockfd, size, WebServerName); close(clientSockfd); pthread_exit(0); }
void HttpReqImpl::finish() { if (!outputClosed) { //otherwise finalize chunk finishChunk(); } if (!method.empty()) { TimeStamp reqEndTime = TimeStamp::now(); natural reqTime = (reqEndTime - reqBeginTime).getMilis(); logRequest(reqTime); if (reportDuration) this->recordRequestDuration(reqTime); } clear(); }
static int readClientRequest(int fd) { FD *f; Buf *buf; f = table[fd]; buf = readAvailableBytes(fd); send(f->writeFD, current(buf), inputLength(buf), 0); if (!logRequest(f, buf)) { bufFree(buf); } return 0; }
int httpReply(int fd, FILE **fpp, int code, char *message, char *type, char *content, char *modifiedTime, int contentLength, struct logInformation *logInfo) { FILE *outputStream = fdopen(fd, "w"); int bytes = 0; if (outputStream != NULL) { bytes = fprintf(outputStream, "HTTP/1.0 %d %s\r\n", code, message); time_t currentTime = time(0); char *timeString = asctime(gmtime(¤tTime)); // get the Greenwich Mean Time char *timeStringPointer = timeString; // step through the time string until we find the newline character and // change it to a null character to remove it while (*timeStringPointer != '\n') { timeStringPointer++; } *timeStringPointer = '\0'; bytes += fprintf(outputStream, "Date: %s\r\n", timeString); bytes += fprintf(outputStream, "Server: %s\r\n", SERVER_NAME); if (modifiedTime != NULL) { bytes += fprintf(outputStream, "Last-Modified: %s\r\n", modifiedTime); } bytes += fprintf(outputStream, "Content-Type: %s\r\n", type); bytes += fprintf(outputStream, "Content-Length: %d\r\n\r\n", contentLength); if (content) bytes += fprintf(outputStream, "%s\r\n", content); } fflush(outputStream); // log the request logInfo->status = code; logInfo->sizeOfResponse = contentLength; logRequest(logInfo); // if fpp is not null then close the stream // otherwise return the stream so someone else can use it later if (fpp) *fpp = outputStream; else fclose(outputStream); return bytes; }
void HttpReqImpl::sendHeaders() { if (bHeaderSent) return; if (bNeedContinue) { remainPostData = 0; chunkedPost = false; } bNeedContinue = false; bool hasServer = false; bool hasContentType = false; bool hasTransfEnc = false; bool hasConnection = false; bool hasLength = false; bool hasDate = false; static ConstStrA contentTypeKey = getHeaderFieldName(fldContentType); static ConstStrA serverKey = getHeaderFieldName(fldServer); static ConstStrA transfEnc = getHeaderFieldName(fldTransferEncoding); static ConstStrA connectionStr = getHeaderFieldName(fldConnection); static ConstStrA contenLenStr = getHeaderFieldName(fldContentLength); static ConstStrA dateStr = getHeaderFieldName(fldDate); ConstStrA statusMsgStr = this->statusMsg; if (statusMsgStr.empty()) statusMsgStr = getStatusMessage(statusCode); PrintTextA print(*inout); print.setNL("\r\n"); print("HTTP/%1.%2 %3 %4\n") << httpMajVer << httpMinVer << statusCode << statusMsgStr; if (statusCode == 101) { hasTransfEnc = hasConnection = hasContentType = true; useChunked = false; remainPostData = naturalNull; switchedProtocol = true; TimeStamp reqEndTime = TimeStamp::now(); natural reqTime = (reqEndTime - reqBeginTime).getMilis(); logRequest(reqTime); } for (HeaderMap::Iterator iter = responseHdrs.getFwIter(); iter.hasItems();) { const HeaderMap::Entity hdrPair = iter.getNext(); if (!hasContentType && hdrPair.key == contentTypeKey) hasContentType = true; if (!hasServer && hdrPair.key == serverKey) hasServer = true; if (!hasTransfEnc && hdrPair.key == transfEnc) hasTransfEnc = !(useChunked && hdrPair.value == ConstStrA("chunked")); if (!hasConnection && hdrPair.key == connectionStr) hasConnection = true; if (!hasDate && hdrPair.key == dateStr) hasDate = true; if (!hasLength && hdrPair.key == contenLenStr) { hasLength = true; } print("%1: %2\n") << ConstStrA(hdrPair.key) << ConstStrA(hdrPair.value); } if (!hasContentType) print("%1: %2\n") << contentTypeKey << "text/html;charset=UTF-8"; if (!hasServer) print("%1: %2\n") << serverKey << serverIdent; if (hasLength) { useChunked = false; } if (!hasDate) { TimeStamp::RFC1123Time datenow = TimeStamp::now().asRFC1123Time(); print("%1: %2\n") << dateStr << ConstStrA(datenow); } if (!hasTransfEnc && useChunked && !closeConn) print("%1: %2\n") << transfEnc << "chunked"; else useChunked = false; if (!hasConnection && closeConn) print("%1: %2\n") << connectionStr << "close"; print("\n"); /* LogObject(THISLOCATION).progress("%7 - %3 %4 HTTP/%1.%2 %5 %6") << httpMajVer << httpMajVer << ConstStrA(method) << ConstStrA(path) << statusCode << statusMsgStr << getIfc<IHttpPeerInfo>().getPeerRealAddr();*/ responseHdrs.clear(); //for code 100 or 101, additional header will be next if (statusCode == 100) { //set status code 200 to simply processing reply (handler don't need to reset 100 status statusCode = 200; //unset message this->statusMsg = HdrStr(); //now, handler can exit function with status 100 - when data arrives, onData will be called } else { //header sent, prevent sending new headers bHeaderSent = true; } }
void *worker_thread(void * id) { struct request_bundle bundle; int thread_id = *((int*)id); int requests_handled = 0; int cache_hit; const char* error; fprintf(stderr, "worker_thread: Starting up\n"); while (global_exit == 0) { fprintf(stderr, "worker_thread: Iterating\n"); pthread_mutex_lock(&queue_mutex); fprintf(stderr, "worker_thread: Queue lock acquired\n"); assert (queue_size >= 0); if (queue_size == 0) { fprintf(stderr, "worker_thread: Queue empty, waiting for condition 'queue get' and releasing queue lock\n"); pthread_cond_wait(&queue_get_cond, &queue_mutex); fprintf(stderr, "worker_thread: Condition 'queue get' fufilled, queue lock reacquired\n"); } pthread_mutex_lock(&cache_mutex); fprintf(stderr, "worker_thread: Cache lock acquired\n"); assert ( getRequest != NULL ); bundle = (*getRequest)(); fprintf(stderr, "worker_thread: Got request for connection %d with filename %s\n", bundle.req->fd, bundle.req->filename); pthread_cond_signal(&queue_put_cond); pthread_mutex_unlock(&queue_mutex); error = process_request(&bundle, &cache_hit); fprintf(stderr, "worker_thread: Done processing request\n"); ++requests_handled; pthread_mutex_lock(&log_mutex); logRequest(bundle, thread_id, requests_handled, cache_hit, error); pthread_mutex_unlock(&log_mutex); pthread_mutex_unlock(&cache_mutex); // bundle.ent should NOT be used after this point if (prefetch_size < max_prefetch_size) { pthread_mutex_lock(&prefetch_mutex); prefetch_putRequest(bundle.req); pthread_cond_signal(&prefetch_get_cond); pthread_mutex_unlock(&prefetch_mutex); } else { destroyRequest(bundle.req); } } fprintf(stderr, "worker_thread: Should exit, starting to empty request queue\n"); while (queue_size > 0) { pthread_mutex_lock(&queue_mutex); pthread_mutex_lock(&cache_mutex); bundle = (*getRequest)(); pthread_mutex_unlock(&queue_mutex); error = process_request(&bundle, &cache_hit); pthread_mutex_unlock(&cache_mutex); ++requests_handled; pthread_mutex_lock(&log_mutex); logRequest(bundle, thread_id, requests_handled, cache_hit, error); pthread_mutex_unlock(&log_mutex); destroyRequest(bundle.req); } fprintf(stderr, "worker_thread: Shutting down\n"); return NULL; }
struct Request* parseRequest(char* requestTxt){ // Allocates a new Request struct and populates it with the contents of the request char httpMethod[BUFSIZE]; char httpResource[BUFSIZE]; char httpHeader[BUFSIZE]; if (isLogDebug()){ logMsg(LOG_DEBUG, "Request: %s", requestTxt); } // Extract the HTTP method and resource sscanf(requestTxt, "%s %s HTTP%*4c %2056c", httpMethod, httpResource, httpHeader); struct Request* request = malloc(sizeof(struct Request)); char* path; char* paramPair; char* paramName; char* paramValue; char* paramEquals; char* httpResourceCopy = strdupa(httpResource); int paramNameLen; request->method = strdup(httpMethod); request->params = NULL; request->headers = NULL; /* We parse the httpResource value to extract the parameters, if any. Do this on a copy because strtok alters the contents of the string. */ path = strtok(httpResourceCopy, "?"); if (path == NULL){ // No parameters request->path = strdup(httpResourceCopy); } else { request->path = strdup(path); // Split the path into 'name=value' parts while(1){ paramPair = strtok(NULL, "&"); if (paramPair == NULL){ // Reached the end of the parameter list break; } else { paramEquals = strchr(paramPair, '='); if (paramEquals == NULL){ // There was no '=' sign in this part, so ignore it continue; } else { // Get the part before the '=' character paramNameLen = paramEquals - paramPair; paramName = malloc(paramNameLen + 1); strncpy(paramName, paramPair, paramNameLen + 1); paramName[paramNameLen] = 0; if (strcmp(paramEquals, "=") == 0){ // No value was supplied - this is valid in certain cases (eg RSS hostname update from prefs page) paramValue = strdup(""); } else { paramValue = strdup(paramEquals + 1); } } char* unescapedValue = unescapeValue(paramValue); struct NameValuePair* param = makeNameValuePair(paramName, unescapedValue); appendNameValuePair(&(request->params), param); // These are all malloced above, and copied by makeNameValuePair(), so free them here free(unescapedValue); free(paramName); free(paramValue); } } } char* headerName; char* headerValue; char* headers = strtok(requestTxt,HTTP_EOL); // Extract the headers one at a time, and store them while(1){ headerName = strtok(NULL, ": "); headerValue = strtok(NULL, HTTP_EOL); if( headerName == NULL || headerValue == NULL ){ break; } else { // Strip any leading whitespace while( *headerName == ' ' || *headerName == '\n' || *headerName == '\r' ) headerName++; while( *headerValue == ' ' ) headerValue++; struct NameValuePair* header = makeNameValuePair(headerName, headerValue); appendNameValuePair(&(request->headers), header); } } if (isLogInfo()){ logRequest(request); } return request; }
void Worker::run() { threadId = pthread_self(); Jobqueue& queue = application.getQueue(); log_debug("start thread " << threadId); while (queue.getWaitThreadCount() < application.getMinThreads()) { state = stateWaitingForJob; Jobqueue::JobPtr j = queue.get(); if (Tntnet::shouldStop()) { // put job back to queue to wake up next worker if any left queue.put(j); break; } try { std::iostream& socket = j->getStream(); if (Tntnet::shouldStop()) break; bool keepAlive; do { time(&lastWaitTime); keepAlive = false; state = stateParsing; try { j->getParser().parse(socket); state = statePostParsing; if (socket.eof()) log_debug("eof"); else if (j->getParser().failed()) { state = stateSendError; log_warn("bad request"); tnt::HttpReply errorReply(socket); errorReply.setVersion(1, 0); errorReply.setContentType("text/html"); errorReply.setKeepAliveCounter(0); errorReply.out() << "<html><body><h1>Error</h1><p>bad request</p></body></html>\n"; errorReply.sendReply(400, "Bad Request"); logRequest(j->getRequest(), errorReply, 400); } else if (socket.fail()) log_debug("socket failed"); else { j->getRequest().doPostParse(); j->setWrite(); keepAlive = processRequest(j->getRequest(), socket, j->decrementKeepAliveCounter()); if (keepAlive) { j->setRead(); j->clear(); if (!socket.rdbuf()->in_avail()) { if (queue.getWaitThreadCount() == 0 && !queue.empty()) { // if there is something to do and no threads waiting, we take // the next job just to improve responsiveness. log_debug("put job back into queue"); queue.put(j, true); keepAlive = false; } else { struct pollfd fd; fd.fd = j->getFd(); fd.events = POLLIN; if (::poll(&fd, 1, TntConfig::it().socketReadTimeout) == 0) { log_debug("pass job to poll-thread"); application.getPoller().addIdleJob(j); keepAlive = false; } } } } } } catch (const HttpError& e) { keepAlive = false; state = stateSendError; log_warn("http-Error: " << e.what()); HttpReply errorReply(socket); errorReply.setVersion(1, 0); errorReply.setKeepAliveCounter(0); for (HttpMessage::header_type::const_iterator it = e.header_begin(); it != e.header_end(); ++it) errorReply.setHeader(it->first, it->second); errorReply.out() << e.getBody() << '\n'; errorReply.sendReply(e.getErrcode(), e.getErrmsg()); logRequest(j->getRequest(), errorReply, e.getErrcode()); } } while (keepAlive); } catch (const cxxtools::IOTimeout& e) { application.getPoller().addIdleJob(j); } catch (const cxxtools::net::AcceptTerminated&) { log_debug("listener terminated"); break; } catch (const std::exception& e) { log_warn("unexpected exception: " << e.what()); } } time(&lastWaitTime); state = stateStopping; cxxtools::MutexLock lock(mutex); workers.erase(this); log_debug("end worker thread " << threadId << " - " << workers.size() << " threads left - " << application.getQueue().getWaitThreadCount() << " waiting threads"); }
void Worker::dispatch(HttpRequest& request, HttpReply& reply) { state = stateDispatch; const std::string& url = request.getUrl(); if (!HttpRequest::checkUrl(url)) { log_info("illegal url <" << url << '>'); throw HttpError(HTTP_BAD_REQUEST, "illegal url"); } request.setThreadContext(this); Dispatcher::PosType pos(application.getDispatcher(), request); while (true) { state = stateDispatch; // pos.getNext() throws NotFoundException at end Maptarget ci = pos.getNext(); try { Component* comp = 0; try { if (ci.libname == application.getAppName()) { // if the libname is the app name look first, if the component is // linked directly try { Compident cii = ci; cii.libname = std::string(); comp = &comploader.fetchComp(cii, application.getDispatcher()); } catch (const NotFoundException&) { // if the component is not found in the binary, fetchComp throws // NotFoundException and comp remains 0. // so we can ignore the exceptioni and just continue } } if (comp == 0) comp = &comploader.fetchComp(ci, application.getDispatcher()); } catch (const NotFoundException& e) { log_debug("NotFoundException catched - url " << e.getUrl() << " try next mapping"); continue; } request.setPathInfo(ci.hasPathInfo() ? ci.getPathInfo() : url); request.setArgs(ci.getArgs()); std::string appname = application.getAppName().empty() ? ci.libname : application.getAppName(); application.getScopemanager().preCall(request, appname); state = stateProcessingRequest; unsigned http_return; const char* http_msg; std::string msg; try { http_return = comp->topCall(request, reply, request.getQueryParams()); http_msg = HttpReturn::httpMessage(http_return); } catch (const HttpReturn& e) { http_return = e.getReturnCode(); msg = e.getMessage(); http_msg = msg.c_str(); } if (http_return != DECLINED) { if (reply.isDirectMode()) { log_info("request " << request.getMethod_cstr() << ' ' << request.getQuery() << " ready, returncode " << http_return << ' ' << http_msg); state = stateFlush; reply.out().flush(); } else { log_info("request " << request.getMethod_cstr() << ' ' << request.getQuery() << " ready, returncode " << http_return << ' ' << http_msg << " - ContentSize: " << reply.getContentSize()); application.getScopemanager().postCall(request, reply, appname); state = stateSendReply; reply.sendReply(http_return, http_msg); } logRequest(request, reply, http_return); if (reply.out()) log_debug("reply sent"); else { reply.setKeepAliveCounter(0); log_warn("sending failed"); } return; } else log_debug("component " << ci << " returned DECLINED"); } catch (const LibraryNotFound& e) { log_warn("library " << e.getLibname() << " not found"); } } throw NotFoundException(request.getUrl()); }
bool Worker::processRequest(HttpRequest& request, std::iostream& socket, unsigned keepAliveCount) { // log message log_info("request " << request.getMethod_cstr() << ' ' << request.getQuery() << " from client " << request.getPeerIp() << " user-Agent \"" << request.getUserAgent() << "\" user \"" << request.getUsername() << '"'); // create reply-object HttpReply reply(socket); reply.setVersion(request.getMajorVersion(), request.getMinorVersion()); if (request.isMethodHEAD()) reply.setHeadRequest(); #ifdef ENABLE_LOCALE reply.setLocale(request.getLocale()); #endif if (request.keepAlive()) reply.setKeepAliveCounter(keepAliveCount); if (TntConfig::it().enableCompression) reply.setAcceptEncoding(request.getEncoding()); // process request try { try { dispatch(request, reply); if (!request.keepAlive() || !reply.keepAlive()) keepAliveCount = 0; if (keepAliveCount > 0) log_debug("keep alive"); else { log_debug("no keep alive request/reply=" << request.keepAlive() << '/' << reply.keepAlive()); } } catch (const HttpError& e) { throw; } catch (const std::exception& e) { throw HttpError(HTTP_INTERNAL_SERVER_ERROR, e.what()); } catch (...) { log_error("unknown exception"); throw HttpError(HTTP_INTERNAL_SERVER_ERROR, "unknown error"); } } catch (const HttpError& e) { state = stateSendError; log_warn("http-Error: " << e.what()); HttpReply errorReply(socket); errorReply.setVersion(request.getMajorVersion(), request.getMinorVersion()); if (request.keepAlive()) errorReply.setKeepAliveCounter(keepAliveCount); else keepAliveCount = 0; for (HttpMessage::header_type::const_iterator it = e.header_begin(); it != e.header_end(); ++it) errorReply.setHeader(it->first, it->second); errorReply.out() << e.getBody() << '\n'; errorReply.sendReply(e.getErrcode(), e.getErrmsg()); logRequest(request, errorReply, e.getErrcode()); } return keepAliveCount > 0; }
/* parseIncoming() will parse all incoming headers. It is important to note that * at this point all headers are discarded and only the request line is * reviewed. */ void parseIncoming(int sock, struct http_request *req) { struct tm iftime; char c; char line[BUF_SIZE]; char *method_tok, *path_tok, *http_ver_tok; int i=0, line_no=0; /* default status, if there is a problem it will be changed */ req->status=MSG_OK; for (;;) { bzero(line,sizeof(line)); i=0; /* get the request, character by character */ while ( recv(sock,&c,1,0)>0 ) { line[i] = c; /* Check for a consecutive CRLF, signifying end of the header */ if ( (i==1) && (line_no>0) ) { if ( (line[i-1]=='\r') && (line[i]=='\n') ) { return; } } /* Check for end of line */ if ( (i>0) && (line[i-1]=='\r') && (line[i]=='\n') ) { break; } i++; /* don't let the request exceed the buffer size */ if (i>BUF_SIZE) { req->status = MSG_BAD_REQ; break; } } /* This section will parse the request line */ if (line_no==0) { /* remove CRLF */ line[strlen(line)-1]='\0'; line[strlen(line)-1]='\0'; logTime(req); logRequest(req,line); /* Find out the method */ method_tok=strtok(line," "); if (method_tok==NULL) { req->status = MSG_BAD_REQ; break; } else if (strcmp(method_tok,"GET")==0) { req->method=MTHD_GET; } else if (strcmp(method_tok,"HEAD")==0) { req->method=MTHD_HEAD; } else if (strcmp(method_tok,"POST")==0) { req->method=MTHD_POST; } else { req->method=-1; } /* Find out the path of the requested file */ path_tok=strtok(NULL," "); strcpy(req->path,path_tok); /* Find out if the request is full or simple */ http_ver_tok=strtok(NULL," "); if ( http_ver_tok==NULL ) { req->flag_simple=1; } else if ( (strcmp(http_ver_tok,"HTTP/1.0")==0) || (strcmp(http_ver_tok,"HTTP/1.1")==0) ) { /* default, so do nothing */ } else { req->status = MSG_BAD_REQ; } /* There should be nothing else on this line */ if ( strtok(NULL," ") != NULL ) { req->status = MSG_BAD_REQ; } } /*end of request line parsing*/ if (line_no>0) { if (strncmp( "If-Modified-Since", line, strlen("If-Modified-Since")) ==0 ) { memset(&iftime, 0, sizeof(struct tm)); if ( (strptime( line+strlen("If-Modified-Since: XXX, "), "%d %b %Y %H:%M:%S %z", &iftime))==NULL ) { warn("unable to parse time from request header"); } else { req->flag_if_mod=1; req->if_time = mktime(&iftime); } } } line_no++; } }