├── src ├── server │ ├── tls.h │ ├── server.h │ ├── request.cpp │ ├── server.cpp │ └── request.h ├── interface │ ├── 3ds │ │ ├── interface.h │ │ └── interface.cpp │ └── linux │ │ ├── interface.h │ │ └── interface.cpp ├── util │ └── string.h └── main.cpp ├── cmake ├── bin2s_header.h.in ├── FindSF2D.cmake ├── FindCTRULIB.cmake └── Tools3DS.cmake ├── README.md ├── CMakeLists.txt ├── DevkitArm3DS.cmake └── .gitignore /src/server/tls.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace HTTP { 4 | 5 | } // namespace HTTP -------------------------------------------------------------------------------- /cmake/bin2s_header.h.in: -------------------------------------------------------------------------------- 1 | extern const u8 @__BIN_FILE_NAME@_end[]; 2 | extern const u8 @__BIN_FILE_NAME@[]; 3 | extern const u32 @__BIN_FILE_NAME@_size; 4 | -------------------------------------------------------------------------------- /src/interface/3ds/interface.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../../server/server.h" 4 | 5 | class Interface { 6 | public: 7 | Interface(); 8 | ~Interface(); 9 | 10 | void run(); 11 | 12 | private: 13 | HTTP::Server m_server; 14 | }; -------------------------------------------------------------------------------- /src/interface/linux/interface.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../../server/server.h" 4 | 5 | class Interface { 6 | public: 7 | Interface(); 8 | ~Interface(); 9 | 10 | void run(); 11 | 12 | private: 13 | HTTP::Server m_server; 14 | }; -------------------------------------------------------------------------------- /src/util/string.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | inline std::vector split(std::string const &input, 9 | const char delim) { 10 | std::vector out; 11 | std::string token; 12 | std::stringstream ss(input); 13 | 14 | while (getline(ss, token, delim)) { 15 | out.push_back(token); 16 | } 17 | 18 | return out; 19 | } -------------------------------------------------------------------------------- /src/server/server.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | namespace HTTP { 9 | class Server { 10 | public: 11 | Server(); 12 | ~Server(); 13 | 14 | bool tick(); 15 | 16 | bool failure() { return m_failure; } 17 | std::string error() { return m_error; }; 18 | 19 | void stop() { m_running = false; }; 20 | 21 | private: 22 | bool m_failure{}; 23 | int m_sockfd{}; 24 | bool m_running = false; 25 | std::string m_error{}; 26 | 27 | sockaddr_in m_sockaddr; 28 | }; 29 | } // namespace HTTP -------------------------------------------------------------------------------- /src/interface/linux/interface.cpp: -------------------------------------------------------------------------------- 1 | #include "interface.h" 2 | 3 | #include 4 | 5 | Interface::Interface() { 6 | printf("This is the linux interface starting...\n"); 7 | 8 | bool fail = m_server.failure(); 9 | 10 | if (fail) { 11 | printf("Failed setting up web server...\n"); 12 | printf("Error: %s\n", m_server.error().c_str()); 13 | return; 14 | } else { 15 | printf("Successfully setup web server...\n"); 16 | } 17 | } 18 | 19 | Interface::~Interface() { printf("Goodbye linux interface!\n"); } 20 | 21 | void Interface::run() { 22 | 23 | printf("This is the linux interface running...\n"); 24 | while (true) { 25 | m_server.tick(); 26 | } 27 | } -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | #include "./util/string.h" 2 | #include "server/server.h" 3 | #include 4 | 5 | #if IS_3DS 6 | #include "interface/3ds/interface.h" 7 | #else 8 | #include "interface/linux/interface.h" 9 | #endif 10 | 11 | /* 12 | TODO: Config file to change options? 13 | */ 14 | 15 | int main(int argc, char **argv) { 16 | auto current_executable_path = split(argv[0], '/'); 17 | current_executable_path.erase(current_executable_path.end() - 1); 18 | 19 | std::string current_path; 20 | 21 | for (auto &path : current_executable_path) 22 | current_path += path + "/"; 23 | 24 | chdir(current_path.c_str()); 25 | 26 | auto *i = new Interface(); 27 | 28 | i->run(); 29 | 30 | delete i; 31 | 32 | #if IS_3DS 33 | // printf("We're about to enter the really long loop...\n"); 34 | // for (long long i = 0; i < 5000000000LL; i++) {}; 35 | // printf("We're done with the really long loop. Goodbye!\n"); 36 | #endif 37 | 38 | return 0; 39 | } -------------------------------------------------------------------------------- /src/interface/3ds/interface.cpp: -------------------------------------------------------------------------------- 1 | #include "interface.h" 2 | 3 | #include <3ds.h> 4 | #include 5 | #include 6 | #include 7 | 8 | Interface::Interface() { 9 | gfxInitDefault(); 10 | gfxSetWide(true); 11 | 12 | atexit(gfxExit); 13 | 14 | consoleInit(GFX_TOP, NULL); 15 | 16 | bool fail = m_server.failure(); 17 | 18 | if (fail) { 19 | printf("Failed setting up web server...\n"); 20 | printf("Error: %s\n", m_server.error().c_str()); 21 | return; 22 | } else { 23 | printf("Successfully setup web server...\n"); 24 | } 25 | } 26 | 27 | Interface::~Interface() { gfxExit(); } 28 | 29 | void Interface::run() { 30 | printf("3ds-webserver demo\n"); 31 | printf("Press \"A\" to clear the console\n"); 32 | printf("Press \"Start\" to exit...\n"); 33 | 34 | while (aptMainLoop()) { 35 | gspWaitForVBlank(); 36 | gfxSwapBuffers(); 37 | hidScanInput(); 38 | 39 | m_server.tick(); 40 | 41 | int kDown = hidKeysDown(); 42 | if (kDown & KEY_START) { 43 | m_server.stop(); 44 | break; 45 | } 46 | 47 | if (kDown & KEY_A) { 48 | consoleClear(); 49 | } 50 | } 51 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # 3ds-webserver 3 | 4 | A small webserver that runs on 3DS and linux. 5 | 6 | ## Dependencies 7 | - CMake (build tool) 8 | - python (custom build script to make stuff easier) 9 | - [devkitPro](https://devkitpro.org/wiki/Main_Page) 10 | ## Build Instructions 11 | - Clone the repo 12 | 13 | **to build for linux** 14 | - run `python build.py` 15 | 16 | **to build for 3ds** 17 | - run `python build.py 3ds` 18 | 19 | The binary will be put in `build/`. For linux, you can 20 | simply run `./build/3ds-webserver`. For 3ds, you can copy 21 | the `3ds-webserver.3dsx` into the `3ds` folder in the root of your SD card. 22 | Finally, launch from homebrew. 23 | 24 | ## Usage 25 | By default, the webserver listens on all interfaces on port 8000. It serves 26 | files from a directory named `3ds-webserver-files/` in the same directory as the binary. 27 | Your folder structure should look like: 28 | ``` 29 | . 30 | ├── 3ds-webserver 31 | └── 3ds-webserver-files 32 | ├── about.html 33 | └── index.html 34 | ``` 35 | 36 | On 3ds, it should look like: 37 | ``` 38 | 3ds 39 | ├── [other 3dsx apps...] 40 | ├── 3ds-webserver.3dsx 41 | └── 3ds-webserver-files 42 | ├── about.html 43 | └── index.html 44 | ``` 45 | ## Planned Features 46 | 47 | - [ ] Proper cache support 48 | - [ ] Options configurable through a config file 49 | - [ ] SSL support 50 | - [ ] CIA support 51 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.1) 2 | project(3ds-webserver) 3 | 4 | set(EXECUTABLE 3ds-webserver) 5 | 6 | set(CMAKE_EXPORT_COMPILE_COMMANDS ON) # Fleet 7 | 8 | option(TARGET_PLATFORM "TARGET_PLATFORM" LINUX) 9 | 10 | # Add shared sources 11 | set(SHARED_SOURCES src/server/server.cpp src/server/request.cpp) 12 | 13 | # Platform specific sources 14 | if("${TARGET_PLATFORM}" STREQUAL "3DS") 15 | # 3DS sources 16 | set(PLATFORM_SOURCES src/interface/3ds/interface.cpp) 17 | else() # Linux sources 18 | set(PLATFORM_SOURCES src/interface/linux/interface.cpp) 19 | endif() 20 | 21 | add_executable(${EXECUTABLE} src/main.cpp ${SHARED_SOURCES} ${PLATFORM_SOURCES}) 22 | 23 | 24 | if("${TARGET_PLATFORM}" STREQUAL "3DS") 25 | # 3DS options 26 | message("Building for 3DS...") 27 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${DKA_SUGGESTED_C_FLAGS}") # Use the devkitArm suggested flags. This is not mandatory. 28 | list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/cmake) # Add the cmake folder to the modules paths, so that we can use the tools and find_package for ctrulib 29 | include(Tools3DS) # Include all the macros and tools needed for 3ds development. 30 | find_package(CTRULIB REQUIRED) # Look for ctrulib 31 | target_link_libraries(${EXECUTABLE} 3ds::ctrulib) 32 | target_link_libraries(${EXECUTABLE} m) 33 | add_3dsx_target(${EXECUTABLE}) 34 | add_definitions(-DIS_3DS) 35 | else() # Default to linux build 36 | # Linux specific options 37 | message("Building for Linux...") 38 | add_definitions(-DIS_LINUX) 39 | endif() 40 | -------------------------------------------------------------------------------- /cmake/FindSF2D.cmake: -------------------------------------------------------------------------------- 1 | # - Try to find sf2d 2 | # Once done this will define 3 | # LIBSF2D_FOUND - System has sf2d 4 | # LIBSF2D_INCLUDE_DIRS - The sf2d include directories 5 | # LIBSF2D_LIBRARIES - The libraries needed to use sf2d 6 | # 7 | # It also adds an imported target named `3ds::sf2d`. 8 | # Linking it is the same as target_link_libraries(target ${LIBSF2D_LIBRARIES}) and target_include_directories(target ${LIBSF2D_INCLUDE_DIRS}) 9 | 10 | 11 | # DevkitPro paths are broken on windows, so we have to fix those 12 | macro(msys_to_cmake_path MsysPath ResultingPath) 13 | string(REGEX REPLACE "^/([a-zA-Z])/" "\\1:/" ${ResultingPath} "${MsysPath}") 14 | endmacro() 15 | 16 | if(NOT DEVKITPRO) 17 | msys_to_cmake_path("$ENV{DEVKITPRO}" DEVKITPRO) 18 | endif() 19 | 20 | find_path(LIBSF2D_INCLUDE_DIR sf2d.h 21 | PATH_SUFFIXES include ) 22 | 23 | find_library(LIBSF2D_LIBRARY NAMES sf2d libsf2d.a 24 | PATH_SUFFIXES lib) 25 | 26 | set(LIBSF2D_LIBRARIES ${LIBSF2D_LIBRARY} ) 27 | set(LIBSF2D_INCLUDE_DIRS ${LIBSF2D_INCLUDE_DIR} ) 28 | 29 | include(FindPackageHandleStandardArgs) 30 | # handle the QUIETLY and REQUIRED arguments and set LIBSF2D_FOUND to TRUE 31 | # if all listed variables are TRUE 32 | find_package_handle_standard_args(SF2D DEFAULT_MSG 33 | LIBSF2D_LIBRARY LIBSF2D_INCLUDE_DIR) 34 | 35 | mark_as_advanced(LIBSF2D_INCLUDE_DIR LIBSF2D_LIBRARY ) 36 | if(SF2D_FOUND) 37 | set(SF2D ${LIBSF2D_INCLUDE_DIR}/..) 38 | message(STATUS "setting SF2D to ${SF2D}") 39 | 40 | add_library(3ds::sf2d STATIC IMPORTED GLOBAL) 41 | set_target_properties(3ds::sf2d PROPERTIES 42 | IMPORTED_LOCATION "${LIBSF2D_LIBRARY}" 43 | INTERFACE_INCLUDE_DIRECTORIES "${LIBSF2D_INCLUDE_DIR}" 44 | ) 45 | endif() 46 | -------------------------------------------------------------------------------- /cmake/FindCTRULIB.cmake: -------------------------------------------------------------------------------- 1 | # - Try to find ctrulib 2 | # Once done this will define 3 | # LIBCTRU_FOUND - System has ctrulib 4 | # LIBCTRU_INCLUDE_DIRS - The ctrulib include directories 5 | # LIBCTRU_LIBRARIES - The libraries needed to use ctrulib 6 | # 7 | # It also adds an imported target named `3ds::ctrulib`. 8 | # Linking it is the same as target_link_libraries(target ${LIBCTRU_LIBRARIES}) and target_include_directories(target ${LIBCTRU_INCLUDE_DIRS}) 9 | 10 | # DevkitPro paths are broken on windows, so we have to fix those 11 | macro(msys_to_cmake_path MsysPath ResultingPath) 12 | string(REGEX REPLACE "^/([a-zA-Z])/" "\\1:/" ${ResultingPath} "${MsysPath}") 13 | endmacro() 14 | 15 | if(NOT DEVKITPRO) 16 | msys_to_cmake_path("$ENV{DEVKITPRO}" DEVKITPRO) 17 | endif() 18 | 19 | set(CTRULIB_PATHS $ENV{CTRULIB} libctru ctrulib ${DEVKITPRO}/libctru ${DEVKITPRO}/ctrulib) 20 | 21 | find_path(LIBCTRU_INCLUDE_DIR 3ds.h 22 | PATHS ${CTRULIB_PATHS} 23 | PATH_SUFFIXES include libctru/include ) 24 | 25 | find_library(LIBCTRU_LIBRARY NAMES ctru libctru.a 26 | PATHS ${CTRULIB_PATHS} 27 | PATH_SUFFIXES lib libctru/lib ) 28 | 29 | set(LIBCTRU_LIBRARIES ${LIBCTRU_LIBRARY} ) 30 | set(LIBCTRU_INCLUDE_DIRS ${LIBCTRU_INCLUDE_DIR} ) 31 | 32 | include(FindPackageHandleStandardArgs) 33 | # handle the QUIETLY and REQUIRED arguments and set LIBCTRU_FOUND to TRUE 34 | # if all listed variables are TRUE 35 | find_package_handle_standard_args(CTRULIB DEFAULT_MSG 36 | LIBCTRU_LIBRARY LIBCTRU_INCLUDE_DIR) 37 | 38 | mark_as_advanced(LIBCTRU_INCLUDE_DIR LIBCTRU_LIBRARY ) 39 | if(CTRULIB_FOUND) 40 | set(CTRULIB ${LIBCTRU_INCLUDE_DIR}/..) 41 | message(STATUS "setting CTRULIB to ${CTRULIB}") 42 | 43 | add_library(3ds::ctrulib STATIC IMPORTED GLOBAL) 44 | set_target_properties(3ds::ctrulib PROPERTIES 45 | IMPORTED_LOCATION "${LIBCTRU_LIBRARY}" 46 | INTERFACE_INCLUDE_DIRECTORIES "${LIBCTRU_INCLUDE_DIR}" 47 | ) 48 | endif() 49 | -------------------------------------------------------------------------------- /DevkitArm3DS.cmake: -------------------------------------------------------------------------------- 1 | set(CMAKE_SYSTEM_NAME Generic) 2 | set(CMAKE_SYSTEM_PROCESSOR armv6k) 3 | set(3DS TRUE) # To be used for multiplatform projects 4 | 5 | # DevkitPro Paths are broken on windows, so we have to fix those 6 | macro(msys_to_cmake_path MsysPath ResultingPath) 7 | if(WIN32) 8 | string(REGEX REPLACE "^/([a-zA-Z])/" "\\1:/" ${ResultingPath} "${MsysPath}") 9 | else() 10 | set(${ResultingPath} "${MsysPath}") 11 | endif() 12 | endmacro() 13 | 14 | msys_to_cmake_path("$ENV{DEVKITPRO}" DEVKITPRO) 15 | if(NOT IS_DIRECTORY ${DEVKITPRO}) 16 | message(FATAL_ERROR "Please set DEVKITPRO in your environment") 17 | endif() 18 | 19 | msys_to_cmake_path("$ENV{DEVKITARM}" DEVKITARM) 20 | if(NOT IS_DIRECTORY ${DEVKITARM}) 21 | message(FATAL_ERROR "Please set DEVKITARM in your environment") 22 | endif() 23 | 24 | # Prefix detection only works with compiler id "GNU" 25 | # CMake will look for prefixed g++, cpp, ld, etc. automatically 26 | if(WIN32) 27 | set(CMAKE_C_COMPILER "${DEVKITARM}/bin/arm-none-eabi-gcc.exe") 28 | set(CMAKE_CXX_COMPILER "${DEVKITARM}/bin/arm-none-eabi-g++.exe") 29 | set(CMAKE_AR "${DEVKITARM}/bin/arm-none-eabi-gcc-ar.exe" CACHE STRING "") 30 | set(CMAKE_RANLIB "${DEVKITARM}/bin/arm-none-eabi-gcc-ranlib.exe" CACHE STRING "") 31 | else() 32 | set(CMAKE_C_COMPILER "${DEVKITARM}/bin/arm-none-eabi-gcc") 33 | set(CMAKE_CXX_COMPILER "${DEVKITARM}/bin/arm-none-eabi-g++") 34 | set(CMAKE_AR "${DEVKITARM}/bin/arm-none-eabi-gcc-ar" CACHE STRING "") 35 | set(CMAKE_RANLIB "${DEVKITARM}/bin/arm-none-eabi-gcc-ranlib" CACHE STRING "") 36 | endif() 37 | 38 | set(WITH_PORTLIBS ON CACHE BOOL "use portlibs ?") 39 | 40 | if(WITH_PORTLIBS) 41 | set(CMAKE_FIND_ROOT_PATH ${DEVKITARM} ${DEVKITPRO} ${DEVKITPRO}/portlibs/3ds ${DEVKITPRO}/portlibs/armv6k) 42 | else() 43 | set(CMAKE_FIND_ROOT_PATH ${DEVKITARM} ${DEVKITPRO}) 44 | endif() 45 | 46 | set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) 47 | set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) 48 | set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) 49 | set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) 50 | 51 | SET(BUILD_SHARED_LIBS OFF CACHE INTERNAL "Shared libs not available" ) 52 | 53 | add_definitions(-DARM11 -D__3DS__) 54 | 55 | set(ARCH "-march=armv6k -mtune=mpcore -mfloat-abi=hard -mtp=soft ") 56 | set(CMAKE_C_FLAGS " -mword-relocations ${ARCH}" CACHE STRING "C flags") 57 | set(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS}" CACHE STRING "C++ flags") 58 | set(DKA_SUGGESTED_C_FLAGS "-fomit-frame-pointer") 59 | set(DKA_SUGGESTED_CXX_FLAGS "${DKA_SUGGESTED_C_FLAGS} -fno-rtti -fno-exceptions -std=gnu++11") 60 | 61 | set(CMAKE_INSTALL_PREFIX ${DEVKITPRO}/portlibs/3ds 62 | CACHE PATH "Install libraries in the portlibs dir") 63 | 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | 3dstools/### JetBrains template 3 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider 4 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 5 | 6 | # User-specific stuff 7 | .idea/**/workspace.xml 8 | .idea/**/tasks.xml 9 | .idea/**/usage.statistics.xml 10 | .idea/**/dictionaries 11 | .idea/**/shelf 12 | 13 | # AWS User-specific 14 | .idea/**/aws.xml 15 | 16 | # Generated files 17 | .idea/**/contentModel.xml 18 | 19 | # Sensitive or high-churn files 20 | .idea/**/dataSources/ 21 | .idea/**/dataSources.ids 22 | .idea/**/dataSources.local.xml 23 | .idea/**/sqlDataSources.xml 24 | .idea/**/dynamic.xml 25 | .idea/**/uiDesigner.xml 26 | .idea/**/dbnavigator.xml 27 | 28 | # Gradle 29 | .idea/**/gradle.xml 30 | .idea/**/libraries 31 | 32 | # Gradle and Maven with auto-import 33 | # When using Gradle or Maven with auto-import, you should exclude module files, 34 | # since they will be recreated, and may cause churn. Uncomment if using 35 | # auto-import. 36 | # .idea/artifacts 37 | # .idea/compiler.xml 38 | # .idea/jarRepositories.xml 39 | # .idea/modules.xml 40 | # .idea/*.iml 41 | # .idea/modules 42 | # *.iml 43 | # *.ipr 44 | 45 | # CMake 46 | cmake-build-*/ 47 | 48 | # Mongo Explorer plugin 49 | .idea/**/mongoSettings.xml 50 | 51 | # File-based project format 52 | *.iws 53 | 54 | # IntelliJ 55 | out/ 56 | 57 | # mpeltonen/sbt-idea plugin 58 | .idea_modules/ 59 | 60 | # JIRA plugin 61 | atlassian-ide-plugin.xml 62 | 63 | # Cursive Clojure plugin 64 | .idea/replstate.xml 65 | 66 | # SonarLint plugin 67 | .idea/sonarlint/ 68 | 69 | # Crashlytics plugin (for Android Studio and IntelliJ) 70 | com_crashlytics_export_strings.xml 71 | crashlytics.properties 72 | crashlytics-build.properties 73 | fabric.properties 74 | 75 | # Editor-based Rest Client 76 | .idea/httpRequests 77 | 78 | # Android studio 3.1+ serialized cache file 79 | .idea/caches/build_file_checksums.ser 80 | 81 | ### Example user template template 82 | ### Example user template 83 | 84 | # IntelliJ project files 85 | .idea 86 | *.iml 87 | out 88 | gen 89 | ### C++ template 90 | # Prerequisites 91 | *.d 92 | 93 | # Compiled Object files 94 | *.slo 95 | *.lo 96 | *.o 97 | *.obj 98 | 99 | # Precompiled Headers 100 | *.gch 101 | *.pch 102 | 103 | # Compiled Dynamic libraries 104 | *.so 105 | *.dylib 106 | *.dll 107 | 108 | # Fortran module files 109 | *.mod 110 | *.smod 111 | 112 | # Compiled Static libraries 113 | *.lai 114 | *.la 115 | *.a 116 | *.lib 117 | 118 | # Executables 119 | *.exe 120 | *.out 121 | *.app 122 | 123 | .cache/ 124 | 125 | -------------------------------------------------------------------------------- /src/server/request.cpp: -------------------------------------------------------------------------------- 1 | #include "request.h" 2 | #include "../util/string.h" 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | HTTP::Status::Status(int num, std::string description) { 11 | m_status_num = num; 12 | m_description = description; 13 | if (100 <= num && num <= 199) { 14 | m_class = "Informational"; 15 | } else if (200 <= num && num <= 299) { 16 | m_class = "Successful"; 17 | } else if (300 <= num && num <= 399) { 18 | m_class = "Redirection"; 19 | } else if (400 <= num && num <= 499) { 20 | m_class = "Client error"; 21 | } else if (500 <= num && num <= 599) { 22 | m_class = "Server error"; 23 | } else { 24 | m_class = "Unknown"; 25 | } 26 | } 27 | 28 | HTTP::Request::Request() { add_header("Server", "WebServe3DS"); } 29 | 30 | bool HTTP::Request::add_header(std::string key, std::string value) { 31 | if (m_headers.find(key) == m_headers.end()) { 32 | m_headers[key] = value; 33 | return true; 34 | } 35 | 36 | return false; 37 | } 38 | 39 | bool HTTP::Request::remove_header(std::string key) { 40 | if (m_headers.find(key) != m_headers.end()) { 41 | m_headers.erase(m_headers.find(key)); 42 | return true; 43 | } 44 | 45 | return false; 46 | } 47 | 48 | std::string HTTP::Request::get_header(std::string key) { 49 | if (m_headers.find(key) == m_headers.end()) { 50 | return std::string(); 51 | } 52 | return m_headers[key]; 53 | } 54 | 55 | bool HTTP::Request::add_query_param(std::string key, std::string value) { 56 | if (m_query.find(key) == m_query.end()) { 57 | m_query[key] = value; 58 | return true; 59 | } 60 | 61 | return false; 62 | } 63 | 64 | bool HTTP::Request::remove_query_param(std::string key) { 65 | if (m_query.find(key) != m_query.end()) { 66 | m_query.erase(m_query.find(key)); 67 | return true; 68 | } 69 | 70 | return false; 71 | } 72 | 73 | HTTP::Request HTTP::Request::from_raw(std::string raw_request) { 74 | // TODO: Parse raw http requests 75 | auto lines = split(raw_request, '\n'); 76 | 77 | // We expect exactly 3 arguments in the first line 78 | auto line = split(lines[0], ' '); 79 | 80 | if (line.size() != 3) { 81 | throw std::runtime_error("Invalid HTTP request"); 82 | } 83 | 84 | // printf("0: %s, 1: %s, 2: %s\n", line.at(0).c_str(), line.at(1).c_str(), 85 | // line.at(2).c_str()); 86 | 87 | Request r; 88 | r.set_method(method_from_string(line.at(0))); 89 | r.set_path(line.at(1)); 90 | 91 | auto split_http_version = split(line.at(2), '/'); 92 | if (split_http_version.size() != 2 || split_http_version.at(0) != "HTTP") { 93 | throw std::runtime_error("Invalid HTTP request"); 94 | } 95 | 96 | r.set_version(std::stof(split_http_version.at(1))); 97 | 98 | return r; 99 | } 100 | 101 | std::string HTTP::Request::to_raw() { 102 | std::ostringstream ss; 103 | 104 | ss << std::fixed << std::setprecision(1); 105 | ss << "HTTP/" << m_version << " " << m_status.num() << " " 106 | << m_status.description() << "\r\n"; 107 | for (auto const &x : m_headers) { 108 | ss << x.first << ": " << x.second << "\r\n"; 109 | } 110 | 111 | if (!m_body.empty()) { 112 | ss << "\r\n" << m_body << "\r\n"; 113 | } 114 | 115 | return ss.str(); 116 | } 117 | 118 | std::string HTTP::Request::method() { return method_to_string(m_method); } -------------------------------------------------------------------------------- /src/server/server.cpp: -------------------------------------------------------------------------------- 1 | #include "server.h" 2 | 3 | #include "../util/string.h" 4 | #include "request.h" 5 | #include "tls.h" 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | #if IS_3DS 23 | #include <3ds.h> 24 | 25 | void socClose() { socExit(); } 26 | #endif 27 | 28 | HTTP::Server::Server() { 29 | #if IS_3DS 30 | #define SOC_ALIGN 0x1000 31 | #define SOC_BUFFERSIZE 0x100000 32 | int ret; 33 | u32 *SOC_buffer = NULL; 34 | SOC_buffer = (u32 *)memalign(SOC_ALIGN, SOC_BUFFERSIZE); 35 | 36 | if (SOC_buffer == NULL) { 37 | printf("memalign: failed to allocate\n"); 38 | m_failure = true; 39 | m_error = "Memalign failed to allocate"; 40 | } 41 | 42 | // Now intialise soc:u service 43 | if ((ret = socInit(SOC_buffer, SOC_BUFFERSIZE)) != 0) { 44 | printf("socInit: 0x%08X\n", (unsigned int)ret); 45 | m_failure = true; 46 | m_error = "Failed to run socInit"; 47 | } 48 | 49 | // register socShutdown to run at exit 50 | // atexit functions execute in reverse order so this runs before gfxExit 51 | atexit(socClose); 52 | #endif 53 | 54 | m_sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_IP); 55 | 56 | if (m_sockfd < 0) { 57 | perror("Socket creation failed"); 58 | m_failure = true; 59 | m_error = "Socket creation failed"; 60 | return; 61 | } 62 | 63 | const int opt = 1; 64 | 65 | #ifdef SO_REUSEPORT 66 | if (setsockopt(m_sockfd, SOL_SOCKET, SO_REUSEPORT, &opt, sizeof(opt)) < 0) { 67 | perror("Failed to set socket options (SO_REUSEPORT)"); 68 | m_failure = true; 69 | m_error = "Failed to set socket options (SO_REUSEPORT)"; 70 | return; 71 | } 72 | #endif 73 | 74 | if (setsockopt(m_sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) { 75 | perror("Failed to set socket options (SO_REUSEADDR)"); 76 | m_failure = true; 77 | m_error = "Failed to set socket options (SO_REUSEADDR)"; 78 | return; 79 | } 80 | 81 | memset(&m_sockaddr, 0, sizeof(m_sockaddr)); 82 | 83 | m_sockaddr.sin_family = AF_INET; 84 | #if IS_3DS 85 | m_sockaddr.sin_addr.s_addr = gethostid(); 86 | #else 87 | m_sockaddr.sin_addr.s_addr = INADDR_ANY; 88 | #endif 89 | m_sockaddr.sin_port = 90 | htons(8000); // TODO: Make this configurable through a config file 91 | 92 | printf("Trying to bind to %s:%d\n", inet_ntoa(m_sockaddr.sin_addr), 8000); 93 | 94 | #if IS_3DS 95 | if (bind(m_sockfd, (struct sockaddr *)&m_sockaddr, sizeof(m_sockaddr))) { 96 | #else 97 | if (bind(m_sockfd, (struct sockaddr *)&m_sockaddr, sizeof(m_sockaddr)) < 0) { 98 | #endif 99 | perror("Socket bind failure"); 100 | m_failure = true; 101 | m_error = "Failed to bind socket"; 102 | return; 103 | } 104 | 105 | #if IS_3DS 106 | fcntl(m_sockfd, F_SETFL, fcntl(m_sockfd, F_GETFL, 0) | O_NONBLOCK); 107 | #endif 108 | 109 | #if IS_3DS 110 | if (listen(m_sockfd, 10)) { 111 | #else 112 | if (listen(m_sockfd, 10) < 0) { 113 | #endif 114 | perror("Failed to listen on socket"); 115 | m_failure = true; 116 | m_error = "Failed to listen"; 117 | return; 118 | } 119 | 120 | m_failure = false; 121 | } 122 | HTTP::Server::~Server() { close(m_sockfd); } 123 | 124 | bool HTTP::Server::tick() { 125 | struct sockaddr_in client; 126 | memset(&client, 0, sizeof(client)); 127 | 128 | uint32_t clientlen = sizeof(client); 129 | 130 | int connection = accept(m_sockfd, (struct sockaddr *)&client, &clientlen); 131 | if (connection < 0) { 132 | return true; 133 | } 134 | 135 | fcntl(connection, F_SETFL, fcntl(connection, F_GETFL, 0) & ~O_NONBLOCK); 136 | 137 | char buf[1024]; 138 | memset(&buf, 0, 1024); 139 | read(connection, buf, 1024); 140 | 141 | try { 142 | Request r; 143 | 144 | r = Request::from_raw(std::string(buf)); 145 | 146 | if (r.path() == "/") { 147 | r.set_path("/index.html"); 148 | } 149 | 150 | // Sanitize relative path (local file inclusion) 151 | // TODO: Make this more robust 152 | std::string path = r.path(); 153 | std::string to_find = ".."; 154 | 155 | while (true) { 156 | std::string::size_type i = path.find(to_find); 157 | 158 | if (i != std::string::npos) 159 | path.erase(i, to_find.length()); 160 | else 161 | break; 162 | } 163 | 164 | r.set_path(path); 165 | 166 | std::filesystem::path f{"./3ds-webserver-files" + r.path()}; 167 | auto normal_path = f.lexically_normal(); 168 | if (!std::filesystem::exists(normal_path)) { 169 | printf("%s %s - %d\n", r.method().c_str(), r.path().c_str(), 170 | PageNotFoundResponse.status().num()); 171 | std::string message = PageNotFoundResponse.to_raw(); 172 | send(connection, message.c_str(), message.size(), 0); 173 | } 174 | 175 | if (std::filesystem::is_directory(f)) { 176 | normal_path.append("index.html"); 177 | if (r.path().back() == '/') 178 | r.set_path(r.path() + "index.html"); 179 | else 180 | r.set_path(r.path() + "/index.html"); 181 | } 182 | 183 | struct stat result; 184 | stat(("./3ds-webserver-files" + r.path()).c_str(), &result); 185 | auto mod_time = result.st_mtime; 186 | 187 | auto modified_header = r.get_header("If-Modified-Since"); 188 | // TODO: Enable later 189 | if (!modified_header.empty()) { 190 | printf("We're checking dates!\n"); 191 | struct tm t = {0}; 192 | #if IS_LINUX 193 | // Check if cache 194 | if (strptime(modified_header.c_str(), "%a, %d %b %G %T %Z", &t) == 195 | nullptr) { 196 | printf("Parse time failed, ignoring cache"); 197 | } else { 198 | time_t if_modified_time = mktime(&t); 199 | if (if_modified_time > mod_time) { 200 | auto message = NotModified.to_raw(); 201 | send(connection, message.c_str(), message.size(), 0); 202 | } 203 | } 204 | #endif 205 | } else { 206 | std::ifstream file("./3ds-webserver-files" + r.path()); 207 | if (!file.is_open()) { 208 | } else { 209 | file.seekg(0, std::ios_base::end); // Seek to end of file. 210 | const unsigned int file_length = file.tellg(); 211 | file.seekg(0); 212 | std::vector file_data(file_length); 213 | file.read(&file_data[0], file_length); 214 | 215 | Request res; 216 | res.set_status(HTTP::Statuses[200]); 217 | res.set_body(std::string(file_data.begin(), file_data.end())); 218 | 219 | std::tm *t = std::gmtime(&mod_time); 220 | std::stringstream ss; 221 | ss << std::put_time(t, "%a, %d %b %G %T %Z"); 222 | res.add_header("Last-Modified", ss.str()); 223 | 224 | std::stringstream content_length_str; 225 | content_length_str << file_length; 226 | res.add_header("Content-Length", content_length_str.str()); 227 | 228 | time_t current_time; 229 | time(¤t_time); 230 | std::stringstream current_datetime; 231 | t = std::gmtime(¤t_time); 232 | current_datetime << std::put_time(t, "%a, %d %b %G %T %Z"); 233 | res.add_header("Date", current_datetime.str()); 234 | 235 | // Add the Content-type header 236 | auto ext = split(r.path(), '.').back(); 237 | 238 | res.add_header("Content-Type", HTTP::ext_to_mime(ext)); 239 | 240 | res.add_header("Cache-Control", "max-age=3600"); 241 | 242 | printf("%s %s - %d\n", r.method().c_str(), r.path().c_str(), 243 | res.status().num()); 244 | 245 | std::string final_res = res.to_raw(); 246 | send(connection, final_res.c_str(), final_res.size(), 0); 247 | file.close(); 248 | fcntl(m_sockfd, F_SETFL, fcntl(m_sockfd, F_GETFL, 0) | O_NONBLOCK); 249 | } 250 | } 251 | } catch (const std::runtime_error &e) { 252 | m_error = e.what(); 253 | printf("%s\n", m_error.c_str()); 254 | std::string message = InternalServerErrorResponse.to_raw(); 255 | send(connection, message.c_str(), message.size(), 0); 256 | } 257 | close(connection); 258 | 259 | return true; 260 | } 261 | -------------------------------------------------------------------------------- /cmake/Tools3DS.cmake: -------------------------------------------------------------------------------- 1 | ############################################################################ 2 | # Various macros for 3DS homebrews tools 3 | # 4 | # add_3dsx_target 5 | # ^^^^^^^^^^^^^^^ 6 | # 7 | # This macro has two signatures : 8 | # 9 | # ## add_3dsx_target(target [NO_SMDH]) 10 | # 11 | # Adds a target that generates a .3dsx file from `target`. If NO_SMDH is specified, no .smdh file will be generated. 12 | # 13 | # You can set the following variables to change the SMDH file : 14 | # 15 | # * APP_TITLE is the name of the app stored in the SMDH file (Optional) 16 | # * APP_DESCRIPTION is the description of the app stored in the SMDH file (Optional) 17 | # * APP_AUTHOR is the author of the app stored in the SMDH file (Optional) 18 | # * APP_ICON is the filename of the icon (.png), relative to the project folder. 19 | # If not set, it attempts to use one of the following (in this order): 20 | # - $(target).png 21 | # - icon.png 22 | # - $(libctru folder)/default_icon.png 23 | # 24 | # ## add_3dsx_target(target APP_TITLE APP_DESCRIPTION APP_AUTHOR [APP_ICON]) 25 | # 26 | # This version will produce the SMDH with tha values passed as arguments. Tha APP_ICON is optional and follows the same rule as the other version of `add_3dsx_target`. 27 | # 28 | # add_cia_target(target RSF IMAGE SOUND [APP_TITLE APP_DESCRIPTION APP_AUTHOR [APP_ICON]]) 29 | # ^^^^^^^^^^^^^^ 30 | # 31 | # Same as add_3dsx_target but for CIA files. 32 | # 33 | # RSF is the .rsf file to be given to makerom. 34 | # IMAGE is either a .png or a cgfximage file. 35 | # SOUND is either a .wav or a cwavaudio file. 36 | # 37 | # add_netload_target(name target_or_file) 38 | # ^^^^^^^^^^^^^^^^^^ 39 | # 40 | # Adds a target `name` that sends a .3dsx using the homebrew launcher netload system (3dslink). 41 | # target_or_file is either the name of a target or of file. 42 | # 43 | # add_binary_library(target input1 [input2 ...]) 44 | # ^^^^^^^^^^^^^^^^^^ 45 | # 46 | # /!\ Requires ASM to be enabled ( `enable_language(ASM)` or `project(yourprojectname C CXX ASM)`) 47 | # 48 | # Converts the files given as input to arrays of their binary data. This is useful to embed resources into your project. 49 | # For example, logo.bmp will generate the array `u8 logo_bmp[]` and its size `logo_bmp_size`. By linking this library, you 50 | # will also have access to a generated header file called `logo_bmp.h` which contains the declarations you need to use it. 51 | # 52 | # Note : All dots in the filename are converted to `_`, and if it starts with a number, `_` will be prepended. 53 | # For example 8x8.gas.tex would give the name _8x8_gas_tex. 54 | # 55 | # target_embed_file(target input1 [input2 ...]) 56 | # ^^^^^^^^^^^^^^^^^ 57 | # 58 | # Same as add_binary_library(tempbinlib input1 [input2 ...]) + target_link_libraries(target tempbinlib) 59 | # 60 | # add_shbin(output input [entrypoint] [shader_type]) 61 | # ^^^^^^^^^^^^^^^^^^^^^^^ 62 | # 63 | # Assembles the shader given as `input` into the file `output`. No file extension is added. 64 | # You can choose the shader assembler by setting SHADER_AS to `picasso` or `nihstro`. 65 | # 66 | # If `nihstro` is set as the assembler, entrypoint and shader_type will be used. 67 | # entrypoint is set to `main` by default 68 | # shader_type can be either VSHADER or GSHADER. By default it is VSHADER. 69 | # 70 | # generate_shbins(input1 [input2 ...]) 71 | # ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 72 | # 73 | # Assemble all the shader files given as input into .shbin files. Those will be located in the folder `shaders` of the build directory. 74 | # The names of the output files will be .shbin. vshader.pica will output shader.shbin but shader.vertex.pica will output shader.shbin too. 75 | # 76 | # add_shbin_library(target input1 [input2 ...]) 77 | # ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 78 | # 79 | # /!\ Requires ASM to be enabled ( `enable_language(ASM)` or `project(yourprojectname C CXX ASM)`) 80 | # 81 | # This is the same as calling generate_shbins and add_binary_library. This is the function to be used to reproduce devkitArm makefiles behaviour. 82 | # For example, add_shbin_library(shaders data/my1stshader.vsh.pica) will generate the target library `shaders` and you 83 | # will be able to use the shbin in your program by linking it, including `my1stshader_pica.h` and using `my1stshader_pica[]` and `my1stshader_pica_size`. 84 | # 85 | # target_embed_shader(target input1 [input2 ...]) 86 | # ^^^^^^^^^^^^^^^^^ 87 | # 88 | # Same as add_shbin_library(tempbinlib input1 [input2 ...]) + target_link_libraries(target tempbinlib) 89 | # 90 | ############################################################################ 91 | 92 | if(NOT 3DS) 93 | message(WARNING "Those tools can only be used if you are using the 3DS toolchain file. Please erase this build directory or create another one, and then use -DCMAKE_TOOLCHAIN_FILE=DevkitArm3DS.cmake when calling cmake for the 1st time. For more information, see the Readme.md for more information.") 94 | endif() 95 | 96 | get_filename_component(__tools3dsdir ${CMAKE_CURRENT_LIST_FILE} PATH) # Used to locate files to be used with configure_file 97 | 98 | message(STATUS "Looking for 3ds tools...") 99 | 100 | ############## 101 | ## 3DSXTOOL ## 102 | ############## 103 | if(NOT _3DSXTOOL) 104 | # message(STATUS "Looking for 3dsxtool...") 105 | find_program(_3DSXTOOL 3dsxtool ${DEVKITARM}/bin) 106 | if(_3DSXTOOL) 107 | message(STATUS "3dsxtool: ${_3DSXTOOL} - found") 108 | else() 109 | message(WARNING "3dsxtool - not found") 110 | endif() 111 | endif() 112 | 113 | 114 | ############## 115 | ## SMDHTOOL ## 116 | ############## 117 | if(NOT SMDHTOOL) 118 | # message(STATUS "Looking for smdhtool...") 119 | find_program(SMDHTOOL smdhtool ${DEVKITARM}/bin) 120 | if(SMDHTOOL) 121 | message(STATUS "smdhtool: ${SMDHTOOL} - found") 122 | else() 123 | message(WARNING "smdhtool - not found") 124 | endif() 125 | endif() 126 | 127 | ################ 128 | ## BANNERTOOL ## 129 | ################ 130 | if(NOT BANNERTOOL) 131 | # message(STATUS "Looking for bannertool...") 132 | find_program(BANNERTOOL bannertool ${DEVKITARM}/bin) 133 | if(BANNERTOOL) 134 | message(STATUS "bannertool: ${BANNERTOOL} - found") 135 | else() 136 | message(WARNING "bannertool - not found") 137 | endif() 138 | endif() 139 | 140 | set(FORCE_SMDHTOOL FALSE CACHE BOOL "Force the use of smdhtool instead of bannertool") 141 | 142 | ############# 143 | ## MAKEROM ## 144 | ############# 145 | if(NOT MAKEROM) 146 | # message(STATUS "Looking for makerom...") 147 | find_program(MAKEROM makerom ${DEVKITARM}/bin) 148 | if(MAKEROM) 149 | message(STATUS "makerom: ${MAKEROM} - found") 150 | else() 151 | message(WARNING "makerom - not found") 152 | endif() 153 | endif() 154 | 155 | 156 | 157 | ############# 158 | ## STRIP ## 159 | ############# 160 | if(NOT STRIP) 161 | # message(STATUS "Looking for strip...") 162 | find_program(STRIP arm-none-eabi-strip ${DEVKITARM}/bin) 163 | if(STRIP) 164 | message(STATUS "strip: ${STRIP} - found") 165 | else() 166 | message(WARNING "strip - not found") 167 | endif() 168 | endif() 169 | 170 | 171 | 172 | ############# 173 | ## BIN2S ## 174 | ############# 175 | if(NOT BIN2S) 176 | # message(STATUS "Looking for bin2s...") 177 | find_program(BIN2S bin2s ${DEVKITARM}/bin) 178 | if(BIN2S) 179 | message(STATUS "bin2s: ${BIN2S} - found") 180 | else() 181 | message(WARNING "bin2s - not found") 182 | endif() 183 | endif() 184 | 185 | ############### 186 | ## 3DSLINK ## 187 | ############### 188 | if(NOT _3DSLINK) 189 | # message(STATUS "Looking for 3dslink...") 190 | find_program(_3DSLINK 3dslink ${DEVKITARM}/bin) 191 | if(_3DSLINK) 192 | message(STATUS "3dslink: ${_3DSLINK} - found") 193 | else() 194 | message(WARNING "3dslink - not found") 195 | endif() 196 | endif() 197 | 198 | ############# 199 | ## PICASSO ## 200 | ############# 201 | if(NOT PICASSO_EXE) 202 | # message(STATUS "Looking for Picasso...") 203 | find_program(PICASSO_EXE picasso ${DEVKITARM}/bin) 204 | if(PICASSO_EXE) 205 | message(STATUS "Picasso: ${PICASSO_EXE} - found") 206 | set(SHADER_AS picasso CACHE STRING "The shader assembler to be used. Allowed values are 'none', 'picasso' or 'nihstro'") 207 | else() 208 | message(STATUS "Picasso - not found") 209 | endif() 210 | endif() 211 | 212 | 213 | ############# 214 | ## NIHSTRO ## 215 | ############# 216 | 217 | if(NOT NIHSTRO_AS) 218 | # message(STATUS "Looking for nihstro...") 219 | find_program(NIHSTRO_AS nihstro ${DEVKITARM}/bin) 220 | if(NIHSTRO_AS) 221 | message(STATUS "nihstro: ${NIHSTRO_AS} - found") 222 | set(SHADER_AS nihstro CACHE STRING "The shader assembler to be used. Allowed values are 'none', 'picasso' or 'nihstro'") 223 | else() 224 | message(STATUS "nihstro - not found") 225 | endif() 226 | endif() 227 | 228 | set(SHADER_AS none CACHE STRING "The shader assembler to be used. Allowed values are 'none', 'picasso' or 'nihstro'") 229 | 230 | ############################### 231 | ############################### 232 | ######## MACROS ######### 233 | ############################### 234 | ############################### 235 | 236 | 237 | ################### 238 | ### EXECUTABLES ### 239 | ################### 240 | 241 | 242 | function(__add_smdh target APP_TITLE APP_DESCRIPTION APP_AUTHOR APP_ICON) 243 | if(BANNERTOOL AND NOT FORCE_SMDHTOOL) 244 | set(__SMDH_COMMAND ${BANNERTOOL} makesmdh -s ${APP_TITLE} -l ${APP_DESCRIPTION} -p ${APP_AUTHOR} -i ${APP_ICON} -o ${CMAKE_CURRENT_BINARY_DIR}/${target}) 245 | else() 246 | set(__SMDH_COMMAND ${SMDHTOOL} --create ${APP_TITLE} ${APP_DESCRIPTION} ${APP_AUTHOR} ${APP_ICON} ${CMAKE_CURRENT_BINARY_DIR}/${target}) 247 | endif() 248 | add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${target} 249 | COMMAND ${__SMDH_COMMAND} 250 | WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR} 251 | DEPENDS ${APP_ICON} 252 | VERBATIM 253 | ) 254 | endfunction() 255 | 256 | function(add_3dsx_target target) 257 | get_filename_component(target_we ${target} NAME_WE) 258 | if((NOT (${ARGC} GREATER 1 AND "${ARGV1}" STREQUAL "NO_SMDH") ) OR (${ARGC} GREATER 3) ) 259 | if(${ARGC} GREATER 3) 260 | set(APP_TITLE ${ARGV1}) 261 | set(APP_DESCRIPTION ${ARGV2}) 262 | set(APP_AUTHOR ${ARGV3}) 263 | endif() 264 | if(${ARGC} EQUAL 5) 265 | set(APP_ICON ${ARGV4}) 266 | endif() 267 | if(NOT APP_TITLE) 268 | set(APP_TITLE ${target}) 269 | endif() 270 | if(NOT APP_DESCRIPTION) 271 | set(APP_DESCRIPTION "Built with devkitARM & libctru") 272 | endif() 273 | if(NOT APP_AUTHOR) 274 | set(APP_AUTHOR "Unspecified Author") 275 | endif() 276 | if(NOT APP_ICON) 277 | if(EXISTS ${target}.png) 278 | set(APP_ICON ${target}.png) 279 | elseif(EXISTS icon.png) 280 | set(APP_ICON icon.png) 281 | elseif(CTRULIB) 282 | set(APP_ICON ${CTRULIB}/default_icon.png) 283 | else() 284 | message(FATAL_ERROR "No icon found ! Please use NO_SMDH or provide some icon.") 285 | endif() 286 | endif() 287 | if( NOT ${target_we}.smdh) 288 | __add_smdh(${target_we}.smdh ${APP_TITLE} ${APP_DESCRIPTION} ${APP_AUTHOR} ${APP_ICON}) 289 | endif() 290 | add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${target_we}.3dsx 291 | COMMAND ${_3DSXTOOL} $ ${CMAKE_CURRENT_BINARY_DIR}/${target_we}.3dsx --smdh=${CMAKE_CURRENT_BINARY_DIR}/${target_we}.smdh 292 | DEPENDS ${target} ${CMAKE_CURRENT_BINARY_DIR}/${target_we}.smdh 293 | VERBATIM 294 | ) 295 | else() 296 | message(STATUS "No smdh file will be generated") 297 | add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${target_we}.3dsx 298 | COMMAND ${_3DSXTOOL} $ ${CMAKE_CURRENT_BINARY_DIR}/${target_we}.3dsx 299 | DEPENDS ${target} 300 | VERBATIM 301 | ) 302 | endif() 303 | add_custom_target(${target_we}_3dsx ALL SOURCES ${CMAKE_CURRENT_BINARY_DIR}/${target_we}.3dsx) 304 | set_target_properties(${target} PROPERTIES LINK_FLAGS "-specs=3dsx.specs") 305 | endfunction() 306 | 307 | function(__add_ncch_banner target IMAGE SOUND) 308 | if(IMAGE MATCHES ".*\\.png$") 309 | set(IMG_PARAM -i ${IMAGE}) 310 | else() 311 | set(IMG_PARAM -ci ${IMAGE}) 312 | endif() 313 | if(SOUND MATCHES ".*\\.wav$") 314 | set(SND_PARAM -a ${SOUND}) 315 | else() 316 | set(SND_PARAM -ca ${SOUND}) 317 | endif() 318 | add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${target} 319 | COMMAND ${BANNERTOOL} makebanner -o ${CMAKE_CURRENT_BINARY_DIR}/${target} ${IMG_PARAM} ${SND_PARAM} 320 | WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR} 321 | DEPENDS ${IMAGE} ${SOUND} 322 | VERBATIM 323 | ) 324 | endfunction() 325 | 326 | function(add_cia_target target RSF IMAGE SOUND ) 327 | get_filename_component(target_we ${target} NAME_WE) 328 | if(${ARGC} GREATER 6) 329 | set(APP_TITLE ${ARGV4}) 330 | set(APP_DESCRIPTION ${ARGV5}) 331 | set(APP_AUTHOR ${ARGV6}) 332 | endif() 333 | if(${ARGC} EQUAL 8) 334 | set(APP_ICON ${ARGV7}) 335 | endif() 336 | if(NOT APP_TITLE) 337 | set(APP_TITLE ${target}) 338 | endif() 339 | if(NOT APP_DESCRIPTION) 340 | set(APP_DESCRIPTION "Built with devkitARM & libctru") 341 | endif() 342 | if(NOT APP_AUTHOR) 343 | set(APP_AUTHOR "Unspecified Author") 344 | endif() 345 | if(NOT APP_ICON) 346 | if(EXISTS ${target}.png) 347 | set(APP_ICON ${target}.png) 348 | elseif(EXISTS icon.png) 349 | set(APP_ICON icon.png) 350 | elseif(CTRULIB) 351 | set(APP_ICON ${CTRULIB}/default_icon.png) 352 | else() 353 | message(FATAL_ERROR "No icon found ! Please use NO_SMDH or provide some icon.") 354 | endif() 355 | endif() 356 | if( NOT ${target_we}.smdh) 357 | __add_smdh(${target_we}.smdh ${APP_TITLE} ${APP_DESCRIPTION} ${APP_AUTHOR} ${APP_ICON}) 358 | endif() 359 | __add_ncch_banner(${target_we}.bnr ${IMAGE} ${SOUND}) 360 | add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${target_we}.cia 361 | COMMAND ${STRIP} -o $-stripped $ 362 | COMMAND ${MAKEROM} -f cia 363 | -target t 364 | -exefslogo 365 | -o ${CMAKE_CURRENT_BINARY_DIR}/${target_we}.cia 366 | -elf $-stripped 367 | -rsf ${RSF} 368 | -banner ${CMAKE_CURRENT_BINARY_DIR}/${target_we}.bnr 369 | -icon ${CMAKE_CURRENT_BINARY_DIR}/${target_we}.smdh 370 | DEPENDS ${target} ${RSF} ${CMAKE_CURRENT_BINARY_DIR}/${target_we}.bnr ${CMAKE_CURRENT_BINARY_DIR}/${target_we}.smdh 371 | WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR} 372 | VERBATIM 373 | ) 374 | 375 | add_custom_target(${target_we}_cia ALL SOURCES ${CMAKE_CURRENT_BINARY_DIR}/${target_we}.cia) 376 | set_target_properties(${target} PROPERTIES LINK_FLAGS "-specs=3dsx.specs") 377 | endfunction() 378 | 379 | macro(add_netload_target name target) 380 | set(NETLOAD_IP "" CACHE STRING "The ip address of the 3ds when using netload.") 381 | if(NETLOAD_IP) 382 | set(__NETLOAD_IP_OPTION -a ${NETLOAD_IP}) 383 | endif() 384 | if(NOT TARGET ${target}) 385 | message("NOT ${target}") 386 | set(FILE ${target}) 387 | else() 388 | set(FILE ${CMAKE_CURRENT_BINARY_DIR}/${target}.3dsx) 389 | endif() 390 | add_custom_target(${name} 391 | COMMAND ${_3DSLINK} ${FILE} ${__NETLOAD_IP_OPTION} 392 | DEPENDS ${FILE} 393 | ) 394 | endmacro() 395 | 396 | ###################### 397 | ### File embedding ### 398 | ###################### 399 | 400 | macro(add_binary_library libtarget) 401 | if(NOT ${ARGC} GREATER 1) 402 | message(FATAL_ERROR "add_binary_library : Argument error (no input files)") 403 | endif() 404 | get_cmake_property(ENABLED_LANGUAGES ENABLED_LANGUAGES) 405 | if(NOT ENABLED_LANGUAGES MATCHES ".*ASM.*") 406 | message(FATAL_ERROR "You have to enable ASM in order to use add_binary_library (or any target_embed_* which relies on it). Use enable_language(ASM) in your CMakeLists. Currently enabled languages are ${ENABLED_LANGUAGES}") 407 | endif() 408 | 409 | 410 | foreach(__file ${ARGN}) 411 | get_filename_component(__file_wd ${__file} NAME) 412 | string(REGEX REPLACE "^([0-9])" "_\\1" __BIN_FILE_NAME ${__file_wd}) # add '_' if the file name starts by a number 413 | string(REGEX REPLACE "[-./]" "_" __BIN_FILE_NAME ${__BIN_FILE_NAME}) 414 | 415 | #Generate the header file 416 | configure_file(${__tools3dsdir}/bin2s_header.h.in ${CMAKE_CURRENT_BINARY_DIR}/${libtarget}_include/${__BIN_FILE_NAME}.h) 417 | endforeach() 418 | 419 | file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/binaries_asm) 420 | # Generate the assembly file, and create the new target 421 | add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/binaries_asm/${libtarget}.s 422 | COMMAND ${BIN2S} ${ARGN} > ${CMAKE_CURRENT_BINARY_DIR}/binaries_asm/${libtarget}.s 423 | DEPENDS ${ARGN} 424 | WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR} 425 | ) 426 | 427 | add_library(${libtarget} ${CMAKE_CURRENT_BINARY_DIR}/binaries_asm/${libtarget}.s) 428 | target_include_directories(${libtarget} INTERFACE ${CMAKE_CURRENT_BINARY_DIR}/${libtarget}_include) 429 | endmacro() 430 | 431 | macro(target_embed_file _target) 432 | if(NOT ${ARGC} GREATER 1) 433 | message(FATAL_ERROR "target_embed_file : Argument error (no input files)") 434 | endif() 435 | get_filename_component(__1st_file_wd ${ARGV1} NAME) 436 | add_binary_library(__${_target}_embed_${__1st_file_wd} ${ARGN}) 437 | target_link_libraries(${_target} __${_target}_embed_${__1st_file_wd}) 438 | endmacro() 439 | 440 | ################### 441 | ##### SHADERS ##### 442 | ################### 443 | 444 | macro(add_shbin OUTPUT INPUT ) 445 | 446 | if(SHADER_AS STREQUAL "picasso") 447 | 448 | if(${ARGC} GREATER 2) 449 | message(WARNING "Picasso doesn't support changing the entrypoint or shader type") 450 | endif() 451 | add_custom_command(OUTPUT ${OUTPUT} COMMAND ${PICASSO_EXE} -o ${OUTPUT} ${INPUT} WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}) 452 | 453 | elseif(SHADER_AS STREQUAL "nihstro") 454 | if(NOT NIHSTRO_AS) 455 | message(SEND_ERROR "SHADER_AS is set to nihstro, but nihstro wasn't found. Please set NIHSTRO_AS.") 456 | endif() 457 | if(${ARGC} GREATER 2) 458 | if(${ARGV2} EQUAL GSHADER) 459 | set(SHADER_TYPE_FLAG "-g") 460 | elseif(NOT ${ARGV2} EQUAL VSHADER) 461 | set(_ENTRYPOINT ${ARGV2}) 462 | endif() 463 | endif() 464 | if(${ARGC} GREATER 3) 465 | if(${ARGV2} EQUAL GSHADER) 466 | set(SHADER_TYPE_FLAG "-g") 467 | elseif(NOT ${ARGV3} EQUAL VSHADER) 468 | set(_ENTRYPOINT ${ARGV3}) 469 | endif() 470 | endif() 471 | if(NOT _ENTRYPOINT) 472 | set(_ENTRYPOINT "main") 473 | endif() 474 | add_custom_command(OUTPUT ${OUTPUT} COMMAND ${NIHSTRO_AS} ${INPUT} -o ${OUTPUT} -e ${_ENTRYPOINT} ${SHADER_TYPE_FLAG} WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}) 475 | 476 | else() 477 | message(FATAL_ERROR "Please set SHADER_AS to 'picasso' or 'nihstro' if you use the shbin feature.") 478 | endif() 479 | 480 | endmacro() 481 | 482 | function(generate_shbins) 483 | file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/shaders) 484 | foreach(__shader_file ${ARGN}) 485 | get_filename_component(__shader_file_we ${__shader_file} NAME_WE) 486 | #Generate the shbin file 487 | list(APPEND __SHADERS_BIN_FILES ${CMAKE_CURRENT_BINARY_DIR}/shaders/${__shader_file_we}.shbin) 488 | add_shbin(${CMAKE_CURRENT_BINARY_DIR}/shaders/${__shader_file_we}.shbin ${__shader_file}) 489 | endforeach() 490 | endfunction() 491 | 492 | function(add_shbin_library libtarget) 493 | file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/shaders) 494 | foreach(__shader_file ${ARGN}) 495 | get_filename_component(__shader_file_we ${__shader_file} NAME_WE) 496 | #Generate the shbin file 497 | list(APPEND __SHADERS_BIN_FILES ${CMAKE_CURRENT_BINARY_DIR}/shaders/${__shader_file_we}.shbin) 498 | add_shbin(${CMAKE_CURRENT_BINARY_DIR}/shaders/${__shader_file_we}.shbin ${__shader_file}) 499 | endforeach() 500 | add_binary_library(${libtarget} ${__SHADERS_BIN_FILES}) 501 | endfunction() 502 | 503 | 504 | macro(target_embed_shader _target) 505 | if(NOT ${ARGC} GREATER 1) 506 | message(FATAL_ERROR "target_embed_shader : Argument error (no input files)") 507 | endif() 508 | get_filename_component(__1st_file_wd ${ARGV1} NAME) 509 | add_shbin_library(__${_target}_embed_${__1st_file_wd} ${ARGN}) 510 | target_link_libraries(${_target} __${_target}_embed_${__1st_file_wd}) 511 | endmacro() 512 | -------------------------------------------------------------------------------- /src/server/request.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | namespace HTTP { 9 | enum Method { 10 | CONNECT, 11 | DELETE, 12 | GET, 13 | HEAD, 14 | OPTIONS, 15 | POST, 16 | PUT, 17 | TRACE, 18 | }; 19 | 20 | inline Method method_from_string(std::string name) { 21 | transform(name.begin(), name.end(), name.begin(), ::toupper); 22 | if (name == "CONNECT") 23 | return CONNECT; 24 | else if (name == "DELETE") 25 | return DELETE; 26 | else if (name == "GET") 27 | return GET; 28 | else if (name == "HEAD") 29 | return HEAD; 30 | else if (name == "OPTIONS") 31 | return OPTIONS; 32 | else if (name == "POST") 33 | return POST; 34 | else if (name == "PUT") 35 | return PUT; 36 | else if (name == "TRACE") 37 | return TRACE; 38 | throw std::runtime_error("Invalid method"); 39 | } 40 | 41 | inline std::string method_to_string(Method m) { 42 | if (m == CONNECT) 43 | return "CONNECT"; 44 | else if (m == DELETE) 45 | return "DELETE"; 46 | else if (m == GET) 47 | return "GET"; 48 | else if (m == HEAD) 49 | return "HEAD"; 50 | else if (m == OPTIONS) 51 | return "OPTIONS"; 52 | else if (m == POST) 53 | return "POST"; 54 | else if (m == PUT) 55 | return "PUT"; 56 | else if (m == TRACE) 57 | return "TRACE"; 58 | throw std::runtime_error("Invalid method"); 59 | } 60 | 61 | inline std::string ext_to_mime(const std::string &ext) { 62 | // https://www.freeformatter.com/mime-types-list.html 63 | std::map mime_map = { 64 | {"123", "application/vnd.lotus-1-2-3"}, 65 | {"1km", "application/vnd.1000minds.decision-model+xml"}, 66 | {"3dml", "text/vnd.in3d.3dml"}, 67 | {"3ds", "image/x-3ds"}, 68 | {"3g2", "video/3gpp2"}, 69 | {"3gp", "video/3gpp"}, 70 | {"3gpp", "video/3gpp"}, 71 | {"3mf", "model/3mf"}, 72 | {"7z", "application/x-7z-compressed"}, 73 | {"aab", "application/x-authorware-bin"}, 74 | {"aac", "audio/x-aac"}, 75 | {"aam", "application/x-authorware-map"}, 76 | {"aas", "application/x-authorware-seg"}, 77 | {"abw", "application/x-abiword"}, 78 | {"ac", "application/vnd.nokia.n-gage.ac+xml"}, 79 | {"acc", "application/vnd.americandynamics.acc"}, 80 | {"ace", "application/x-ace-compressed"}, 81 | {"acu", "application/vnd.acucobol"}, 82 | {"acutc", "application/vnd.acucorp"}, 83 | {"adp", "audio/adpcm"}, 84 | {"aep", "application/vnd.audiograph"}, 85 | {"afm", "application/x-font-type1"}, 86 | {"afp", "application/vnd.ibm.modcap"}, 87 | {"age", "application/vnd.age"}, 88 | {"ahead", "application/vnd.ahead.space"}, 89 | {"ai", "application/postscript"}, 90 | {"aif", "audio/x-aiff"}, 91 | {"aifc", "audio/x-aiff"}, 92 | {"aiff", "audio/x-aiff"}, 93 | {"air", "application/vnd.adobe.air-application-installer-package+zip"}, 94 | {"ait", "application/vnd.dvb.ait"}, 95 | {"ami", "application/vnd.amiga.ami"}, 96 | {"amr", "audio/amr"}, 97 | {"apk", "application/vnd.android.package-archive"}, 98 | {"apng", "image/apng"}, 99 | {"appcache", "text/cache-manifest"}, 100 | {"application", "application/x-ms-application"}, 101 | {"apr", "application/vnd.lotus-approach"}, 102 | {"arc", "application/x-freearc"}, 103 | {"arj", "application/x-arj"}, 104 | {"asc", "application/pgp-signature"}, 105 | {"asf", "video/x-ms-asf"}, 106 | {"asm", "text/x-asm"}, 107 | {"aso", "application/vnd.accpac.simply.aso"}, 108 | {"asx", "video/x-ms-asf"}, 109 | {"atc", "application/vnd.acucorp"}, 110 | {"atom", "application/atom+xml"}, 111 | {"atomcat", "application/atomcat+xml"}, 112 | {"atomdeleted", "application/atomdeleted+xml"}, 113 | {"atomsvc", "application/atomsvc+xml"}, 114 | {"atx", "application/vnd.antix.game-component"}, 115 | {"au", "audio/basic"}, 116 | {"avi", "video/x-msvideo"}, 117 | {"avif", "image/avif"}, 118 | {"aw", "application/applixware"}, 119 | {"azf", "application/vnd.airzip.filesecure.azf"}, 120 | {"azs", "application/vnd.airzip.filesecure.azs"}, 121 | {"azv", "image/vnd.airzip.accelerator.azv"}, 122 | {"azw", "application/vnd.amazon.ebook"}, 123 | {"b16", "image/vnd.pco.b16"}, 124 | {"bat", "application/x-msdownload"}, 125 | {"bcpio", "application/x-bcpio"}, 126 | {"bdf", "application/x-font-bdf"}, 127 | {"bdm", "application/vnd.syncml.dm+wbxml"}, 128 | {"bdoc", "application/x-bdoc"}, 129 | {"bed", "application/vnd.realvnc.bed"}, 130 | {"bh2", "application/vnd.fujitsu.oasysprs"}, 131 | {"bin", "application/octet-stream"}, 132 | {"blb", "application/x-blorb"}, 133 | {"blorb", "application/x-blorb"}, 134 | {"bmi", "application/vnd.bmi"}, 135 | {"bmml", "application/vnd.balsamiq.bmml+xml"}, 136 | {"bmp", "image/x-ms-bmp"}, 137 | {"book", "application/vnd.framemaker"}, 138 | {"box", "application/vnd.previewsystems.box"}, 139 | {"boz", "application/x-bzip2"}, 140 | {"bpk", "application/octet-stream"}, 141 | {"bsp", "model/vnd.valve.source.compiled-map"}, 142 | {"btif", "image/prs.btif"}, 143 | {"buffer", "application/octet-stream"}, 144 | {"bz", "application/x-bzip"}, 145 | {"bz2", "application/x-bzip2"}, 146 | {"c", "text/x-c"}, 147 | {"c11amc", "application/vnd.cluetrust.cartomobile-config"}, 148 | {"c11amz", "application/vnd.cluetrust.cartomobile-config-pkg"}, 149 | {"c4d", "application/vnd.clonk.c4group"}, 150 | {"c4f", "application/vnd.clonk.c4group"}, 151 | {"c4g", "application/vnd.clonk.c4group"}, 152 | {"c4p", "application/vnd.clonk.c4group"}, 153 | {"c4u", "application/vnd.clonk.c4group"}, 154 | {"cab", "application/vnd.ms-cab-compressed"}, 155 | {"caf", "audio/x-caf"}, 156 | {"cap", "application/vnd.tcpdump.pcap"}, 157 | {"car", "application/vnd.curl.car"}, 158 | {"cat", "application/vnd.ms-pki.seccat"}, 159 | {"cb7", "application/x-cbr"}, 160 | {"cba", "application/x-cbr"}, 161 | {"cbr", "application/x-cbr"}, 162 | {"cbt", "application/x-cbr"}, 163 | {"cbz", "application/x-cbr"}, 164 | {"cc", "text/x-c"}, 165 | {"cco", "application/x-cocoa"}, 166 | {"cct", "application/x-director"}, 167 | {"ccxml", "application/ccxml+xml"}, 168 | {"cdbcmsg", "application/vnd.contact.cmsg"}, 169 | {"cdf", "application/x-netcdf"}, 170 | {"cdfx", "application/cdfx+xml"}, 171 | {"cdkey", "application/vnd.mediastation.cdkey"}, 172 | {"cdmia", "application/cdmi-capability"}, 173 | {"cdmic", "application/cdmi-container"}, 174 | {"cdmid", "application/cdmi-domain"}, 175 | {"cdmio", "application/cdmi-object"}, 176 | {"cdmiq", "application/cdmi-queue"}, 177 | {"cdx", "chemical/x-cdx"}, 178 | {"cdxml", "application/vnd.chemdraw+xml"}, 179 | {"cdy", "application/vnd.cinderella"}, 180 | {"cer", "application/pkix-cert"}, 181 | {"cfs", "application/x-cfs-compressed"}, 182 | {"cgm", "image/cgm"}, 183 | {"chat", "application/x-chat"}, 184 | {"chm", "application/vnd.ms-htmlhelp"}, 185 | {"chrt", "application/vnd.kde.kchart"}, 186 | {"cif", "chemical/x-cif"}, 187 | {"cii", "application/vnd.anser-web-certificate-issue-initiation"}, 188 | {"cil", "application/vnd.ms-artgalry"}, 189 | {"cjs", "application/node"}, 190 | {"cla", "application/vnd.claymore"}, 191 | {"class", "application/java-vm"}, 192 | {"clkk", "application/vnd.crick.clicker.keyboard"}, 193 | {"clkp", "application/vnd.crick.clicker.palette"}, 194 | {"clkt", "application/vnd.crick.clicker.template"}, 195 | {"clkw", "application/vnd.crick.clicker.wordbank"}, 196 | {"clkx", "application/vnd.crick.clicker"}, 197 | {"clp", "application/x-msclip"}, 198 | {"cmc", "application/vnd.cosmocaller"}, 199 | {"cmdf", "chemical/x-cmdf"}, 200 | {"cml", "chemical/x-cml"}, 201 | {"cmp", "application/vnd.yellowriver-custom-menu"}, 202 | {"cmx", "image/x-cmx"}, 203 | {"cod", "application/vnd.rim.cod"}, 204 | {"coffee", "text/coffeescript"}, 205 | {"com", "application/x-msdownload"}, 206 | {"conf", "text/plain"}, 207 | {"cpio", "application/x-cpio"}, 208 | {"cpp", "text/x-c"}, 209 | {"cpt", "application/mac-compactpro"}, 210 | {"crd", "application/x-mscardfile"}, 211 | {"crl", "application/pkix-crl"}, 212 | {"crt", "application/x-x509-ca-cert"}, 213 | {"crx", "application/x-chrome-extension"}, 214 | {"cryptonote", "application/vnd.rig.cryptonote"}, 215 | {"csh", "application/x-csh"}, 216 | {"csl", "application/vnd.citationstyles.style+xml"}, 217 | {"csml", "chemical/x-csml"}, 218 | {"csp", "application/vnd.commonspace"}, 219 | {"css", "text/css"}, 220 | {"cst", "application/x-director"}, 221 | {"csv", "text/csv"}, 222 | {"cu", "application/cu-seeme"}, 223 | {"curl", "text/vnd.curl"}, 224 | {"cww", "application/prs.cww"}, 225 | {"cxt", "application/x-director"}, 226 | {"cxx", "text/x-c"}, 227 | {"dae", "model/vnd.collada+xml"}, 228 | {"daf", "application/vnd.mobius.daf"}, 229 | {"dart", "application/vnd.dart"}, 230 | {"dataless", "application/vnd.fdsn.seed"}, 231 | {"davmount", "application/davmount+xml"}, 232 | {"dbf", "application/vnd.dbf"}, 233 | {"dbk", "application/docbook+xml"}, 234 | {"dcr", "application/x-director"}, 235 | {"dcurl", "text/vnd.curl.dcurl"}, 236 | {"dd2", "application/vnd.oma.dd2+xml"}, 237 | {"ddd", "application/vnd.fujixerox.ddd"}, 238 | {"ddf", "application/vnd.syncml.dmddf+xml"}, 239 | {"dds", "image/vnd.ms-dds"}, 240 | {"deb", "application/x-debian-package"}, 241 | {"def", "text/plain"}, 242 | {"deploy", "application/octet-stream"}, 243 | {"der", "application/x-x509-ca-cert"}, 244 | {"dfac", "application/vnd.dreamfactory"}, 245 | {"dgc", "application/x-dgc-compressed"}, 246 | {"dic", "text/x-c"}, 247 | {"dir", "application/x-director"}, 248 | {"dis", "application/vnd.mobius.dis"}, 249 | {"disposition-notification", "message/disposition-notification"}, 250 | {"dist", "application/octet-stream"}, 251 | {"distz", "application/octet-stream"}, 252 | {"djv", "image/vnd.djvu"}, 253 | {"djvu", "image/vnd.djvu"}, 254 | {"dll", "application/x-msdownload"}, 255 | {"dmg", "application/x-apple-diskimage"}, 256 | {"dmp", "application/vnd.tcpdump.pcap"}, 257 | {"dms", "application/octet-stream"}, 258 | {"dna", "application/vnd.dna"}, 259 | {"doc", "application/msword"}, 260 | {"docm", "application/vnd.ms-word.document.macroenabled.12"}, 261 | {"docx", "application/" 262 | "vnd.openxmlformats-officedocument.wordprocessingml.document"}, 263 | {"dot", "application/msword"}, 264 | {"dotm", "application/vnd.ms-word.template.macroenabled.12"}, 265 | {"dotx", "application/" 266 | "vnd.openxmlformats-officedocument.wordprocessingml.template"}, 267 | {"dp", "application/vnd.osgi.dp"}, 268 | {"dpg", "application/vnd.dpgraph"}, 269 | {"dra", "audio/vnd.dra"}, 270 | {"drle", "image/dicom-rle"}, 271 | {"dsc", "text/prs.lines.tag"}, 272 | {"dssc", "application/dssc+der"}, 273 | {"dtb", "application/x-dtbook+xml"}, 274 | {"dtd", "application/xml-dtd"}, 275 | {"dts", "audio/vnd.dts"}, 276 | {"dtshd", "audio/vnd.dts.hd"}, 277 | {"dump", "application/octet-stream"}, 278 | {"dvb", "video/vnd.dvb.file"}, 279 | {"dvi", "application/x-dvi"}, 280 | {"dwd", "application/atsc-dwd+xml"}, 281 | {"dwf", "model/vnd.dwf"}, 282 | {"dwg", "image/vnd.dwg"}, 283 | {"dxf", "image/vnd.dxf"}, 284 | {"dxp", "application/vnd.spotfire.dxp"}, 285 | {"dxr", "application/x-director"}, 286 | {"ear", "application/java-archive"}, 287 | {"ecelp4800", "audio/vnd.nuera.ecelp4800"}, 288 | {"ecelp7470", "audio/vnd.nuera.ecelp7470"}, 289 | {"ecelp9600", "audio/vnd.nuera.ecelp9600"}, 290 | {"ecma", "application/ecmascript"}, 291 | {"edm", "application/vnd.novadigm.edm"}, 292 | {"edx", "application/vnd.novadigm.edx"}, 293 | {"efif", "application/vnd.picsel"}, 294 | {"ei6", "application/vnd.pg.osasli"}, 295 | {"elc", "application/octet-stream"}, 296 | {"emf", "image/emf"}, 297 | {"eml", "message/rfc822"}, 298 | {"emma", "application/emma+xml"}, 299 | {"emotionml", "application/emotionml+xml"}, 300 | {"emz", "application/x-msmetafile"}, 301 | {"eol", "audio/vnd.digital-winds"}, 302 | {"eot", "application/vnd.ms-fontobject"}, 303 | {"eps", "application/postscript"}, 304 | {"epub", "application/epub+zip"}, 305 | {"es", "application/ecmascript"}, 306 | {"es3", "application/vnd.eszigno3+xml"}, 307 | {"esa", "application/vnd.osgi.subsystem"}, 308 | {"esf", "application/vnd.epson.esf"}, 309 | {"et3", "application/vnd.eszigno3+xml"}, 310 | {"etx", "text/x-setext"}, 311 | {"eva", "application/x-eva"}, 312 | {"evy", "application/x-envoy"}, 313 | {"exe", "application/x-msdownload"}, 314 | {"exi", "application/exi"}, 315 | {"exp", "application/express"}, 316 | {"exr", "image/aces"}, 317 | {"ext", "application/vnd.novadigm.ext"}, 318 | {"ez", "application/andrew-inset"}, 319 | {"ez2", "application/vnd.ezpix-album"}, 320 | {"ez3", "application/vnd.ezpix-package"}, 321 | {"f", "text/x-fortran"}, 322 | {"f4v", "video/x-f4v"}, 323 | {"f77", "text/x-fortran"}, 324 | {"f90", "text/x-fortran"}, 325 | {"fbs", "image/vnd.fastbidsheet"}, 326 | {"fcdt", "application/vnd.adobe.formscentral.fcdt"}, 327 | {"fcs", "application/vnd.isac.fcs"}, 328 | {"fdf", "application/vnd.fdf"}, 329 | {"fdt", "application/fdt+xml"}, 330 | {"fe_launch", "application/vnd.denovo.fcselayout-link"}, 331 | {"fg5", "application/vnd.fujitsu.oasysgp"}, 332 | {"fgd", "application/x-director"}, 333 | {"fh", "image/x-freehand"}, 334 | {"fh4", "image/x-freehand"}, 335 | {"fh5", "image/x-freehand"}, 336 | {"fh7", "image/x-freehand"}, 337 | {"fhc", "image/x-freehand"}, 338 | {"fig", "application/x-xfig"}, 339 | {"fits", "image/fits"}, 340 | {"flac", "audio/x-flac"}, 341 | {"fli", "video/x-fli"}, 342 | {"flo", "application/vnd.micrografx.flo"}, 343 | {"flv", "video/x-flv"}, 344 | {"flw", "application/vnd.kde.kivio"}, 345 | {"flx", "text/vnd.fmi.flexstor"}, 346 | {"fly", "text/vnd.fly"}, 347 | {"fm", "application/vnd.framemaker"}, 348 | {"fnc", "application/vnd.frogans.fnc"}, 349 | {"fo", "application/vnd.software602.filler.form+xml"}, 350 | {"for", "text/x-fortran"}, 351 | {"fpx", "image/vnd.fpx"}, 352 | {"frame", "application/vnd.framemaker"}, 353 | {"fsc", "application/vnd.fsc.weblaunch"}, 354 | {"fst", "image/vnd.fst"}, 355 | {"ftc", "application/vnd.fluxtime.clip"}, 356 | {"fti", "application/vnd.anser-web-funds-transfer-initiation"}, 357 | {"fvt", "video/vnd.fvt"}, 358 | {"fxp", "application/vnd.adobe.fxp"}, 359 | {"fxpl", "application/vnd.adobe.fxp"}, 360 | {"fzs", "application/vnd.fuzzysheet"}, 361 | {"g2w", "application/vnd.geoplan"}, 362 | {"g3", "image/g3fax"}, 363 | {"g3w", "application/vnd.geospace"}, 364 | {"gac", "application/vnd.groove-account"}, 365 | {"gam", "application/x-tads"}, 366 | {"gbr", "application/rpki-ghostbusters"}, 367 | {"gca", "application/x-gca-compressed"}, 368 | {"gdl", "model/vnd.gdl"}, 369 | {"gdoc", "application/vnd.google-apps.document"}, 370 | {"ged", "text/vnd.familysearch.gedcom"}, 371 | {"geo", "application/vnd.dynageo"}, 372 | {"geojson", "application/geo+json"}, 373 | {"gex", "application/vnd.geometry-explorer"}, 374 | {"ggb", "application/vnd.geogebra.file"}, 375 | {"ggt", "application/vnd.geogebra.tool"}, 376 | {"ghf", "application/vnd.groove-help"}, 377 | {"gif", "image/gif"}, 378 | {"gim", "application/vnd.groove-identity-message"}, 379 | {"glb", "model/gltf-binary"}, 380 | {"gltf", "model/gltf+json"}, 381 | {"gml", "application/gml+xml"}, 382 | {"gmx", "application/vnd.gmx"}, 383 | {"gnumeric", "application/x-gnumeric"}, 384 | {"gph", "application/vnd.flographit"}, 385 | {"gpx", "application/gpx+xml"}, 386 | {"gqf", "application/vnd.grafeq"}, 387 | {"gqs", "application/vnd.grafeq"}, 388 | {"gram", "application/srgs"}, 389 | {"gramps", "application/x-gramps-xml"}, 390 | {"gre", "application/vnd.geometry-explorer"}, 391 | {"grv", "application/vnd.groove-injector"}, 392 | {"grxml", "application/srgs+xml"}, 393 | {"gsf", "application/x-font-ghostscript"}, 394 | {"gsheet", "application/vnd.google-apps.spreadsheet"}, 395 | {"gslides", "application/vnd.google-apps.presentation"}, 396 | {"gtar", "application/x-gtar"}, 397 | {"gtm", "application/vnd.groove-tool-message"}, 398 | {"gtw", "model/vnd.gtw"}, 399 | {"gv", "text/vnd.graphviz"}, 400 | {"gxf", "application/gxf"}, 401 | {"gxt", "application/vnd.geonext"}, 402 | {"gz", "application/gzip"}, 403 | {"h", "text/x-c"}, 404 | {"h261", "video/h261"}, 405 | {"h263", "video/h263"}, 406 | {"h264", "video/h264"}, 407 | {"hal", "application/vnd.hal+xml"}, 408 | {"hbci", "application/vnd.hbci"}, 409 | {"hbs", "text/x-handlebars-template"}, 410 | {"hdd", "application/x-virtualbox-hdd"}, 411 | {"hdf", "application/x-hdf"}, 412 | {"heic", "image/heic"}, 413 | {"heics", "image/heic-sequence"}, 414 | {"heif", "image/heif"}, 415 | {"heifs", "image/heif-sequence"}, 416 | {"hej2", "image/hej2k"}, 417 | {"held", "application/atsc-held+xml"}, 418 | {"hh", "text/x-c"}, 419 | {"hjson", "application/hjson"}, 420 | {"hlp", "application/winhlp"}, 421 | {"hpgl", "application/vnd.hp-hpgl"}, 422 | {"hpid", "application/vnd.hp-hpid"}, 423 | {"hps", "application/vnd.hp-hps"}, 424 | {"hqx", "application/mac-binhex40"}, 425 | {"hsj2", "image/hsj2"}, 426 | {"htc", "text/x-component"}, 427 | {"htke", "application/vnd.kenameaapp"}, 428 | {"htm", "text/html"}, 429 | {"html", "text/html"}, 430 | {"hvd", "application/vnd.yamaha.hv-dic"}, 431 | {"hvp", "application/vnd.yamaha.hv-voice"}, 432 | {"hvs", "application/vnd.yamaha.hv-script"}, 433 | {"i2g", "application/vnd.intergeo"}, 434 | {"icc", "application/vnd.iccprofile"}, 435 | {"ice", "x-conference/x-cooltalk"}, 436 | {"icm", "application/vnd.iccprofile"}, 437 | {"ico", "image/x-icon"}, 438 | {"ics", "text/calendar"}, 439 | {"ief", "image/ief"}, 440 | {"ifb", "text/calendar"}, 441 | {"ifm", "application/vnd.shana.informed.formdata"}, 442 | {"iges", "model/iges"}, 443 | {"igl", "application/vnd.igloader"}, 444 | {"igm", "application/vnd.insors.igm"}, 445 | {"igs", "model/iges"}, 446 | {"igx", "application/vnd.micrografx.igx"}, 447 | {"iif", "application/vnd.shana.informed.interchange"}, 448 | {"img", "application/octet-stream"}, 449 | {"imp", "application/vnd.accpac.simply.imp"}, 450 | {"ims", "application/vnd.ms-ims"}, 451 | {"in", "text/plain"}, 452 | {"ini", "text/plain"}, 453 | {"ink", "application/inkml+xml"}, 454 | {"inkml", "application/inkml+xml"}, 455 | {"install", "application/x-install-instructions"}, 456 | {"iota", "application/vnd.astraea-software.iota"}, 457 | {"ipfix", "application/ipfix"}, 458 | {"ipk", "application/vnd.shana.informed.package"}, 459 | {"irm", "application/vnd.ibm.rights-management"}, 460 | {"irp", "application/vnd.irepository.package+xml"}, 461 | {"iso", "application/x-iso9660-image"}, 462 | {"itp", "application/vnd.shana.informed.formtemplate"}, 463 | {"its", "application/its+xml"}, 464 | {"ivp", "application/vnd.immervision-ivp"}, 465 | {"ivu", "application/vnd.immervision-ivu"}, 466 | {"jad", "text/vnd.sun.j2me.app-descriptor"}, 467 | {"jade", "text/jade"}, 468 | {"jam", "application/vnd.jam"}, 469 | {"jar", "application/java-archive"}, 470 | {"jardiff", "application/x-java-archive-diff"}, 471 | {"java", "text/x-java-source"}, 472 | {"jhc", "image/jphc"}, 473 | {"jisp", "application/vnd.jisp"}, 474 | {"jls", "image/jls"}, 475 | {"jlt", "application/vnd.hp-jlyt"}, 476 | {"jng", "image/x-jng"}, 477 | {"jnlp", "application/x-java-jnlp-file"}, 478 | {"joda", "application/vnd.joost.joda-archive"}, 479 | {"jp2", "image/jp2"}, 480 | {"jpe", "image/jpeg"}, 481 | {"jpeg", "image/jpeg"}, 482 | {"jpf", "image/jpx"}, 483 | {"jpg", "image/jpeg"}, 484 | {"jpg2", "image/jp2"}, 485 | {"jpgm", "video/jpm"}, 486 | {"jpgv", "video/jpeg"}, 487 | {"jph", "image/jph"}, 488 | {"jpm", "video/jpm"}, 489 | {"jpx", "image/jpx"}, 490 | {"js", "application/javascript"}, 491 | {"json", "application/json"}, 492 | {"json5", "application/json5"}, 493 | {"jsonld", "application/ld+json"}, 494 | {"jsonml", "application/jsonml+json"}, 495 | {"jsx", "text/jsx"}, 496 | {"jxr", "image/jxr"}, 497 | {"jxra", "image/jxra"}, 498 | {"jxrs", "image/jxrs"}, 499 | {"jxs", "image/jxs"}, 500 | {"jxsc", "image/jxsc"}, 501 | {"jxsi", "image/jxsi"}, 502 | {"jxss", "image/jxss"}, 503 | {"kar", "audio/midi"}, 504 | {"karbon", "application/vnd.kde.karbon"}, 505 | {"kdbx", "application/x-keepass2"}, 506 | {"key", "application/x-iwork-keynote-sffkey"}, 507 | {"kfo", "application/vnd.kde.kformula"}, 508 | {"kia", "application/vnd.kidspiration"}, 509 | {"kml", "application/vnd.google-earth.kml+xml"}, 510 | {"kmz", "application/vnd.google-earth.kmz"}, 511 | {"kne", "application/vnd.kinar"}, 512 | {"knp", "application/vnd.kinar"}, 513 | {"kon", "application/vnd.kde.kontour"}, 514 | {"kpr", "application/vnd.kde.kpresenter"}, 515 | {"kpt", "application/vnd.kde.kpresenter"}, 516 | {"kpxx", "application/vnd.ds-keypoint"}, 517 | {"ksp", "application/vnd.kde.kspread"}, 518 | {"ktr", "application/vnd.kahootz"}, 519 | {"ktx", "image/ktx"}, 520 | {"ktx2", "image/ktx2"}, 521 | {"ktz", "application/vnd.kahootz"}, 522 | {"kwd", "application/vnd.kde.kword"}, 523 | {"kwt", "application/vnd.kde.kword"}, 524 | {"lasxml", "application/vnd.las.las+xml"}, 525 | {"latex", "application/x-latex"}, 526 | {"lbd", "application/vnd.llamagraphics.life-balance.desktop"}, 527 | {"lbe", "application/vnd.llamagraphics.life-balance.exchange+xml"}, 528 | {"les", "application/vnd.hhe.lesson-player"}, 529 | {"less", "text/less"}, 530 | {"lgr", "application/lgr+xml"}, 531 | {"lha", "application/x-lzh-compressed"}, 532 | {"link66", "application/vnd.route66.link66+xml"}, 533 | {"list", "text/plain"}, 534 | {"list3820", "application/vnd.ibm.modcap"}, 535 | {"listafp", "application/vnd.ibm.modcap"}, 536 | {"litcoffee", "text/coffeescript"}, 537 | {"lnk", "application/x-ms-shortcut"}, 538 | {"log", "text/plain"}, 539 | {"lostxml", "application/lost+xml"}, 540 | {"lrf", "application/octet-stream"}, 541 | {"lrm", "application/vnd.ms-lrm"}, 542 | {"ltf", "application/vnd.frogans.ltf"}, 543 | {"lua", "text/x-lua"}, 544 | {"luac", "application/x-lua-bytecode"}, 545 | {"lvp", "audio/vnd.lucent.voice"}, 546 | {"lwp", "application/vnd.lotus-wordpro"}, 547 | {"lzh", "application/x-lzh-compressed"}, 548 | {"m13", "application/x-msmediaview"}, 549 | {"m14", "application/x-msmediaview"}, 550 | {"m1v", "video/mpeg"}, 551 | {"m21", "application/mp21"}, 552 | {"m2a", "audio/mpeg"}, 553 | {"m2v", "video/mpeg"}, 554 | {"m3a", "audio/mpeg"}, 555 | {"m3u", "audio/x-mpegurl"}, 556 | {"m3u8", "application/vnd.apple.mpegurl"}, 557 | {"m4a", "audio/x-m4a"}, 558 | {"m4p", "application/mp4"}, 559 | {"m4s", "video/iso.segment"}, 560 | {"m4u", "video/vnd.mpegurl"}, 561 | {"m4v", "video/x-m4v"}, 562 | {"ma", "application/mathematica"}, 563 | {"mads", "application/mads+xml"}, 564 | {"maei", "application/mmt-aei+xml"}, 565 | {"mag", "application/vnd.ecowin.chart"}, 566 | {"maker", "application/vnd.framemaker"}, 567 | {"man", "text/troff"}, 568 | {"manifest", "text/cache-manifest"}, 569 | {"map", "application/json"}, 570 | {"mar", "application/octet-stream"}, 571 | {"markdown", "text/markdown"}, 572 | {"mathml", "application/mathml+xml"}, 573 | {"mb", "application/mathematica"}, 574 | {"mbk", "application/vnd.mobius.mbk"}, 575 | {"mbox", "application/mbox"}, 576 | {"mc1", "application/vnd.medcalcdata"}, 577 | {"mcd", "application/vnd.mcd"}, 578 | {"mcurl", "text/vnd.curl.mcurl"}, 579 | {"md", "text/markdown"}, 580 | {"mdb", "application/x-msaccess"}, 581 | {"mdi", "image/vnd.ms-modi"}, 582 | {"mdx", "text/mdx"}, 583 | {"me", "text/troff"}, 584 | {"mesh", "model/mesh"}, 585 | {"meta4", "application/metalink4+xml"}, 586 | {"metalink", "application/metalink+xml"}, 587 | {"mets", "application/mets+xml"}, 588 | {"mfm", "application/vnd.mfmp"}, 589 | {"mft", "application/rpki-manifest"}, 590 | {"mgp", "application/vnd.osgeo.mapguide.package"}, 591 | {"mgz", "application/vnd.proteus.magazine"}, 592 | {"mid", "audio/midi"}, 593 | {"midi", "audio/midi"}, 594 | {"mie", "application/x-mie"}, 595 | {"mif", "application/vnd.mif"}, 596 | {"mime", "message/rfc822"}, 597 | {"mj2", "video/mj2"}, 598 | {"mjp2", "video/mj2"}, 599 | {"mjs", "application/javascript"}, 600 | {"mk3d", "video/x-matroska"}, 601 | {"mka", "audio/x-matroska"}, 602 | {"mkd", "text/x-markdown"}, 603 | {"mks", "video/x-matroska"}, 604 | {"mkv", "video/x-matroska"}, 605 | {"mlp", "application/vnd.dolby.mlp"}, 606 | {"mmd", "application/vnd.chipnuts.karaoke-mmd"}, 607 | {"mmf", "application/vnd.smaf"}, 608 | {"mml", "text/mathml"}, 609 | {"mmr", "image/vnd.fujixerox.edmics-mmr"}, 610 | {"mng", "video/x-mng"}, 611 | {"mny", "application/x-msmoney"}, 612 | {"mobi", "application/x-mobipocket-ebook"}, 613 | {"mods", "application/mods+xml"}, 614 | {"mov", "video/quicktime"}, 615 | {"movie", "video/x-sgi-movie"}, 616 | {"mp2", "audio/mpeg"}, 617 | {"mp21", "application/mp21"}, 618 | {"mp2a", "audio/mpeg"}, 619 | {"mp3", "audio/mpeg"}, 620 | {"mp4", "video/mp4"}, 621 | {"mp4a", "audio/mp4"}, 622 | {"mp4s", "application/mp4"}, 623 | {"mp4v", "video/mp4"}, 624 | {"mpc", "application/vnd.mophun.certificate"}, 625 | {"mpd", "application/dash+xml"}, 626 | {"mpe", "video/mpeg"}, 627 | {"mpeg", "video/mpeg"}, 628 | {"mpg", "video/mpeg"}, 629 | {"mpg4", "video/mp4"}, 630 | {"mpga", "audio/mpeg"}, 631 | {"mpkg", "application/vnd.apple.installer+xml"}, 632 | {"mpm", "application/vnd.blueice.multipass"}, 633 | {"mpn", "application/vnd.mophun.application"}, 634 | {"mpp", "application/vnd.ms-project"}, 635 | {"mpt", "application/vnd.ms-project"}, 636 | {"mpy", "application/vnd.ibm.minipay"}, 637 | {"mqy", "application/vnd.mobius.mqy"}, 638 | {"mrc", "application/marc"}, 639 | {"mrcx", "application/marcxml+xml"}, 640 | {"ms", "text/troff"}, 641 | {"mscml", "application/mediaservercontrol+xml"}, 642 | {"mseed", "application/vnd.fdsn.mseed"}, 643 | {"mseq", "application/vnd.mseq"}, 644 | {"msf", "application/vnd.epson.msf"}, 645 | {"msg", "application/vnd.ms-outlook"}, 646 | {"msh", "model/mesh"}, 647 | {"msi", "application/x-msdownload"}, 648 | {"msl", "application/vnd.mobius.msl"}, 649 | {"msm", "application/octet-stream"}, 650 | {"msp", "application/octet-stream"}, 651 | {"msty", "application/vnd.muvee.style"}, 652 | {"mtl", "model/mtl"}, 653 | {"mts", "model/vnd.mts"}, 654 | {"mus", "application/vnd.musician"}, 655 | {"musd", "application/mmt-usd+xml"}, 656 | {"musicxml", "application/vnd.recordare.musicxml+xml"}, 657 | {"mvb", "application/x-msmediaview"}, 658 | {"mvt", "application/vnd.mapbox-vector-tile"}, 659 | {"mwf", "application/vnd.mfer"}, 660 | {"mxf", "application/mxf"}, 661 | {"mxl", "application/vnd.recordare.musicxml"}, 662 | {"mxmf", "audio/mobile-xmf"}, 663 | {"mxml", "application/xv+xml"}, 664 | {"mxs", "application/vnd.triscape.mxs"}, 665 | {"mxu", "video/vnd.mpegurl"}, 666 | {"n-gage", "application/vnd.nokia.n-gage.symbian.install"}, 667 | {"n3", "text/n3"}, 668 | {"nb", "application/mathematica"}, 669 | {"nbp", "application/vnd.wolfram.player"}, 670 | {"nc", "application/x-netcdf"}, 671 | {"ncx", "application/x-dtbncx+xml"}, 672 | {"nfo", "text/x-nfo"}, 673 | {"ngdat", "application/vnd.nokia.n-gage.data"}, 674 | {"nitf", "application/vnd.nitf"}, 675 | {"nlu", "application/vnd.neurolanguage.nlu"}, 676 | {"nml", "application/vnd.enliven"}, 677 | {"nnd", "application/vnd.noblenet-directory"}, 678 | {"nns", "application/vnd.noblenet-sealer"}, 679 | {"nnw", "application/vnd.noblenet-web"}, 680 | {"npx", "image/vnd.net-fpx"}, 681 | {"nq", "application/n-quads"}, 682 | {"nsc", "application/x-conference"}, 683 | {"nsf", "application/vnd.lotus-notes"}, 684 | {"nt", "application/n-triples"}, 685 | {"ntf", "application/vnd.nitf"}, 686 | {"numbers", "application/x-iwork-numbers-sffnumbers"}, 687 | {"nzb", "application/x-nzb"}, 688 | {"oa2", "application/vnd.fujitsu.oasys2"}, 689 | {"oa3", "application/vnd.fujitsu.oasys3"}, 690 | {"oas", "application/vnd.fujitsu.oasys"}, 691 | {"obd", "application/x-msbinder"}, 692 | {"obgx", "application/vnd.openblox.game+xml"}, 693 | {"obj", "model/obj"}, 694 | {"oda", "application/oda"}, 695 | {"odb", "application/vnd.oasis.opendocument.database"}, 696 | {"odc", "application/vnd.oasis.opendocument.chart"}, 697 | {"odf", "application/vnd.oasis.opendocument.formula"}, 698 | {"odft", "application/vnd.oasis.opendocument.formula-template"}, 699 | {"odg", "application/vnd.oasis.opendocument.graphics"}, 700 | {"odi", "application/vnd.oasis.opendocument.image"}, 701 | {"odm", "application/vnd.oasis.opendocument.text-master"}, 702 | {"odp", "application/vnd.oasis.opendocument.presentation"}, 703 | {"ods", "application/vnd.oasis.opendocument.spreadsheet"}, 704 | {"odt", "application/vnd.oasis.opendocument.text"}, 705 | {"oga", "audio/ogg"}, 706 | {"ogex", "model/vnd.opengex"}, 707 | {"ogg", "audio/ogg"}, 708 | {"ogv", "video/ogg"}, 709 | {"ogx", "application/ogg"}, 710 | {"omdoc", "application/omdoc+xml"}, 711 | {"onepkg", "application/onenote"}, 712 | {"onetmp", "application/onenote"}, 713 | {"onetoc", "application/onenote"}, 714 | {"onetoc2", "application/onenote"}, 715 | {"opf", "application/oebps-package+xml"}, 716 | {"opml", "text/x-opml"}, 717 | {"oprc", "application/vnd.palm"}, 718 | {"opus", "audio/ogg"}, 719 | {"org", "text/x-org"}, 720 | {"osf", "application/vnd.yamaha.openscoreformat"}, 721 | {"osfpvg", "application/vnd.yamaha.openscoreformat.osfpvg+xml"}, 722 | {"osm", "application/vnd.openstreetmap.data+xml"}, 723 | {"otc", "application/vnd.oasis.opendocument.chart-template"}, 724 | {"otf", "font/otf"}, 725 | {"otg", "application/vnd.oasis.opendocument.graphics-template"}, 726 | {"oth", "application/vnd.oasis.opendocument.text-web"}, 727 | {"oti", "application/vnd.oasis.opendocument.image-template"}, 728 | {"otp", "application/vnd.oasis.opendocument.presentation-template"}, 729 | {"ots", "application/vnd.oasis.opendocument.spreadsheet-template"}, 730 | {"ott", "application/vnd.oasis.opendocument.text-template"}, 731 | {"ova", "application/x-virtualbox-ova"}, 732 | {"ovf", "application/x-virtualbox-ovf"}, 733 | {"owl", "application/rdf+xml"}, 734 | {"oxps", "application/oxps"}, 735 | {"oxt", "application/vnd.openofficeorg.extension"}, 736 | {"p", "text/x-pascal"}, 737 | {"p10", "application/pkcs10"}, 738 | {"p12", "application/x-pkcs12"}, 739 | {"p7b", "application/x-pkcs7-certificates"}, 740 | {"p7c", "application/pkcs7-mime"}, 741 | {"p7m", "application/pkcs7-mime"}, 742 | {"p7r", "application/x-pkcs7-certreqresp"}, 743 | {"p7s", "application/pkcs7-signature"}, 744 | {"p8", "application/pkcs8"}, 745 | {"pac", "application/x-ns-proxy-autoconfig"}, 746 | {"pages", "application/x-iwork-pages-sffpages"}, 747 | {"pas", "text/x-pascal"}, 748 | {"paw", "application/vnd.pawaafile"}, 749 | {"pbd", "application/vnd.powerbuilder6"}, 750 | {"pbm", "image/x-portable-bitmap"}, 751 | {"pcap", "application/vnd.tcpdump.pcap"}, 752 | {"pcf", "application/x-font-pcf"}, 753 | {"pcl", "application/vnd.hp-pcl"}, 754 | {"pclxl", "application/vnd.hp-pclxl"}, 755 | {"pct", "image/x-pict"}, 756 | {"pcurl", "application/vnd.curl.pcurl"}, 757 | {"pcx", "image/x-pcx"}, 758 | {"pdb", "application/x-pilot"}, 759 | {"pde", "text/x-processing"}, 760 | {"pdf", "application/pdf"}, 761 | {"pem", "application/x-x509-ca-cert"}, 762 | {"pfa", "application/x-font-type1"}, 763 | {"pfb", "application/x-font-type1"}, 764 | {"pfm", "application/x-font-type1"}, 765 | {"pfr", "application/font-tdpfr"}, 766 | {"pfx", "application/x-pkcs12"}, 767 | {"pgm", "image/x-portable-graymap"}, 768 | {"pgn", "application/x-chess-pgn"}, 769 | {"pgp", "application/pgp-encrypted"}, 770 | {"php", "application/x-httpd-php"}, 771 | {"pic", "image/x-pict"}, 772 | {"pkg", "application/octet-stream"}, 773 | {"pki", "application/pkixcmp"}, 774 | {"pkipath", "application/pkix-pkipath"}, 775 | {"pkpass", "application/vnd.apple.pkpass"}, 776 | {"pl", "application/x-perl"}, 777 | {"plb", "application/vnd.3gpp.pic-bw-large"}, 778 | {"plc", "application/vnd.mobius.plc"}, 779 | {"plf", "application/vnd.pocketlearn"}, 780 | {"pls", "application/pls+xml"}, 781 | {"pm", "application/x-perl"}, 782 | {"pml", "application/vnd.ctc-posml"}, 783 | {"png", "image/png"}, 784 | {"pnm", "image/x-portable-anymap"}, 785 | {"portpkg", "application/vnd.macports.portpkg"}, 786 | {"pot", "application/vnd.ms-powerpoint"}, 787 | {"potm", "application/vnd.ms-powerpoint.template.macroenabled.12"}, 788 | {"potx", 789 | "application/vnd.openxmlformats-officedocument.presentationml.template"}, 790 | {"ppam", "application/vnd.ms-powerpoint.addin.macroenabled.12"}, 791 | {"ppd", "application/vnd.cups-ppd"}, 792 | {"ppm", "image/x-portable-pixmap"}, 793 | {"pps", "application/vnd.ms-powerpoint"}, 794 | {"ppsm", "application/vnd.ms-powerpoint.slideshow.macroenabled.12"}, 795 | {"ppsx", "application/" 796 | "vnd.openxmlformats-officedocument.presentationml.slideshow"}, 797 | {"ppt", "application/vnd.ms-powerpoint"}, 798 | {"pptm", "application/vnd.ms-powerpoint.presentation.macroenabled.12"}, 799 | {"pptx", "application/" 800 | "vnd.openxmlformats-officedocument.presentationml.presentation"}, 801 | {"pqa", "application/vnd.palm"}, 802 | {"prc", "application/x-pilot"}, 803 | {"pre", "application/vnd.lotus-freelance"}, 804 | {"prf", "application/pics-rules"}, 805 | {"provx", "application/provenance+xml"}, 806 | {"ps", "application/postscript"}, 807 | {"psb", "application/vnd.3gpp.pic-bw-small"}, 808 | {"psd", "image/vnd.adobe.photoshop"}, 809 | {"psf", "application/x-font-linux-psf"}, 810 | {"pskcxml", "application/pskc+xml"}, 811 | {"pti", "image/prs.pti"}, 812 | {"ptid", "application/vnd.pvi.ptid1"}, 813 | {"pub", "application/x-mspublisher"}, 814 | {"pvb", "application/vnd.3gpp.pic-bw-var"}, 815 | {"pwn", "application/vnd.3m.post-it-notes"}, 816 | {"pya", "audio/vnd.ms-playready.media.pya"}, 817 | {"pyv", "video/vnd.ms-playready.media.pyv"}, 818 | {"qam", "application/vnd.epson.quickanime"}, 819 | {"qbo", "application/vnd.intu.qbo"}, 820 | {"qfx", "application/vnd.intu.qfx"}, 821 | {"qps", "application/vnd.publishare-delta-tree"}, 822 | {"qt", "video/quicktime"}, 823 | {"qwd", "application/vnd.quark.quarkxpress"}, 824 | {"qwt", "application/vnd.quark.quarkxpress"}, 825 | {"qxb", "application/vnd.quark.quarkxpress"}, 826 | {"qxd", "application/vnd.quark.quarkxpress"}, 827 | {"qxl", "application/vnd.quark.quarkxpress"}, 828 | {"qxt", "application/vnd.quark.quarkxpress"}, 829 | {"ra", "audio/x-realaudio"}, 830 | {"ram", "audio/x-pn-realaudio"}, 831 | {"raml", "application/raml+yaml"}, 832 | {"rapd", "application/route-apd+xml"}, 833 | {"rar", "application/x-rar-compressed"}, 834 | {"ras", "image/x-cmu-raster"}, 835 | {"rcprofile", "application/vnd.ipunplugged.rcprofile"}, 836 | {"rdf", "application/rdf+xml"}, 837 | {"rdz", "application/vnd.data-vision.rdz"}, 838 | {"relo", "application/p2p-overlay+xml"}, 839 | {"rep", "application/vnd.businessobjects"}, 840 | {"res", "application/x-dtbresource+xml"}, 841 | {"rgb", "image/x-rgb"}, 842 | {"rif", "application/reginfo+xml"}, 843 | {"rip", "audio/vnd.rip"}, 844 | {"ris", "application/x-research-info-systems"}, 845 | {"rl", "application/resource-lists+xml"}, 846 | {"rlc", "image/vnd.fujixerox.edmics-rlc"}, 847 | {"rld", "application/resource-lists-diff+xml"}, 848 | {"rm", "application/vnd.rn-realmedia"}, 849 | {"rmi", "audio/midi"}, 850 | {"rmp", "audio/x-pn-realaudio-plugin"}, 851 | {"rms", "application/vnd.jcp.javame.midlet-rms"}, 852 | {"rmvb", "application/vnd.rn-realmedia-vbr"}, 853 | {"rnc", "application/relax-ng-compact-syntax"}, 854 | {"rng", "application/xml"}, 855 | {"roa", "application/rpki-roa"}, 856 | {"roff", "text/troff"}, 857 | {"rp9", "application/vnd.cloanto.rp9"}, 858 | {"rpm", "application/x-redhat-package-manager"}, 859 | {"rpss", "application/vnd.nokia.radio-presets"}, 860 | {"rpst", "application/vnd.nokia.radio-preset"}, 861 | {"rq", "application/sparql-query"}, 862 | {"rs", "application/rls-services+xml"}, 863 | {"rsat", "application/atsc-rsat+xml"}, 864 | {"rsd", "application/rsd+xml"}, 865 | {"rsheet", "application/urc-ressheet+xml"}, 866 | {"rss", "application/rss+xml"}, 867 | {"rtf", "text/rtf"}, 868 | {"rtx", "text/richtext"}, 869 | {"run", "application/x-makeself"}, 870 | {"rusd", "application/route-usd+xml"}, 871 | {"s", "text/x-asm"}, 872 | {"s3m", "audio/s3m"}, 873 | {"saf", "application/vnd.yamaha.smaf-audio"}, 874 | {"sass", "text/x-sass"}, 875 | {"sbml", "application/sbml+xml"}, 876 | {"sc", "application/vnd.ibm.secure-container"}, 877 | {"scd", "application/x-msschedule"}, 878 | {"scm", "application/vnd.lotus-screencam"}, 879 | {"scq", "application/scvp-cv-request"}, 880 | {"scs", "application/scvp-cv-response"}, 881 | {"scss", "text/x-scss"}, 882 | {"scurl", "text/vnd.curl.scurl"}, 883 | {"sda", "application/vnd.stardivision.draw"}, 884 | {"sdc", "application/vnd.stardivision.calc"}, 885 | {"sdd", "application/vnd.stardivision.impress"}, 886 | {"sdkd", "application/vnd.solent.sdkm+xml"}, 887 | {"sdkm", "application/vnd.solent.sdkm+xml"}, 888 | {"sdp", "application/sdp"}, 889 | {"sdw", "application/vnd.stardivision.writer"}, 890 | {"sea", "application/x-sea"}, 891 | {"see", "application/vnd.seemail"}, 892 | {"seed", "application/vnd.fdsn.seed"}, 893 | {"sema", "application/vnd.sema"}, 894 | {"semd", "application/vnd.semd"}, 895 | {"semf", "application/vnd.semf"}, 896 | {"senmlx", "application/senml+xml"}, 897 | {"sensmlx", "application/sensml+xml"}, 898 | {"ser", "application/java-serialized-object"}, 899 | {"setpay", "application/set-payment-initiation"}, 900 | {"setreg", "application/set-registration-initiation"}, 901 | {"sfd-hdstx", "application/vnd.hydrostatix.sof-data"}, 902 | {"sfs", "application/vnd.spotfire.sfs"}, 903 | {"sfv", "text/x-sfv"}, 904 | {"sgi", "image/sgi"}, 905 | {"sgl", "application/vnd.stardivision.writer-global"}, 906 | {"sgm", "text/sgml"}, 907 | {"sgml", "text/sgml"}, 908 | {"sh", "application/x-sh"}, 909 | {"shar", "application/x-shar"}, 910 | {"shex", "text/shex"}, 911 | {"shf", "application/shf+xml"}, 912 | {"shtml", "text/html"}, 913 | {"sid", "image/x-mrsid-image"}, 914 | {"sieve", "application/sieve"}, 915 | {"sig", "application/pgp-signature"}, 916 | {"sil", "audio/silk"}, 917 | {"silo", "model/mesh"}, 918 | {"sis", "application/vnd.symbian.install"}, 919 | {"sisx", "application/vnd.symbian.install"}, 920 | {"sit", "application/x-stuffit"}, 921 | {"sitx", "application/x-stuffitx"}, 922 | {"siv", "application/sieve"}, 923 | {"skd", "application/vnd.koan"}, 924 | {"skm", "application/vnd.koan"}, 925 | {"skp", "application/vnd.koan"}, 926 | {"skt", "application/vnd.koan"}, 927 | {"sldm", "application/vnd.ms-powerpoint.slide.macroenabled.12"}, 928 | {"sldx", 929 | "application/vnd.openxmlformats-officedocument.presentationml.slide"}, 930 | {"slim", "text/slim"}, 931 | {"slm", "text/slim"}, 932 | {"sls", "application/route-s-tsid+xml"}, 933 | {"slt", "application/vnd.epson.salt"}, 934 | {"sm", "application/vnd.stepmania.stepchart"}, 935 | {"smf", "application/vnd.stardivision.math"}, 936 | {"smi", "application/smil+xml"}, 937 | {"smil", "application/smil+xml"}, 938 | {"smv", "video/x-smv"}, 939 | {"smzip", "application/vnd.stepmania.package"}, 940 | {"snd", "audio/basic"}, 941 | {"snf", "application/x-font-snf"}, 942 | {"so", "application/octet-stream"}, 943 | {"spc", "application/x-pkcs7-certificates"}, 944 | {"spdx", "text/spdx"}, 945 | {"spf", "application/vnd.yamaha.smaf-phrase"}, 946 | {"spl", "application/x-futuresplash"}, 947 | {"spot", "text/vnd.in3d.spot"}, 948 | {"spp", "application/scvp-vp-response"}, 949 | {"spq", "application/scvp-vp-request"}, 950 | {"spx", "audio/ogg"}, 951 | {"sql", "application/x-sql"}, 952 | {"src", "application/x-wais-source"}, 953 | {"srt", "application/x-subrip"}, 954 | {"sru", "application/sru+xml"}, 955 | {"srx", "application/sparql-results+xml"}, 956 | {"ssdl", "application/ssdl+xml"}, 957 | {"sse", "application/vnd.kodak-descriptor"}, 958 | {"ssf", "application/vnd.epson.ssf"}, 959 | {"ssml", "application/ssml+xml"}, 960 | {"st", "application/vnd.sailingtracker.track"}, 961 | {"stc", "application/vnd.sun.xml.calc.template"}, 962 | {"std", "application/vnd.sun.xml.draw.template"}, 963 | {"stf", "application/vnd.wt.stf"}, 964 | {"sti", "application/vnd.sun.xml.impress.template"}, 965 | {"stk", "application/hyperstudio"}, 966 | {"stl", "model/stl"}, 967 | {"stpx", "model/step+xml"}, 968 | {"stpxz", "model/step-xml+zip"}, 969 | {"stpz", "model/step+zip"}, 970 | {"str", "application/vnd.pg.format"}, 971 | {"stw", "application/vnd.sun.xml.writer.template"}, 972 | {"styl", "text/stylus"}, 973 | {"stylus", "text/stylus"}, 974 | {"sub", "text/vnd.dvb.subtitle"}, 975 | {"sus", "application/vnd.sus-calendar"}, 976 | {"susp", "application/vnd.sus-calendar"}, 977 | {"sv4cpio", "application/x-sv4cpio"}, 978 | {"sv4crc", "application/x-sv4crc"}, 979 | {"svc", "application/vnd.dvb.service"}, 980 | {"svd", "application/vnd.svd"}, 981 | {"svg", "image/svg+xml"}, 982 | {"svgz", "image/svg+xml"}, 983 | {"swa", "application/x-director"}, 984 | {"swf", "application/x-shockwave-flash"}, 985 | {"swi", "application/vnd.aristanetworks.swi"}, 986 | {"swidtag", "application/swid+xml"}, 987 | {"sxc", "application/vnd.sun.xml.calc"}, 988 | {"sxd", "application/vnd.sun.xml.draw"}, 989 | {"sxg", "application/vnd.sun.xml.writer.global"}, 990 | {"sxi", "application/vnd.sun.xml.impress"}, 991 | {"sxm", "application/vnd.sun.xml.math"}, 992 | {"sxw", "application/vnd.sun.xml.writer"}, 993 | {"t", "text/troff"}, 994 | {"t3", "application/x-t3vm-image"}, 995 | {"t38", "image/t38"}, 996 | {"taglet", "application/vnd.mynfc"}, 997 | {"tao", "application/vnd.tao.intent-module-archive"}, 998 | {"tap", "image/vnd.tencent.tap"}, 999 | {"tar", "application/x-tar"}, 1000 | {"tcap", "application/vnd.3gpp2.tcap"}, 1001 | {"tcl", "application/x-tcl"}, 1002 | {"td", "application/urc-targetdesc+xml"}, 1003 | {"teacher", "application/vnd.smart.teacher"}, 1004 | {"tei", "application/tei+xml"}, 1005 | {"teicorpus", "application/tei+xml"}, 1006 | {"tex", "application/x-tex"}, 1007 | {"texi", "application/x-texinfo"}, 1008 | {"texinfo", "application/x-texinfo"}, 1009 | {"text", "text/plain"}, 1010 | {"tfi", "application/thraud+xml"}, 1011 | {"tfm", "application/x-tex-tfm"}, 1012 | {"tfx", "image/tiff-fx"}, 1013 | {"tga", "image/x-tga"}, 1014 | {"thmx", "application/vnd.ms-officetheme"}, 1015 | {"tif", "image/tiff"}, 1016 | {"tiff", "image/tiff"}, 1017 | {"tk", "application/x-tcl"}, 1018 | {"tmo", "application/vnd.tmobile-livetv"}, 1019 | {"toml", "application/toml"}, 1020 | {"torrent", "application/x-bittorrent"}, 1021 | {"tpl", "application/vnd.groove-tool-template"}, 1022 | {"tpt", "application/vnd.trid.tpt"}, 1023 | {"tr", "text/troff"}, 1024 | {"tra", "application/vnd.trueapp"}, 1025 | {"trig", "application/trig"}, 1026 | {"trm", "application/x-msterminal"}, 1027 | {"ts", "video/mp2t"}, 1028 | {"tsd", "application/timestamped-data"}, 1029 | {"tsv", "text/tab-separated-values"}, 1030 | {"ttc", "font/collection"}, 1031 | {"ttf", "font/ttf"}, 1032 | {"ttl", "text/turtle"}, 1033 | {"ttml", "application/ttml+xml"}, 1034 | {"twd", "application/vnd.simtech-mindmapper"}, 1035 | {"twds", "application/vnd.simtech-mindmapper"}, 1036 | {"txd", "application/vnd.genomatix.tuxedo"}, 1037 | {"txf", "application/vnd.mobius.txf"}, 1038 | {"txt", "text/plain"}, 1039 | {"u32", "application/x-authorware-bin"}, 1040 | {"u8dsn", "message/global-delivery-status"}, 1041 | {"u8hdr", "message/global-headers"}, 1042 | {"u8mdn", "message/global-disposition-notification"}, 1043 | {"u8msg", "message/global"}, 1044 | {"ubj", "application/ubjson"}, 1045 | {"udeb", "application/x-debian-package"}, 1046 | {"ufd", "application/vnd.ufdl"}, 1047 | {"ufdl", "application/vnd.ufdl"}, 1048 | {"ulx", "application/x-glulx"}, 1049 | {"umj", "application/vnd.umajin"}, 1050 | {"unityweb", "application/vnd.unity"}, 1051 | {"uoml", "application/vnd.uoml+xml"}, 1052 | {"uri", "text/uri-list"}, 1053 | {"uris", "text/uri-list"}, 1054 | {"urls", "text/uri-list"}, 1055 | {"usdz", "model/vnd.usdz+zip"}, 1056 | {"ustar", "application/x-ustar"}, 1057 | {"utz", "application/vnd.uiq.theme"}, 1058 | {"uu", "text/x-uuencode"}, 1059 | {"uva", "audio/vnd.dece.audio"}, 1060 | {"uvd", "application/vnd.dece.data"}, 1061 | {"uvf", "application/vnd.dece.data"}, 1062 | {"uvg", "image/vnd.dece.graphic"}, 1063 | {"uvh", "video/vnd.dece.hd"}, 1064 | {"uvi", "image/vnd.dece.graphic"}, 1065 | {"uvm", "video/vnd.dece.mobile"}, 1066 | {"uvp", "video/vnd.dece.pd"}, 1067 | {"uvs", "video/vnd.dece.sd"}, 1068 | {"uvt", "application/vnd.dece.ttml+xml"}, 1069 | {"uvu", "video/vnd.uvvu.mp4"}, 1070 | {"uvv", "video/vnd.dece.video"}, 1071 | {"uvva", "audio/vnd.dece.audio"}, 1072 | {"uvvd", "application/vnd.dece.data"}, 1073 | {"uvvf", "application/vnd.dece.data"}, 1074 | {"uvvg", "image/vnd.dece.graphic"}, 1075 | {"uvvh", "video/vnd.dece.hd"}, 1076 | {"uvvi", "image/vnd.dece.graphic"}, 1077 | {"uvvm", "video/vnd.dece.mobile"}, 1078 | {"uvvp", "video/vnd.dece.pd"}, 1079 | {"uvvs", "video/vnd.dece.sd"}, 1080 | {"uvvt", "application/vnd.dece.ttml+xml"}, 1081 | {"uvvu", "video/vnd.uvvu.mp4"}, 1082 | {"uvvv", "video/vnd.dece.video"}, 1083 | {"uvvx", "application/vnd.dece.unspecified"}, 1084 | {"uvvz", "application/vnd.dece.zip"}, 1085 | {"uvx", "application/vnd.dece.unspecified"}, 1086 | {"uvz", "application/vnd.dece.zip"}, 1087 | {"vbox", "application/x-virtualbox-vbox"}, 1088 | {"vbox-extpack", "application/x-virtualbox-vbox-extpack"}, 1089 | {"vcard", "text/vcard"}, 1090 | {"vcd", "application/x-cdlink"}, 1091 | {"vcf", "text/x-vcard"}, 1092 | {"vcg", "application/vnd.groove-vcard"}, 1093 | {"vcs", "text/x-vcalendar"}, 1094 | {"vcx", "application/vnd.vcx"}, 1095 | {"vdi", "application/x-virtualbox-vdi"}, 1096 | {"vds", "model/vnd.sap.vds"}, 1097 | {"vhd", "application/x-virtualbox-vhd"}, 1098 | {"vis", "application/vnd.visionary"}, 1099 | {"viv", "video/vnd.vivo"}, 1100 | {"vmdk", "application/x-virtualbox-vmdk"}, 1101 | {"vob", "video/x-ms-vob"}, 1102 | {"vor", "application/vnd.stardivision.writer"}, 1103 | {"vox", "application/x-authorware-bin"}, 1104 | {"vrml", "model/vrml"}, 1105 | {"vsd", "application/vnd.visio"}, 1106 | {"vsf", "application/vnd.vsf"}, 1107 | {"vss", "application/vnd.visio"}, 1108 | {"vst", "application/vnd.visio"}, 1109 | {"vsw", "application/vnd.visio"}, 1110 | {"vtf", "image/vnd.valve.source.texture"}, 1111 | {"vtt", "text/vtt"}, 1112 | {"vtu", "model/vnd.vtu"}, 1113 | {"vxml", "application/voicexml+xml"}, 1114 | {"w3d", "application/x-director"}, 1115 | {"wad", "application/x-doom"}, 1116 | {"wadl", "application/vnd.sun.wadl+xml"}, 1117 | {"war", "application/java-archive"}, 1118 | {"wasm", "application/wasm"}, 1119 | {"wav", "audio/x-wav"}, 1120 | {"wax", "audio/x-ms-wax"}, 1121 | {"wbmp", "image/vnd.wap.wbmp"}, 1122 | {"wbs", "application/vnd.criticaltools.wbs+xml"}, 1123 | {"wbxml", "application/vnd.wap.wbxml"}, 1124 | {"wcm", "application/vnd.ms-works"}, 1125 | {"wdb", "application/vnd.ms-works"}, 1126 | {"wdp", "image/vnd.ms-photo"}, 1127 | {"weba", "audio/webm"}, 1128 | {"webapp", "application/x-web-app-manifest+json"}, 1129 | {"webm", "video/webm"}, 1130 | {"webmanifest", "application/manifest+json"}, 1131 | {"webp", "image/webp"}, 1132 | {"wg", "application/vnd.pmi.widget"}, 1133 | {"wgt", "application/widget"}, 1134 | {"wks", "application/vnd.ms-works"}, 1135 | {"wm", "video/x-ms-wm"}, 1136 | {"wma", "audio/x-ms-wma"}, 1137 | {"wmd", "application/x-ms-wmd"}, 1138 | {"wmf", "image/wmf"}, 1139 | {"wml", "text/vnd.wap.wml"}, 1140 | {"wmlc", "application/vnd.wap.wmlc"}, 1141 | {"wmls", "text/vnd.wap.wmlscript"}, 1142 | {"wmlsc", "application/vnd.wap.wmlscriptc"}, 1143 | {"wmv", "video/x-ms-wmv"}, 1144 | {"wmx", "video/x-ms-wmx"}, 1145 | {"wmz", "application/x-msmetafile"}, 1146 | {"woff", "font/woff"}, 1147 | {"woff2", "font/woff2"}, 1148 | {"wpd", "application/vnd.wordperfect"}, 1149 | {"wpl", "application/vnd.ms-wpl"}, 1150 | {"wps", "application/vnd.ms-works"}, 1151 | {"wqd", "application/vnd.wqd"}, 1152 | {"wri", "application/x-mswrite"}, 1153 | {"wrl", "model/vrml"}, 1154 | {"wsc", "message/vnd.wfa.wsc"}, 1155 | {"wsdl", "application/wsdl+xml"}, 1156 | {"wspolicy", "application/wspolicy+xml"}, 1157 | {"wtb", "application/vnd.webturbo"}, 1158 | {"wvx", "video/x-ms-wvx"}, 1159 | {"x32", "application/x-authorware-bin"}, 1160 | {"x3d", "model/x3d+xml"}, 1161 | {"x3db", "model/x3d+fastinfoset"}, 1162 | {"x3dbz", "model/x3d+binary"}, 1163 | {"x3dv", "model/x3d-vrml"}, 1164 | {"x3dvz", "model/x3d+vrml"}, 1165 | {"x3dz", "model/x3d+xml"}, 1166 | {"x_b", "model/vnd.parasolid.transmit.binary"}, 1167 | {"x_t", "model/vnd.parasolid.transmit.text"}, 1168 | {"xaml", "application/xaml+xml"}, 1169 | {"xap", "application/x-silverlight-app"}, 1170 | {"xar", "application/vnd.xara"}, 1171 | {"xav", "application/xcap-att+xml"}, 1172 | {"xbap", "application/x-ms-xbap"}, 1173 | {"xbd", "application/vnd.fujixerox.docuworks.binder"}, 1174 | {"xbm", "image/x-xbitmap"}, 1175 | {"xca", "application/xcap-caps+xml"}, 1176 | {"xcs", "application/calendar+xml"}, 1177 | {"xdf", "application/xcap-diff+xml"}, 1178 | {"xdm", "application/vnd.syncml.dm+xml"}, 1179 | {"xdp", "application/vnd.adobe.xdp+xml"}, 1180 | {"xdssc", "application/dssc+xml"}, 1181 | {"xdw", "application/vnd.fujixerox.docuworks"}, 1182 | {"xel", "application/xcap-el+xml"}, 1183 | {"xenc", "application/xenc+xml"}, 1184 | {"xer", "application/patch-ops-error+xml"}, 1185 | {"xfdf", "application/vnd.adobe.xfdf"}, 1186 | {"xfdl", "application/vnd.xfdl"}, 1187 | {"xht", "application/xhtml+xml"}, 1188 | {"xhtml", "application/xhtml+xml"}, 1189 | {"xhvml", "application/xv+xml"}, 1190 | {"xif", "image/vnd.xiff"}, 1191 | {"xla", "application/vnd.ms-excel"}, 1192 | {"xlam", "application/vnd.ms-excel.addin.macroenabled.12"}, 1193 | {"xlc", "application/vnd.ms-excel"}, 1194 | {"xlf", "application/xliff+xml"}, 1195 | {"xlm", "application/vnd.ms-excel"}, 1196 | {"xls", "application/vnd.ms-excel"}, 1197 | {"xlsb", "application/vnd.ms-excel.sheet.binary.macroenabled.12"}, 1198 | {"xlsm", "application/vnd.ms-excel.sheet.macroenabled.12"}, 1199 | {"xlsx", 1200 | "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"}, 1201 | {"xlt", "application/vnd.ms-excel"}, 1202 | {"xltm", "application/vnd.ms-excel.template.macroenabled.12"}, 1203 | {"xltx", 1204 | "application/vnd.openxmlformats-officedocument.spreadsheetml.template"}, 1205 | {"xlw", "application/vnd.ms-excel"}, 1206 | {"xm", "audio/xm"}, 1207 | {"xml", "text/xml"}, 1208 | {"xns", "application/xcap-ns+xml"}, 1209 | {"xo", "application/vnd.olpc-sugar"}, 1210 | {"xop", "application/xop+xml"}, 1211 | {"xpi", "application/x-xpinstall"}, 1212 | {"xpl", "application/xproc+xml"}, 1213 | {"xpm", "image/x-xpixmap"}, 1214 | {"xpr", "application/vnd.is-xpr"}, 1215 | {"xps", "application/vnd.ms-xpsdocument"}, 1216 | {"xpw", "application/vnd.intercon.formnet"}, 1217 | {"xpx", "application/vnd.intercon.formnet"}, 1218 | {"xsd", "application/xml"}, 1219 | {"xsl", "application/xslt+xml"}, 1220 | {"xslt", "application/xslt+xml"}, 1221 | {"xsm", "application/vnd.syncml+xml"}, 1222 | {"xspf", "application/xspf+xml"}, 1223 | {"xul", "application/vnd.mozilla.xul+xml"}, 1224 | {"xvm", "application/xv+xml"}, 1225 | {"xvml", "application/xv+xml"}, 1226 | {"xwd", "image/x-xwindowdump"}, 1227 | {"xyz", "chemical/x-xyz"}, 1228 | {"xz", "application/x-xz"}, 1229 | {"yaml", "text/yaml"}, 1230 | {"yang", "application/yang"}, 1231 | {"yin", "application/yin+xml"}, 1232 | {"yml", "text/yaml"}, 1233 | {"ymp", "text/x-suse-ymp"}, 1234 | {"z1", "application/x-zmachine"}, 1235 | {"z2", "application/x-zmachine"}, 1236 | {"z3", "application/x-zmachine"}, 1237 | {"z4", "application/x-zmachine"}, 1238 | {"z5", "application/x-zmachine"}, 1239 | {"z6", "application/x-zmachine"}, 1240 | {"z7", "application/x-zmachine"}, 1241 | {"z8", "application/x-zmachine"}, 1242 | {"zaz", "application/vnd.zzazz.deck+xml"}, 1243 | {"zip", "application/zip"}, 1244 | {"zir", "application/vnd.zul"}, 1245 | {"zirz", "application/vnd.zul"}, 1246 | {"zmm", "application/vnd.handheld-entertainment+xml"}, 1247 | }; 1248 | 1249 | auto it = mime_map.find(ext); 1250 | if (it != mime_map.end()) { 1251 | return it->second; 1252 | } 1253 | return "application/octet-stream"; 1254 | } 1255 | 1256 | class Status { 1257 | public: 1258 | Status(){}; // Used for empty Statuses 1259 | Status(int num, std::string description); 1260 | 1261 | int num() { return m_status_num; }; 1262 | std::string description() { return m_description; }; 1263 | 1264 | private: 1265 | int m_status_num{}; 1266 | std::string m_description; 1267 | std::string m_class; 1268 | }; 1269 | 1270 | static std::map Statuses = { 1271 | // 1XX 1272 | {100, Status(100, std::string("Continue"))}, 1273 | {101, Status(101, std::string("Switching Protocols"))}, 1274 | {102, Status(102, std::string("Processing"))}, // Not supported (WebDEV) 1275 | {103, Status(103, std::string("Early Hints"))}, 1276 | // 2XX 1277 | {200, Status(200, std::string("OK"))}, 1278 | {201, Status(201, std::string("Created"))}, 1279 | {202, Status(202, std::string("Accepted"))}, 1280 | {203, Status(203, std::string("Non-Authoritative Information"))}, 1281 | {204, Status(204, std::string("No Content"))}, 1282 | {205, Status(205, std::string("Reset Content"))}, 1283 | {206, Status(206, std::string("Partial Content"))}, 1284 | {207, Status(207, std::string("Multi-Status"))}, // Not supported (WebDAV) 1285 | {208, 1286 | Status(208, std::string("Already Reported"))}, // Not supported (WebDAV) 1287 | {226, Status(226, std::string( 1288 | "IM Used"))}, // Not supported (HTTP Delta Encoding) 1289 | // 3XX 1290 | {300, Status(300, std::string("Multiple Choices"))}, 1291 | {301, Status(301, std::string("Moved Permanently"))}, 1292 | {302, Status(302, std::string("Found"))}, 1293 | {303, Status(303, std::string("See Other"))}, 1294 | {304, Status(304, std::string("Not Modified"))}, 1295 | {305, Status(305, std::string("Use Proxy"))}, 1296 | {306, Status(306, std::string("unused"))}, 1297 | {307, Status(307, std::string("Temporary Redirect"))}, 1298 | {308, Status(308, std::string("Permanent Redirect"))}, 1299 | // 4XX 1300 | {400, Status(400, std::string("Bad Request"))}, 1301 | {401, Status(401, std::string("Unauthorized"))}, 1302 | {402, Status(402, std::string("Payment Required"))}, 1303 | {403, Status(403, std::string("Forbidden"))}, 1304 | {404, Status(404, std::string("Not Found"))}, 1305 | {405, Status(405, std::string("Method Not Allowed"))}, 1306 | {406, Status(406, std::string("Not Acceptable"))}, 1307 | {407, Status(407, std::string("Proxy Authentication Required"))}, 1308 | {408, Status(408, std::string("Request Timeout"))}, 1309 | {409, Status(409, std::string("Conflict"))}, 1310 | {410, Status(410, std::string("Gone"))}, 1311 | {411, Status(411, std::string("Length Required"))}, 1312 | {412, Status(412, std::string("Precondition Failed"))}, 1313 | {413, Status(413, std::string("Payload Too Large"))}, 1314 | {414, Status(414, std::string("URI Too Long"))}, 1315 | {415, Status(415, std::string("Unsupported Media Type"))}, 1316 | {416, Status(416, std::string("Range Not Satisfiable"))}, 1317 | {417, Status(417, std::string("Expectation Failed"))}, 1318 | {418, Status(418, std::string("I'm a teapot"))}, 1319 | {421, Status(421, std::string("Misdirected Request"))}, 1320 | {422, Status(422, std::string( 1321 | "Unprocessable Entity"))}, // Not supported (WebDAV) 1322 | {423, Status(423, std::string("Locked"))}, // Not supported (WebDAV) 1323 | {424, 1324 | Status(424, std::string("Failed Dependency"))}, // Not supported (WebDAV) 1325 | {425, Status(425, std::string("Too Early"))}, 1326 | {426, Status(426, std::string("Upgrade Required"))}, 1327 | {428, Status(428, std::string("Precondition Required"))}, 1328 | {429, Status(429, std::string("Too Many Requests"))}, 1329 | {431, Status(431, std::string("Request Header Fields Too Large"))}, 1330 | {451, Status(451, std::string("Unavailable For Legal Reasons"))}, 1331 | // 5XX 1332 | {500, Status(500, std::string("Internal Server Error"))}, 1333 | {501, Status(501, std::string("Not Implemented"))}, 1334 | {502, Status(502, std::string("Bad Gateway"))}, 1335 | {503, Status(503, std::string("Service Unavailable"))}, 1336 | {504, Status(504, std::string("Gateway Timeout"))}, 1337 | {505, Status(505, std::string("HTTP Version Not Supported"))}, 1338 | {506, Status(506, std::string("Variant Also Negotiates"))}, 1339 | {507, Status(507, std::string( 1340 | "Insufficient Storage"))}, // Not supported (WebDAV) 1341 | {510, Status(510, std::string("Not Extended"))}, 1342 | {511, Status(511, std::string("Network Authentication Required"))}, 1343 | 1344 | }; 1345 | 1346 | class Request { 1347 | public: 1348 | Request(); 1349 | ~Request(){}; 1350 | 1351 | void set_method(Method method) { m_method = method; }; 1352 | void set_version(float version) { m_version = version; } 1353 | void set_body(std::string body) { m_body = body; }; 1354 | void set_path(std::string path) { m_path = path; }; 1355 | void set_status(Status status) { m_status = status; }; 1356 | 1357 | bool add_header(std::string key, std::string value); 1358 | bool remove_header(std::string key); 1359 | std::string get_header(std::string key); 1360 | 1361 | bool add_query_param(std::string key, std::string value); 1362 | bool remove_query_param(std::string key); 1363 | 1364 | static Request from_raw(std::string raw_request); 1365 | std::string to_raw(); 1366 | 1367 | std::string path() { return m_path; }; 1368 | std::string method(); 1369 | Status status() { return m_status; }; 1370 | 1371 | private: 1372 | Method m_method; 1373 | std::string m_host; 1374 | std::string m_path; 1375 | float m_version{1.1}; 1376 | std::map m_headers; 1377 | std::map m_query; 1378 | std::string m_body; 1379 | Status m_status; 1380 | }; 1381 | 1382 | static Request build_304_response() { 1383 | Request r; 1384 | r.set_status(Statuses[304]); 1385 | 1386 | return r; 1387 | } 1388 | 1389 | static Request build_404_response() { 1390 | Request r; 1391 | r.set_status(Statuses[404]); 1392 | r.set_body("

404 Page not found :(

"); 1393 | 1394 | return r; 1395 | } 1396 | 1397 | static Request build_500_response() { 1398 | Request r; 1399 | r.set_status(Statuses[500]); 1400 | r.set_body("

500 Internal server error occurred :(

"); 1401 | 1402 | return r; 1403 | } 1404 | 1405 | static Request NotModified = build_304_response(); 1406 | static Request PageNotFoundResponse = build_404_response(); 1407 | static Request InternalServerErrorResponse = build_500_response(); 1408 | 1409 | } // namespace HTTP --------------------------------------------------------------------------------