Пример #1
0
bool CHttpServer::parse_request(String &method, String &uri, String &query, HeaderList &headers, String &body )
{
    method.clear();
    uri.clear();
    headers.clear();
    body.clear();

    size_t s = 0;
    ByteVector request;

    bool parsing_headers = true;
    size_t content_length = 0;

    for (;;) {
        if (!receive_request(request))
            return false;

        size_t lim = request.size();
        while (parsing_headers) {
            size_t e;
            for(e = s; e < lim && request[e] != '\r'; ++e);
            if (e >= lim - 1) {
                // Incomplete line, will read further
                break;
            }
            if (request[e + 1] != '\n') {
                if (verbose) RAWLOG_ERROR("Wrong request syntax, line should ends by '\\r\\n'");
                return false;
            }
            
            String line(&request[s], e - s);
            s = e + 2;
            
            if (line.empty()) {
                parsing_headers = false;
                break;
            }
            
            if (uri.empty()) {
                // Parse start line
                if (!parse_startline(line, method, uri, query) || uri.empty())
                    return false;
            }
            else {
                Header hdr;
                if (!parse_header(line, hdr) || hdr.name.empty())
                    return false;
                headers.push_back(hdr);
                
                String low;
                std::transform(hdr.name.begin(), hdr.name.end(), std::back_inserter(low), &::tolower);
                if (low == "content-length") {
                    content_length = ::atoi(hdr.value.c_str());
                }
            }
        }

        if (!parsing_headers) {
            if (content_length == 0)
                return true;

            if (lim - s < content_length)
                continue;

            body.assign(&request[s], &request[s] + content_length);
            return true;
        }
    }
}
Пример #2
0
bool CHttpServer::receive_request(ByteVector &request)
{
	if (verbose) RAWTRACE("Receiving request...");

	ByteVector r;
    char buf[BUF_SIZE];
    int attempts = 0;
    for(;;) {
        if (verbose) RAWTRACE("Read portion of data from socket...");
        int n = recv(m_sock, &buf[0], sizeof(buf), 0);
        //RAWTRACE1("RECV: %d", n);
        if (n == -1) {
            int e = RHO_NET_ERROR_CODE;
            if (verbose) RAWTRACE1("RECV ERROR: %d", e);
#if !defined(WINDOWS_PLATFORM)
            if (e == EINTR)
                continue;
#else
			if (e == WSAEINTR)
				continue;
#endif

#if defined(OS_WP8) || (defined(RHODES_QT_PLATFORM) && defined(OS_WINDOWS_DESKTOP)) || defined(OS_WINCE)
            if (e == EAGAIN || e == WSAEWOULDBLOCK) {
#else
            if (e == EAGAIN) {
#endif
                if (!r.empty())
                    break;
                
                if(++attempts > (HTTP_EAGAIN_TIMEOUT*10))
                {
                    if (verbose) RAWLOG_ERROR("Error when receiving data from socket. Client does not send data for " HTTP_EAGAIN_TIMEOUT_STR " sec. Cancel recieve.");
                    return false;
                }

                fd_set fds;
                FD_ZERO(&fds);
                FD_SET(m_sock, &fds);
                timeval tv = {0};
				tv.tv_usec = 100000;//100 MS
                select(m_sock + 1, &fds, 0, 0, &tv);
                continue;
            }
            
            if (verbose) RAWLOG_ERROR1("Error when receiving data from socket: %d", e);
            return false;
        }
        
        if (n == 0) {
            if(!r.empty()) {
                if (verbose) RAWTRACE("Client closed connection gracefully");
                break;
            } else {
                if (verbose) RAWLOG_ERROR("Connection gracefully closed before we receive any data");
                return false;
            }
        } else {
            if (verbose) RAWTRACE1("Actually read %d bytes", n);
            r.insert(r.end(), &buf[0], &buf[0] + n);
        }
    }
    
    if (!r.empty()) {
        request.insert(request.end(), r.begin(), r.end());
        if ( !rho_conf_getBool("log_skip_post") ) {
            String strRequest(request.begin(),request.end());
            if (verbose) RAWTRACE1("Received request:\n%s", strRequest.c_str());
        }
    }
    return true;
}

bool CHttpServer::send_response_impl(String const &data, bool continuation)
{
#ifdef OS_MACOSX
    if ( m_localResponseWriter != 0 ) {
      m_localResponseWriter->writeResponse( data );
      return true;
    }
#endif

    if (verbose) {
        if (continuation)
            if (verbose) RAWTRACE("Send continuation data...");
        else
            if (verbose) RAWTRACE("Sending response...");
    }
    
    // First of all, make socket blocking
#if defined(WINDOWS_PLATFORM)
    unsigned long optval = 0;
        if(::ioctlsocket(m_sock, FIONBIO, &optval) == SOCKET_ERROR) {
        RAWLOG_ERROR1("Can not set blocking socket mode: %d", RHO_NET_ERROR_CODE);
        return false;
    }
#else
    int flags = fcntl(m_sock, F_GETFL);
    if (flags == -1) {
        if (verbose) RAWLOG_ERROR1("Can not get current socket mode: %d", errno);
        return false;
    }
    if (fcntl(m_sock, F_SETFL, flags & ~O_NONBLOCK) == -1) {
        if (verbose) RAWLOG_ERROR1("Can not set blocking socket mode: %d", errno);
        return false;
    }
#endif
    
    size_t pos = 0;
    for(; pos < data.size();) {
        int n = send(m_sock, data.c_str() + pos, data.size() - pos, 0);
        if (n == -1) {
            int e = RHO_NET_ERROR_CODE;
#if !defined(WINDOWS_PLATFORM)
            if (e == EINTR)
                continue;
#endif
            
            if (verbose) RAWLOG_ERROR1("Can not send response data: %d", e);
            return false;
        }
        
        if (n == 0)
            break;
        
        pos += n;
    }
    
    //String dbg_response = response.size() > 100 ? response.substr(0, 100) : response;
    //RAWTRACE2("Sent response:\n%s%s", dbg_response.c_str(), response.size() > 100 ? "..." : "   ");
    if (continuation) {
        if (verbose) RAWTRACE1("Sent response body: %d bytes", data.size());
    }
    else if ( !rho_conf_getBool("log_skip_post") ) {
        if (verbose) RAWTRACE1("Sent response (only headers displayed):\n%s", data.c_str());
    }

    return true;
}

bool CHttpServer::send_response(String const &response, bool redirect)
{
#ifdef OS_ANDROID
    if (redirect) {
        CAutoPtr<IRhoThreadImpl> ptrThread = rho_get_RhoClassFactory()->createThreadImpl();
        ptrThread->sleep(20);
    }
#endif
    return send_response_impl(response, false);
}

String CHttpServer::create_response(String const &reason)
{
    return create_response(reason, "");
}

String CHttpServer::create_response(String const &reason, HeaderList const &headers)
{
    return create_response(reason, headers, "");
}

String CHttpServer::create_response(String const &reason, String const &body)
{
    return create_response(reason, HeaderList(), body);
}

String CHttpServer::create_response(String const &reason, HeaderList const &hdrs, String const &body)
{
    String response = "HTTP/1.1 ";
    response += reason;
    response += "\r\n";
    
    char buf[50];
    snprintf(buf, sizeof(buf), "%d", m_port);
    
    HeaderList headers;
    headers.push_back(Header("Host", String("127.0.0.1:") + buf));
    headers.push_back(Header("Connection", "close"));
    headers.push_back(HttpHeader("Access-Control-Allow-Origin", "*"));
    std::copy(hdrs.begin(), hdrs.end(), std::back_inserter(headers));
    
    for(HeaderList::const_iterator it = headers.begin(), lim = headers.end();
        it != lim; ++it) {
        response += it->name;
        response += ": ";
        response += it->value;
        response += "\r\n";
    }
    
    response += "\r\n";
    
    response += body;
    
    return response;
}
Пример #3
0
bool CHttpServer::decide(String const &method, String const &arg_uri, String const &query,
                         HeaderList const &headers, String const &body/*, IResponseSender& respSender*/ )
{
    if (verbose) RAWTRACE1("Decide what to do with uri %s", arg_uri.c_str());
    callback_t callback = registered(arg_uri);
    if (callback) {
        if (verbose) RAWTRACE1("Uri %s is registered callback, so handle it appropriately", arg_uri.c_str());

        if ( callback == rho_http_ruby_proc_callback )
            call_ruby_proc( query, body );
        else
            callback(this, query.length() ? query : body);

        return false;
    }

    String uri = arg_uri;

    String fullPath = CFilePath::join(m_root, uri);
#ifndef RHO_NO_RUBY_API    
    if (rho_ruby_is_started())
    {
        Route route;
        if (dispatch(uri, route)) {
            if (verbose) RAWTRACE1("Uri %s is correct route, so enable MVC logic", uri.c_str());
            
            VALUE req = create_request_hash(route.application, route.model, route.action, route.id,
                                            method, uri, query, headers, body);
            VALUE data = callFramework(req);
            String reply(getStringFromValue(data), getStringLenFromValue(data));
            rho_ruby_releaseValue(data);

            bool isRedirect = String_startsWith(reply, "HTTP/1.1 301") ||
                              String_startsWith(reply, "HTTP/1.1 302");

            if (!send_response(reply, isRedirect))
                return false;

            if (method == "GET")
                rho_rhodesapp_keeplastvisitedurl(uri.c_str());

		    if ( sync::RhoconnectClientManager::haveRhoconnectClientImpl() ) {

			    if (!route.id.empty()) {
				    sync::RhoconnectClientManager::rho_sync_addobjectnotify_bysrcname(route.model.c_str(), route.id.c_str());
			    }			
		    }
            
            return true;
        }
        
        if (isdir(fullPath)) {
            if (verbose) RAWTRACE1("Uri %s is directory, redirecting to index", uri.c_str());
            String q = query.empty() ? "" : "?" + query;
            
            HeaderList headers;
            headers.push_back(Header("Location", CFilePath::join( uri, "index" RHO_ERB_EXT) + q));
            
            send_response(create_response("301 Moved Permanently", headers), true);
            return false;
        }

        if (isindex(uri)) {
            if (!isfile(fullPath)) {
                if (verbose) RAWLOG_ERROR1("The file %s was not found", fullPath.c_str());
                String error = "<!DOCTYPE html><html><font size=\"+4\"><h2>404 Not Found.</h2> The file " + uri + " was not found.</font></html>";
                send_response(create_response("404 Not Found",error));
                return false;
            }
            
            if (verbose) RAWTRACE1("Uri %s is index file, call serveIndex", uri.c_str());

            VALUE req = create_request_hash(route.application, route.model, route.action, route.id,
                                            method, uri, query, headers, body);

            VALUE data = callServeIndex((char *)fullPath.c_str(), req);
            String reply(getStringFromValue(data), getStringLenFromValue(data));
            rho_ruby_releaseValue(data);

            if (!send_response(reply))
                return false;

            if (method == "GET")
                rho_rhodesapp_keeplastvisitedurl(uri.c_str());

            return true;
        }
    }
#endif
    // Try to send requested file
    if (verbose) RAWTRACE1("Uri %s should be regular file, trying to send it", uri.c_str());

    PROF_START("READ_FILE");
    bool bRes = send_file(uri, headers);
    PROF_STOP("READ_FILE");

    return bRes;
}
Пример #4
0
bool CHttpServer::send_file(String const &path, HeaderList const &hdrs)
{
    String fullPath = CFilePath::normalizePath(path);

    if (String_startsWith(fullPath,"/app/db/db-files") )
        fullPath = CFilePath::join( rho_native_rhodbpath(), path.substr(4) );
    else if (fullPath.find(m_root) != 0 && fullPath.find(m_strRhoRoot) != 0 && fullPath.find(m_strRuntimeRoot) != 0 && fullPath.find(m_userroot) != 0 && fullPath.find(m_strRhoUserRoot) != 0)
        fullPath = CFilePath::join( m_root, path );
	
    struct stat st;
    bool bCheckExist = true;
#ifdef RHODES_EMULATOR
    String strPlatform = RHOSIMCONF().getString("platform");
    if ( strPlatform.length() > 0 )
    {
        String fullPath1 = fullPath;
        int nDot = fullPath1.rfind('.');
        if ( nDot >= 0 )
            fullPath1.insert(nDot, String(".") + strPlatform);
        else
            fullPath1 += String(".") + strPlatform;

        if (stat(fullPath1.c_str(), &st) == 0 && S_ISREG(st.st_mode))
        {
            fullPath = fullPath1;
            bCheckExist = false;
        }
    }

#endif

    bool doesNotExists = bCheckExist && (stat(fullPath.c_str(), &st) != 0 || !S_ISREG(st.st_mode));
    if ( doesNotExists ) {
        // looking for files at 'rho/apps' at runtime folder
        fullPath = CFilePath::join( m_strRuntimeRoot, path );
    }

    if (verbose) RAWTRACE1("Sending file %s...", fullPath.c_str());

    if ( doesNotExists ) {
        if ( stat(fullPath.c_str(), &st) != 0 || !S_ISREG(st.st_mode) ) {
            doesNotExists = true;
        }else
            doesNotExists = false;
    }

#ifdef RHODES_EMULATOR
    if ( doesNotExists )
    {
        CTokenizer oTokenizer( RHOSIMCONF().getString("ext_path"), ";" );
	    while (oTokenizer.hasMoreTokens()) 
        {
		    String tok = oTokenizer.nextToken();
		    tok = String_trim(tok);
            
            String fullPath1 = CFilePath::join( tok, path );
            if (stat(fullPath1.c_str(), &st) == 0 && S_ISREG(st.st_mode))
            {
                fullPath = fullPath1;
                doesNotExists = false;
                break;
            }

        }
    }
#endif

    if ( doesNotExists ) {
        if (verbose) RAWLOG_ERROR1("The file %s was not found", path.c_str());
        String error = "<!DOCTYPE html><html><font size=\"+4\"><h2>404 Not Found.</h2> The file " + path + " was not found.</font></html>";
        send_response(create_response("404 Not Found",error));
        return false;
    }

    PROF_START("LOW_FILE");
    FILE *fp = fopen(fullPath.c_str(), "rb");
    PROF_STOP("LOW_FILE");
    if (!fp) {
        if (verbose) RAWLOG_ERROR1("The file %s could not be opened", path.c_str());
        String error = "<!DOCTYPE html><html><font size=\"+4\"><h2>404 Not Found.</h2> The file " + path + " could not be opened.</font></html";
        send_response(create_response("404 Not Found",error));
        return false;
    }
    
    HeaderList headers;
    
    // Detect mime type
    headers.push_back(Header("Content-Type", get_mime_type(path)));
    if ( String_startsWith(path, "/public") )
    {
        headers.push_back(Header("Expires", "Thu, 15 Apr 2020 20:00:00 GMT") );
        headers.push_back(Header("Cache-Control", "max-age=2592000") );
    }

    // Content length
    char* buf = new char[FILE_BUF_SIZE];
    
    String start_line;
    
    size_t file_size = st.st_size;
    size_t range_begin = 0, range_end = file_size - 1;
    size_t content_size = file_size;
    if (parse_range(hdrs, &range_begin, &range_end))
    {
        if (range_end >= file_size)
            range_end = file_size - 1;
        if (range_begin >= range_end)
            range_begin = range_end - 1;
        content_size = range_end - range_begin + 1;
        
        if (fseek(fp, range_begin, SEEK_SET) == -1) {
            RAWLOG_ERROR1("Can not seek to specified range start: %lu", (unsigned long)range_begin);
			snprintf(buf, FILE_BUF_SIZE, "bytes */%lu", (unsigned long)file_size);
			headers.push_back(Header("Content-Range", buf));
			send_response(create_response("416 Request Range Not Satisfiable",headers));
            fclose(fp);
            delete[] buf;
            return false;
        }
		
		snprintf(buf, FILE_BUF_SIZE, "bytes %lu-%lu/%lu", (unsigned long)range_begin,
                 (unsigned long)range_end, (unsigned long)file_size);
        headers.push_back(Header("Content-Range", buf));
        
        start_line = "206 Partial Content";
    }
    else {
        start_line = "200 OK";
    }

    
    snprintf(buf, FILE_BUF_SIZE, "%lu", (unsigned long)content_size);
    headers.push_back(Header("Content-Length", buf));
    
    // Send headers
    if (!send_response(create_response(start_line, headers))) {
        if (verbose) RAWLOG_ERROR1("Can not send headers while sending file %s", path.c_str());
        fclose(fp);
        delete[] buf;
        return false;
    }
    
    // Send body
    for (size_t start = range_begin; start < range_end + 1;) {
        size_t need_to_read = range_end - start + 1;
        if (need_to_read == 0)
            break;
        
        if (need_to_read > FILE_BUF_SIZE)
            need_to_read = FILE_BUF_SIZE;

PROF_START("LOW_FILE");
        size_t n = fread(buf, 1, need_to_read, fp);//fread(buf, 1, need_to_read, fp);
PROF_STOP("LOW_FILE");
        if (n < need_to_read) {
			if (ferror(fp) ) {
				if (verbose) RAWLOG_ERROR2("Can not read part of file (at position %lu): %s", (unsigned long)start, strerror(errno));
			} else if ( feof(fp) ) {
				if (verbose) RAWLOG_ERROR1("End of file reached, but we expect data (%lu bytes)", (unsigned long)need_to_read);
			}
            fclose(fp);
            delete[] buf;
            return false;
        }
        
        start += n;
        
        if (!send_response_body(String(buf, n))) {
            if (verbose) RAWLOG_ERROR1("Can not send part of data while sending file %s", path.c_str());
            fclose(fp);
            delete[] buf;
            return false;
        }
    }

PROF_START("LOW_FILE");
    fclose(fp);
PROF_STOP("LOW_FILE");
    delete[] buf;
    if (verbose) RAWTRACE1("File %s was sent successfully", path.c_str());
    return false;
}
Пример #5
0
    void CsvFile::read(HeaderList& header, DataMap& data) const  throw (FileException, CustomException)
    {
        std::ifstream file(filename.c_str()); 
        if (!file)  throw FileException("Cannot open file", filename, __FUNCTION_NAME__);
	
	
        // a buffer to store the line read
        std::string line;
	
        if ( ! std::getline(file, line))
            throw FileException("Cannot get the first line of file", filename, __FUNCTION_NAME__);
	

        unsigned int pointer = 0;
        unsigned int rawnb = 0;
        std::string word ="";
        int localHostColNumber = -1, tmp=0;
        bool uselocalhost=false;
        bool useotherthanlocalhost=false;
	
        // Parse the first line (header line)
        while(pointer < line.size())
            {
                while( pointer < line.size() && line[pointer] != separator)
                    {
                        //remove white spaces
                        if (line[pointer] != ' ')
                            {
                                word.push_back(line[pointer]);
                            }
                        pointer++;
                    }
                if( !word.empty())
                    {
                        // warning      if id    already in
                        if (std::find(header.begin(), header.end(), word) != header.end() )  
                            {
                                // Was error before, now just a warning: may be convenient  to have several time the same name in a header
                                Msg::warning("Duplicate header element '"+ word+"' in file "+filename, __FUNCTION_NAME__);
                                // throw FileException("Duplicate header elements'"+ word+"'",filename, __FUNCTION_NAME__);
                            }
                        header.push_back(word);
                        tmp++;
                     }
                // if we have "localhost" in csv file, note the column number.
                if(word == "localhost" || word == "Localhost" || word == "127.0.0.1")
                	localHostColNumber = tmp;

                 pointer++;
                word.clear();
            }
	  
        // Parse the lines
        while (std::getline(file, line))
            {
                rawnb++;
                pointer = 0;
                bool firstWord = true; // Are we parsing the first column
                unsigned int column = 0; // Which column is being scanned?
                std::string id;
                while(line.size() > pointer)
                    {	
                        // get word
                        word.clear();
                        while(line.size() > pointer && line[pointer] != separator)
                            {
                                if (line[pointer] != ' ')
                                    word.push_back(line[pointer]);
                                pointer++;
                            }
                        pointer++;
		  

                        if (firstWord)
                            {
                                // it's the map  id 
                                if ( word.empty() )  break; // forget about this line and go to parse next line
                                id = word;

                                if(data.find(id) != data.end())
                                	throw CustomException("Component : " + id + " is specified twice in CSV file.", __FUNCTION_NAME__ );

                                // init the raw of this id with zeros.
                                data[id].insert(data[id].begin(), header.size(),0);
                                firstWord = false;
                            }
                        else
                            {
                                if (!word.empty())
                                    {
                                	// store data
                                	try{
                                		data[id].at(column) = convertTo<int>(word);

                                		if (data[id].at(column) > 0)// one component instance at least mapped
                                		{
                                			if(column == localHostColNumber-1)   // we are reading in a locahost column
                                			{
                                				// Remember that we have one component mapped on a localHost.
                                				uselocalhost=true;
                                			}
                                			else
                                			{
                                				// Remember that we have one component mapped on an host different  from  localHost.
                                				useotherthanlocalhost=true;
                                			}
                                			if(uselocalhost && useotherthanlocalhost)
                                				throw FileException("Host name 'localhost' is used in CSV file in conjunction with other host names or IP address. Replace 'localhost' by IP address or machine name to avoid problems", filename,  __FUNCTION_NAME__);
                                		}
                                	}//end try
                                       catch(const BadConversion& e)
                                            {
                                                throw FileException("Raw="+toString<unsigned int>(rawnb+1)+", col="+toString<unsigned int>(column+2)+") contains non integer value="+word,filename, __FUNCTION_NAME__);
                                                //			      std::cerr << "ERROR: Csv File "<<fileName<<", element (raw="<<rawnb+1<<", col="<<column+2<<") contains non integer value="<<word<< std::endl;
                                            }
                                        catch(const std::out_of_range& e)
                                            {
                                                throw FileException("Element (raw="+toString<unsigned int>(rawnb+1)+", col="+toString<unsigned int>(column+2)+")  out of column  (value="+word+", ignored)",filename, __FUNCTION_NAME__);
                                                //			      std::cerr << "WARNING : Csv File "<<fileName<<", element (raw="<<rawnb+1<<", col="<<column+2<<")  out of column  (value="<<word<<", ignored)"<< std::endl;
                                            }

                                    }
                                column++;
                            }
                    }
            }
    };
Пример #6
0
bool CHttpServer::decide(String const &method, String const &arg_uri, String const &query,
                         HeaderList const &headers, String const &body)
{
    RAWTRACE1("Decide what to do with uri %s", arg_uri.c_str());
    callback_t callback = registered(arg_uri);
    if (callback) {
        RAWTRACE1("Uri %s is registered callback, so handle it appropriately", arg_uri.c_str());

        if ( callback == rho_http_ruby_proc_callback )
            call_ruby_proc( query, body );
        else
            callback(this, query.length() ? query : body);

        return false;
    }

    String uri = arg_uri;

//#ifdef OS_ANDROID
//    //Work around malformed Android WebView URLs
//    if (!String_startsWith(uri, "/app") &&
//        !String_startsWith(uri, "/public") &&
//        !String_startsWith(uri, "/data")) 
//    {
//        RAWTRACE1("Malformed URL: '%s', adding '/app' prefix.", uri.c_str());
//        uri = CFilePath::join("/app", uri);
//    }
//#endif

    String fullPath = CFilePath::join(m_root, uri);
    
    Route route;
    if (dispatch(uri, route)) {
        RAWTRACE1("Uri %s is correct route, so enable MVC logic", uri.c_str());
        
        VALUE req = create_request_hash(route.application, route.model, route.action, route.id,
                                        method, uri, query, headers, body);
        VALUE data = callFramework(req);
        String reply(getStringFromValue(data), getStringLenFromValue(data));
        rho_ruby_releaseValue(data);

        bool isRedirect = String_startsWith(reply, "HTTP/1.1 301") ||
                          String_startsWith(reply, "HTTP/1.1 302");

        if (!send_response(reply, isRedirect))
            return false;

        if (method == "GET")
            rho_rhodesapp_keeplastvisitedurl(uri.c_str());

		if ( sync::RhoconnectClientManager::haveRhoconnectClientImpl() ) {

			if (!route.id.empty()) {
				sync::RhoconnectClientManager::rho_sync_addobjectnotify_bysrcname(route.model.c_str(), route.id.c_str());
			}			
		}
        
        return true;
    }
    
//#ifndef OS_ANDROID
    if (isdir(fullPath)) {
        RAWTRACE1("Uri %s is directory, redirecting to index", uri.c_str());
        String q = query.empty() ? "" : "?" + query;
        
        HeaderList headers;
        headers.push_back(Header("Location", CFilePath::join( uri, "index"RHO_ERB_EXT) + q));
        
        send_response(create_response("301 Moved Permanently", headers), true);
        return false;
    }
//#else
//    //Work around this Android redirect bug:
//    //http://code.google.com/p/android/issues/detail?can=2&q=11583&id=11583
//    if (isdir(fullPath)) {
//        RAWTRACE1("Uri %s is directory, override with index", uri.c_str());
//        return decide(method, CFilePath::join( uri, "index"RHO_ERB_EXT), query, headers, body);
//    }
//#endif
    if (isindex(uri)) {
        if (!isfile(fullPath)) {
            RAWLOG_ERROR1("The file %s was not found", fullPath.c_str());
            String error = "<html><font size=\"+4\"><h2>404 Not Found.</h2> The file " + uri + " was not found.</font></html>";
            send_response(create_response("404 Not Found",error));
            return false;
        }
        
        RAWTRACE1("Uri %s is index file, call serveIndex", uri.c_str());

        VALUE req = create_request_hash(route.application, route.model, route.action, route.id,
                                        method, uri, query, headers, body);

        VALUE data = callServeIndex((char *)fullPath.c_str(), req);
        String reply(getStringFromValue(data), getStringLenFromValue(data));
        rho_ruby_releaseValue(data);

        if (!send_response(reply))
            return false;

        if (method == "GET")
            rho_rhodesapp_keeplastvisitedurl(uri.c_str());

        return true;
    }
    
    // Try to send requested file
    RAWTRACE1("Uri %s should be regular file, trying to send it", uri.c_str());
    return send_file(uri, headers);
}