int main(int argc, char** argv) { #ifdef __APPLE__ // TODO: Find a proper solution. // Seems to be non-NULL even outside of a proper bundle. CFBundleRef bundle = CFBundleGetMainBundle(); if (bundle) { char bundle_path[PATH_MAX], exe_path[PATH_MAX]; CFURLRef bundleURL = CFBundleCopyBundleURL(bundle); CFURLGetFileSystemRepresentation(bundleURL, true, (unsigned char*)bundle_path, PATH_MAX); CFRelease(bundleURL); uint32_t size = sizeof(exe_path); if (_NSGetExecutablePath(exe_path, &size) != 0) { fprintf(stderr, "Failed to get executable path.\n"); return 1; } char *exe_realpath = realpath(exe_path, NULL); char *exe_dir = dirname(exe_realpath); if (strcmp(exe_dir, bundle_path)) { char resources_path[PATH_MAX]; CFURLRef resourcesURL = CFBundleCopyResourcesDirectoryURL(bundle); CFURLGetFileSystemRepresentation(resourcesURL, true, (unsigned char*)resources_path, PATH_MAX); CFRelease(resourcesURL); chdir(resources_path); } else { chdir(exe_dir); } free(exe_realpath); free(exe_dir); } #endif #ifdef DEBUG Logging::setLogLevel(LOGLEVEL_DEBUG); #else Logging::setLogLevel(LOGLEVEL_INFO); #endif #if defined(__WIN32__) && !defined(DEBUG) Logging::setLogFile("EmptyEpsilon.log"); #endif #ifdef RESOURCE_BASE_DIR PreferencesManager::load(RESOURCE_BASE_DIR "options.ini"); #endif if (getenv("HOME")) PreferencesManager::load(string(getenv("HOME")) + "/.emptyepsilon/options.ini"); else PreferencesManager::load("options.ini"); for(int n=1; n<argc; n++) { char* value = strchr(argv[n], '='); if (!value) continue; *value++ = '\0'; PreferencesManager::set(string(argv[n]).strip(), string(value).strip()); } new Engine(); if (PreferencesManager::get("mod") != "") { string mod = PreferencesManager::get("mod"); if (getenv("HOME")) { new DirectoryResourceProvider(string(getenv("HOME")) + "/.emptyepsilon/resources/mods/" + mod); PackResourceProvider::addPackResourcesForDirectory(string(getenv("HOME")) + "/.emptyepsilon/resources/mods/" + mod); } new DirectoryResourceProvider("resources/mods/" + mod); PackResourceProvider::addPackResourcesForDirectory("resources/mods/" + mod); } #ifdef RESOURCE_BASE_DIR new DirectoryResourceProvider(RESOURCE_BASE_DIR "resources/"); new DirectoryResourceProvider(RESOURCE_BASE_DIR "scripts/"); new DirectoryResourceProvider(RESOURCE_BASE_DIR "packs/SolCommand/"); PackResourceProvider::addPackResourcesForDirectory(RESOURCE_BASE_DIR "packs"); #endif if (getenv("HOME")) { new DirectoryResourceProvider(string(getenv("HOME")) + "/.emptyepsilon/resources/"); new DirectoryResourceProvider(string(getenv("HOME")) + "/.emptyepsilon/scripts/"); new DirectoryResourceProvider(string(getenv("HOME")) + "/.emptyepsilon/packs/SolCommand/"); } new DirectoryResourceProvider("resources/"); new DirectoryResourceProvider("scripts/"); new DirectoryResourceProvider("packs/SolCommand/"); PackResourceProvider::addPackResourcesForDirectory("packs"); textureManager.setDefaultSmooth(true); textureManager.setDefaultRepeated(true); textureManager.setAutoSprite(false); textureManager.getTexture("Tokka_WalkingMan.png", sf::Vector2i(6, 1)); //Setup the sprite mapping. if (PreferencesManager::get("httpserver").toInt() != 0) { int port_nr = PreferencesManager::get("httpserver").toInt(); if (port_nr < 10) port_nr = 80; LOG(INFO) << "Enabling HTTP script access on port: " << port_nr; LOG(INFO) << "NOTE: This is potentially a risk!"; HttpServer* server = new HttpServer(port_nr); server->addHandler(new HttpRequestFileHandler("www")); server->addHandler(new HttpScriptHandler()); } colorConfig.load(); if (PreferencesManager::get("headless") == "") { //Setup the rendering layers. backgroundLayer = new RenderLayer(); objectLayer = new RenderLayer(backgroundLayer); effectLayer = new RenderLayer(objectLayer); hudLayer = new RenderLayer(effectLayer); mouseLayer = new RenderLayer(hudLayer); glitchPostProcessor = new PostProcessor("glitch", mouseLayer); glitchPostProcessor->enabled = false; warpPostProcessor = new PostProcessor("warp", glitchPostProcessor); warpPostProcessor->enabled = false; defaultRenderLayer = objectLayer; int width = 1200; int height = 900; int fsaa = 0; bool fullscreen = PreferencesManager::get("fullscreen", "1").toInt(); if (PreferencesManager::get("fsaa").toInt() > 0) { fsaa = PreferencesManager::get("fsaa").toInt(); if (fsaa < 2) fsaa = 2; } P<WindowManager> window_manager = new WindowManager(width, height, fullscreen, warpPostProcessor, fsaa); window_manager->setAllowVirtualResize(true); engine->registerObject("windowManager", window_manager); } if (PreferencesManager::get("touchscreen").toInt()) { InputHandler::touch_screen = true; } if (!InputHandler::touch_screen) { engine->registerObject("mouseRenderer", new MouseRenderer()); } new DebugRenderer(); if (PreferencesManager::get("touchcalibfile") != "") { FILE* f = fopen(PreferencesManager::get("touchcalibfile").c_str(), "r"); if (f) { float m[6]; if (fscanf(f, "%f %f %f %f %f %f", &m[0], &m[1], &m[2], &m[3], &m[4], &m[5]) == 6) InputHandler::mouse_transform = sf::Transform(m[0], m[1], m[2], m[3], m[4], m[5], 0, 0, 1); fclose(f); } } soundManager->setMusicVolume(PreferencesManager::get("music_volume", "50").toFloat()); if (PreferencesManager::get("disable_shaders").toInt()) PostProcessor::setEnable(false); P<ResourceStream> main_font_stream = getResourceStream("gui/fonts/BebasNeue Regular.otf"); main_font = new sf::Font(); main_font->loadFromStream(**main_font_stream); P<ResourceStream> bold_font_stream = getResourceStream("gui/fonts/BebasNeue Bold.otf"); bold_font = new sf::Font(); bold_font->loadFromStream(**bold_font_stream); if (sf::Shader::isAvailable()) { objectShader = new sf::Shader(); simpleObjectShader = new sf::Shader(); basicShader = new sf::Shader(); billboardShader = new sf::Shader(); P<ResourceStream> vertexStream = getResourceStream("objectShader.vert"); P<ResourceStream> fragmentStream = getResourceStream("objectShader.frag"); objectShader->loadFromStream(**vertexStream, **fragmentStream); vertexStream = getResourceStream("simpleObjectShader.vert"); fragmentStream = getResourceStream("simpleObjectShader.frag"); simpleObjectShader->loadFromStream(**vertexStream, **fragmentStream); vertexStream = getResourceStream("basicShader.vert"); fragmentStream = getResourceStream("basicShader.frag"); basicShader->loadFromStream(**vertexStream, **fragmentStream); vertexStream = getResourceStream("billboardShader.vert"); fragmentStream = getResourceStream("billboardShader.frag"); billboardShader->loadFromStream(**vertexStream, **fragmentStream); } { P<ScriptObject> modelDataScript = new ScriptObject("model_data.lua"); if (modelDataScript->getError() != "") exit(1); modelDataScript->destroy(); P<ScriptObject> shipTemplatesScript = new ScriptObject("shipTemplates.lua"); if (shipTemplatesScript->getError() != "") exit(1); shipTemplatesScript->destroy(); P<ScriptObject> factionInfoScript = new ScriptObject("factionInfo.lua"); if (factionInfoScript->getError() != "") exit(1); factionInfoScript->destroy(); fillDefaultDatabaseData(); P<ScriptObject> scienceInfoScript = new ScriptObject("science_db.lua"); if (scienceInfoScript->getError() != "") exit(1); scienceInfoScript->destroy(); //Find out which model data isn't used by ship templates and output that to log. std::set<string> used_model_data; for(string template_name : ShipTemplate::getAllTemplateNames()) used_model_data.insert(ShipTemplate::getTemplate(template_name)->model_data->getName()); for(string name : ModelData::getModelDataNames()) { if (used_model_data.find(name) == used_model_data.end()) { LOG(INFO) << "Model data: " << name << " is not used by any ship template"; } } } P<HardwareController> hardware_controller = new HardwareController(); #ifdef RESOURCE_BASE_DIR hardware_controller->loadConfiguration(RESOURCE_BASE_DIR "hardware.ini"); #endif if (getenv("HOME")) hardware_controller->loadConfiguration(string(getenv("HOME")) + "/.emptyepsilon/hardware.ini"); else hardware_controller->loadConfiguration("hardware.ini"); returnToMainMenu(); engine->runMainLoop(); P<WindowManager> windowManager = engine->getObject("windowManager"); if (windowManager) { PreferencesManager::set("fsaa", windowManager->getFSAA()); PreferencesManager::set("fullscreen", windowManager->isFullscreen() ? 1 : 0); } PreferencesManager::set("music_volume", soundManager->getMusicVolume()); PreferencesManager::set("disable_shaders", PostProcessor::isEnabled() ? 0 : 1); if (PreferencesManager::get("headless") == "") { #ifndef _MSC_VER // MFC TODO: Fix me -- save prefs to user prefs dir on Windows. if (getenv("HOME")) { #ifdef __WIN32__ mkdir((string(getenv("HOME")) + "/.emptyepsilon").c_str()); #else mkdir((string(getenv("HOME")) + "/.emptyepsilon").c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); #endif PreferencesManager::save(string(getenv("HOME")) + "/.emptyepsilon/options.ini"); }else #endif { PreferencesManager::save("options.ini"); } } delete engine; return 0; }
bool test_http1_1_keepalive() { Socket acceptSocket; SocketError socketErr; HttpServer server; HttpError serverError; std::mutex serverMutex; std::condition_variable serverCond; bool isShutdown = false; std::tie(acceptSocket, socketErr) = getAcceptingSocket(Addrinfo::getLoopback(INet::Protocol::Ipv6, 0)); if (socketErr != SocketError::Ok) { testf("Failed to bind socket for accept with: %d", socketErr); return false; } uint16_t boundPort = acceptSocket.getLocalAddress().getPort(); StreamSourceSocket streamSource(std::move(acceptSocket)); server.addHandler("/index.html", [](HttpRequest& request, HttpResponse& response) { response.setStatus(200, "OK"); response.addHeader("Content-Length", "11"); HttpOutputStream* outputStream; std::tie(outputStream, std::ignore) = response.getOutputStream(); outputStream->write("Hello World", 11); outputStream->close(); }); Async::runAsync([&streamSource, &server, &serverError, &serverMutex, &serverCond, &isShutdown] { HttpError err = server.start(&streamSource); std::unique_lock<std::mutex> lock(serverMutex); isShutdown = true; serverError = err; serverCond.notify_one(); }); std::vector<StringRef> requests = { "GET /index.html HTTP/1.1\r\n" "Connection: keep-alive\r\n" "Host: localhost\r\n" "\r\n" "GET /index.html HTTP/1.1\r\n" "Host: localhost\r\n" "\r\n" "GET /index.html HTTP/1.1\r\n" "Host: localhost\r\n" "Connection: close\r\n" "\r\n", "GET /index.html HTTP/1.1\r\n" "Host: localhost\r\n" "Connection: close\r\n" "\r\n" "GET /index.html HTTP/1.1\r\n" "Host: localhost\r\n" "\r\n", }; std::vector<StringRef> expectedResponses = { "HTTP/1.1 200 OK\r\nContent-Length: 11\r\n\r\nHello World" "HTTP/1.1 200 OK\r\nContent-Length: 11\r\n\r\nHello World" "HTTP/1.1 200 OK\r\nContent-Length: 11\r\n\r\nHello World", "HTTP/1.1 200 OK\r\nContent-Length: 11\r\n\r\nHello World", }; for (size_t i = 0; i < requests.size(); i++) { StringRef request = requests[i]; StringRef expectedResponse = expectedResponses[i]; char responseBuffer[1024]; Socket requestSocket; std::tie(requestSocket, socketErr) = getConnectedSocket(Addrinfo::getLoopback(INet::Protocol::Ipv6, boundPort)); if (socketErr != SocketError::Ok) { testf("Failed to connect to HTTP socket with: %d", socketErr); return false; } socketErr = requestSocket.write(request.data(), request.length()); if (socketErr != SocketError::Ok) { testf("Failed to write to HTTP socket with: %d", socketErr); return false; } uint32_t totalBytesRead = 0; std::tie(totalBytesRead, socketErr) = readFully(&requestSocket, responseBuffer, sizeof(responseBuffer)); if (socketErr != SocketError::Ok) { testf("Failed to read from HTTP socket with: %d", socketErr); return false; } if (StringRef(responseBuffer, totalBytesRead) != expectedResponse) { testf("Did not receive expected response. Got:\n%.*s", (size_t)totalBytesRead, responseBuffer); return false; } } server.shutdown(); // Wait for server shutdown and check its error std::unique_lock<std::mutex> lock(serverMutex); serverCond.wait(lock, [&isShutdown] {return isShutdown; }); if (serverError != HttpError::Ok) { testf("Failed to start HTTP server with: %d", serverError); return false; } return true; }
bool test_http1_1_chunked_request() { Socket acceptSocket; SocketError socketErr; HttpServer server; HttpError serverError; std::mutex serverMutex; std::condition_variable serverCond; bool isShutdown = false; bool gotExpectedPost = false; HttpError postError = HttpError::Ok; bool hadCorrectHeader = false; std::tie(acceptSocket, socketErr) = ::getAcceptingSocket(Addrinfo::getLoopback(INet::Protocol::Ipv6, 0)); if (socketErr != SocketError::Ok) { testf("Failed to bind socket for accept with: %d", socketErr); return false; } uint16_t boundPort = acceptSocket.getLocalAddress().getPort(); StreamSourceSocket streamSource(std::move(acceptSocket)); server.addHandler("/formthingy", [&gotExpectedPost, &postError, &hadCorrectHeader](HttpRequest& request, HttpResponse& response) { char readBuffer[1024]; HttpInputStream& httpInputStream = request.getInputStream(); HttpError readErr; uint32_t readBytes; uint32_t totalRead = 0; do { std::tie(readBytes, readErr) = httpInputStream.read(readBuffer + totalRead, 3); // Intentionally small reads totalRead += readBytes; } while (readErr == HttpError::Ok); if (readErr != HttpError::Eof) { postError = readErr; return; } postError = httpInputStream.close(); const StringRef postData = "Some form post data"; if (totalRead != postData.length()) { gotExpectedPost = false; } else { gotExpectedPost = std::memcmp(postData.data(), readBuffer, postData.length()) == 0; } StringRef transferEncodingHeader; bool hasHeader; std::tie(transferEncodingHeader, hasHeader) = request.getHeader("Transfer-Encoding"); if (!hasHeader) { hadCorrectHeader = false; } else { hadCorrectHeader = transferEncodingHeader.equals("chunked"); } response.setStatus(200, "OK"); }); Async::runAsync([&streamSource, &server, &serverError, &serverMutex, &serverCond, &isShutdown] { HttpError err = server.start(&streamSource); std::unique_lock<std::mutex> lock(serverMutex); isShutdown = true; serverError = err; serverCond.notify_one(); }); StringRef request = "POST /formthingy HTTP/1.1\r\n" "Host: localhost\r\n" "Transfer-Encoding: chunked\r\n" "Connection: close\r\n" "\r\n" "5\r\n" "Some \r\n" "e\r\n" "form post data\r\n" "0\r\n" "\r\n"; StringRef expectedResponse = "HTTP/1.1 200 OK\r\n\r\n"; char responseBuffer[1024]; Socket requestSocket; std::tie(requestSocket, socketErr) = getConnectedSocket(Addrinfo::getLoopback(INet::Protocol::Ipv6, boundPort)); if (socketErr != SocketError::Ok) { testf("Failed to connect to HTTP socket with: %d", socketErr); return false; } socketErr = requestSocket.write(request.data(), request.length()); if (socketErr != SocketError::Ok) { testf("Failed to write to HTTP socket with: %d", socketErr); return false; } uint32_t totalBytesRead = 0; std::tie(totalBytesRead, socketErr) = readFully(&requestSocket, responseBuffer, sizeof(responseBuffer)); if (socketErr != SocketError::Ok) { testf("Failed to read from HTTP socket with: %d", socketErr); return false; } if (StringRef(responseBuffer, totalBytesRead) != expectedResponse) { testf("Did not receive expected response. Got:\n%.*s", totalBytesRead, responseBuffer); return false; } server.shutdown(); // Wait for server shutdown and check its error std::unique_lock<std::mutex> lock(serverMutex); serverCond.wait(lock, [&isShutdown] {return isShutdown; }); if (serverError != HttpError::Ok) { testf("Failed to start HTTP server with: %d", serverError); return false; } if (!gotExpectedPost) { testf("Did not read back expected post data"); return false; } if (postError != HttpError::Ok) { testf("Received error when reading post data: %d", postError); return false; } if (!hadCorrectHeader) { testf("Http request did not have expected Transfer-Encoding header"); return false; } return true; }
// Tests that a chunked header is automatically added, and chunked mode is used, // if neither Transfer-Encoding or Content-Length are specified. bool test_http1_1_auto_chunked_response() { Socket acceptSocket; SocketError socketErr; HttpServer server; HttpError serverError = HttpError::Ok; HttpError responseError = HttpError::Ok; std::mutex serverMutex; std::condition_variable serverCond; bool isShutdown = false; std::tie(acceptSocket, socketErr) = getAcceptingSocket(Addrinfo::getLoopback(INet::Protocol::Ipv6, 0)); if (socketErr != SocketError::Ok) { testf("Failed to bind socket for accept with: %d", socketErr); return false; } uint16_t boundPort = acceptSocket.getLocalAddress().getPort(); StreamSourceSocket streamSource(std::move(acceptSocket)); server.addHandler("/index.html", [&responseError](HttpRequest& request, HttpResponse& response) { response.setStatus(200, "OK"); HttpOutputStream* outputStream; std::tie(outputStream, responseError) = response.getOutputStream(); if (responseError != HttpError::Ok) { return; } responseError = outputStream->write("0123456789", 10); if (responseError != HttpError::Ok) { return; } responseError = outputStream->write("abc", 3); }); Async::runAsync([&streamSource, &server, &serverError, &serverMutex, &serverCond, &isShutdown] { HttpError err = server.start(&streamSource); std::unique_lock<std::mutex> lock(serverMutex); isShutdown = true; serverError = err; serverCond.notify_one(); }); StringRef request = "GET /index.html HTTP/1.1\r\n" "Host: localhost\r\n" "Connection: close\r\n" "\r\n"; StringRef expectedResponse = "HTTP/1.1 200 OK\r\n" "Transfer-Encoding: chunked\r\n" "\r\n" "A\r\n" "0123456789\r\n" "3\r\n" "abc\r\n" "0\r\n" "\r\n"; char responseBuffer[1024]; Socket requestSocket; std::tie(requestSocket, socketErr) = getConnectedSocket(Addrinfo::getLoopback(INet::Protocol::Ipv6, boundPort)); if (socketErr != SocketError::Ok) { testf("Failed to connect to HTTP socket with: %d", socketErr); return false; } socketErr = requestSocket.write(request.data(), request.length()); if (socketErr != SocketError::Ok) { testf("Failed to write to HTTP socket with: %d", socketErr); return false; } uint32_t totalBytesRead = 0; std::tie(totalBytesRead, socketErr) = readFully(&requestSocket, responseBuffer, sizeof(responseBuffer)); if (socketErr != SocketError::Ok) { testf("Failed to read from HTTP socket with: %d", socketErr); return false; } server.shutdown(); // Wait for server shutdown and check its error std::unique_lock<std::mutex> lock(serverMutex); serverCond.wait(lock, [&isShutdown] {return isShutdown; }); if (serverError != HttpError::Ok) { testf("Failed to start HTTP server with: %d", serverError); return false; } if (responseError != HttpError::Ok) { testf("Error when generating response: %d", responseError); return false; } if (StringRef(responseBuffer, totalBytesRead) != expectedResponse) { testf("Did not receive expected response. Got:\n%.*s", (size_t)totalBytesRead, responseBuffer); return false; } return true; }