Ejemplo n.º 1
0
/* 	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);
}
Ejemplo n.º 2
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();
}
Ejemplo n.º 3
0
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(&currentTime)); // 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;
}
Ejemplo n.º 5
0
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;
	}
}
Ejemplo n.º 6
0
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;
}
Ejemplo n.º 7
0
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;
}
Ejemplo n.º 8
0
  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");
  }
Ejemplo n.º 9
0
  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());
  }
Ejemplo n.º 10
0
  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;
  }
Ejemplo n.º 11
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++;
	}
}