├── Release └── HttpMediaServer.exe ├── HttpMediaServer.sln ├── ReadMe.txt ├── HttpMediaServer_VS9.sln ├── LICENSE.txt ├── PathEnumerator.h ├── Thread.h ├── Response.h ├── Utils.h ├── Sockets.h ├── Utils.cpp ├── RequestParser.h ├── PathEnumerator.cpp ├── HttpMediaServer.cpp ├── HttpMediaServer.vcxproj ├── Thread.cpp ├── RequestParser.cpp ├── Sockets.cpp ├── HttpMediaServer_VS9.vcproj └── Response.cpp /Release/HttpMediaServer.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cpearce/HttpMediaServer/HEAD/Release/HttpMediaServer.exe -------------------------------------------------------------------------------- /HttpMediaServer.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 11.00 3 | # Visual Studio 2010 4 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "HttpMediaServer", "HttpMediaServer.vcxproj", "{1F99DEE3-AF25-4BE4-87F4-546C9887C968}" 5 | EndProject 6 | Global 7 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 8 | Debug|Win32 = Debug|Win32 9 | Release|Win32 = Release|Win32 10 | EndGlobalSection 11 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 12 | {1F99DEE3-AF25-4BE4-87F4-546C9887C968}.Debug|Win32.ActiveCfg = Debug|Win32 13 | {1F99DEE3-AF25-4BE4-87F4-546C9887C968}.Debug|Win32.Build.0 = Debug|Win32 14 | {1F99DEE3-AF25-4BE4-87F4-546C9887C968}.Release|Win32.ActiveCfg = Release|Win32 15 | {1F99DEE3-AF25-4BE4-87F4-546C9887C968}.Release|Win32.Build.0 = Release|Win32 16 | EndGlobalSection 17 | GlobalSection(SolutionProperties) = preSolution 18 | HideSolutionNode = FALSE 19 | EndGlobalSection 20 | EndGlobal 21 | -------------------------------------------------------------------------------- /ReadMe.txt: -------------------------------------------------------------------------------- 1 | HttpMediaServer 2 | 3 | A simple HTTP server for testing HTML5 media under rate-limited or 4 | live-streaming conditions. 5 | 6 | BUILDING: Compile on Linux using the build-linux.sh script, or on Windows 7 | using with the Visual Studio project HttpMediaServer.sln. 8 | 9 | USAGE: Just run the HttpMediaServer executable, and all files in the 10 | working directory and (child folders) will be served. 11 | 12 | On Windows the server runs on port 80, and port 8080 on Linux. 13 | 14 | To serve files with rate limiting, append a query parameter rate=N to the 15 | URL, where N is the rate in KB/s, e.g.: 16 | http://localhost:80/video.webm?rate=200 serves video.webm at 200KB/s 17 | 18 | To simulate a live stream, append a query parameter "live" to the URL, e.g.: 19 | http://localhost:80/video.webm?live 20 | 21 | To simulate the round trip delay, append a query parameter "delay=N" to 22 | the URL, where N is the delay in milliseconds, e.g.: 23 | http://localhost:80/video.webm?delay=200 will wait 200 ms before sending. 24 | 25 | Live streams are served without HTTP1.1 byte ranges being supported, and 26 | without a Content-Length HTTP header. 27 | 28 | These query parameters can of course be combined, e.g.: 29 | http://localhost:80/video.webm?live&rate=200 30 | -------------------------------------------------------------------------------- /HttpMediaServer_VS9.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 10.00 3 | # Visual Studio 2008 4 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "HttpMediaServer", "HttpMediaServer_VS9.vcproj", "{A248803F-BDE9-4D93-873B-201324929482}" 5 | EndProject 6 | Global 7 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 8 | Debug|Win32 = Debug|Win32 9 | Debug|x64 = Debug|x64 10 | Release|Win32 = Release|Win32 11 | Release|x64 = Release|x64 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {A248803F-BDE9-4D93-873B-201324929482}.Debug|Win32.ActiveCfg = Debug|Win32 15 | {A248803F-BDE9-4D93-873B-201324929482}.Debug|Win32.Build.0 = Debug|Win32 16 | {A248803F-BDE9-4D93-873B-201324929482}.Debug|x64.ActiveCfg = Debug|x64 17 | {A248803F-BDE9-4D93-873B-201324929482}.Debug|x64.Build.0 = Debug|x64 18 | {A248803F-BDE9-4D93-873B-201324929482}.Release|Win32.ActiveCfg = Release|Win32 19 | {A248803F-BDE9-4D93-873B-201324929482}.Release|Win32.Build.0 = Release|Win32 20 | {A248803F-BDE9-4D93-873B-201324929482}.Release|x64.ActiveCfg = Release|x64 21 | {A248803F-BDE9-4D93-873B-201324929482}.Release|x64.Build.0 = Release|x64 22 | EndGlobalSection 23 | GlobalSection(SolutionProperties) = preSolution 24 | HideSolutionNode = FALSE 25 | EndGlobalSection 26 | EndGlobal 27 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011, Chris Pearce 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are 6 | met: 7 | 8 | * Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | * Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in the 12 | documentation and/or other materials provided with the distribution. 13 | * Neither the name of Chris Pearce nor the names of its 14 | contributors may be used to endorse or promote products derived from 15 | this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 18 | IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 19 | TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 20 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 23 | TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 24 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 25 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 26 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /PathEnumerator.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2011, Nils Maier 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions 6 | are met: 7 | 8 | - Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 11 | - Redistributions in binary form must reproduce the above copyright 12 | notice, this list of conditions and the following disclaimer in the 13 | documentation and/or other materials provided with the distribution. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 16 | ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 17 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 18 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE ORGANISATION OR 19 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 20 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 21 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 22 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 23 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 24 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #ifndef __PATHENUMERATOR_H__ 29 | #define __PATHENUMERATOR_H__ 30 | 31 | #include 32 | 33 | class PathEnumerator { 34 | public: 35 | static PathEnumerator* getEnumerator(const std::string& path); 36 | protected: 37 | PathEnumerator() {}; 38 | public: 39 | virtual ~PathEnumerator() {}; 40 | virtual bool next(std::string& file) const = 0; 41 | }; 42 | 43 | #endif -------------------------------------------------------------------------------- /Thread.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2011, Chris Pearce 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions 6 | are met: 7 | 8 | - Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 11 | - Redistributions in binary form must reproduce the above copyright 12 | notice, this list of conditions and the following disclaimer in the 13 | documentation and/or other materials provided with the distribution. 14 | 15 | - Neither the name of Chris Pearce nor the names of its 16 | contributors may be used to endorse or promote products derived from 17 | this software without specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 22 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE ORGANISATION OR 23 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 24 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 25 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | #ifndef __THREAD_H__ 33 | #define __THREAD_H__ 34 | 35 | // Platform independent thread wrapper. 36 | 37 | class Runnable { 38 | public: 39 | virtual void Run() = 0; 40 | }; 41 | 42 | class Thread { 43 | public: 44 | static Thread* Create(Runnable* aRunnable); 45 | virtual ~Thread() {} 46 | virtual void Join() = 0; 47 | virtual void Start() = 0; 48 | protected: 49 | Thread(Runnable* aRunnable) 50 | : mRunnable(aRunnable) {} 51 | Runnable* mRunnable; 52 | }; 53 | 54 | #ifdef _DEBUG 55 | void Thread_Test(); 56 | #endif 57 | 58 | #endif 59 | -------------------------------------------------------------------------------- /Response.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2011, Chris Pearce 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions 6 | are met: 7 | 8 | - Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 11 | - Redistributions in binary form must reproduce the above copyright 12 | notice, this list of conditions and the following disclaimer in the 13 | documentation and/or other materials provided with the distribution. 14 | 15 | - Neither the name of Chris Pearce nor the names of its 16 | contributors may be used to endorse or promote products derived from 17 | this software without specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 22 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE ORGANISATION OR 23 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 24 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 25 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | #ifndef __RESPONSE_H__ 33 | #define __RESPONSE_H__ 34 | 35 | #include "Utils.h" 36 | #include "RequestParser.h" 37 | #include "Sockets.h" 38 | 39 | class Response { 40 | 41 | enum eMode { 42 | GET_ENTIRE_FILE, 43 | GET_FILE_RANGE, 44 | DIR_LIST, 45 | ERROR_FILE_NOT_EXIST, 46 | INTERNAL_ERROR 47 | }; 48 | 49 | public: 50 | Response(RequestParser p); 51 | 52 | bool SendHeaders(Socket* aSocket); 53 | 54 | // Returns true if we need to call again. 55 | bool SendBody(Socket *aSocket); 56 | 57 | #ifdef _DEBUG 58 | static void Test(); 59 | #endif 60 | 61 | private: 62 | 63 | static string StatusCode(eMode mode); 64 | 65 | static string ExtractContentType(const string& file, eMode mode); 66 | 67 | static string GetDate(); 68 | 69 | int64_t fileLength; 70 | RequestParser parser; 71 | eMode mode; 72 | string path; 73 | FILE* file; 74 | int64_t rangeStart; 75 | int64_t rangeEnd; 76 | int64_t offset; 77 | int64_t bytesRemaining; 78 | }; 79 | 80 | #endif -------------------------------------------------------------------------------- /Utils.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2011, Chris Pearce 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions 6 | are met: 7 | 8 | - Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 11 | - Redistributions in binary form must reproduce the above copyright 12 | notice, this list of conditions and the following disclaimer in the 13 | documentation and/or other materials provided with the distribution. 14 | 15 | - Neither the name of Chris Pearce nor the names of its 16 | contributors may be used to endorse or promote products derived from 17 | this software without specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 22 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE ORGANISATION OR 23 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 24 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 25 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | #ifndef __UTILS_H__ 33 | #define __UTILS_H__ 34 | 35 | #ifdef _WIN32 36 | typedef __int64 int64_t; 37 | typedef unsigned __int64 uint64_t; 38 | #define atoll _atoi64 39 | #define snprintf sprintf_s 40 | #else 41 | #include 42 | #endif 43 | 44 | #include 45 | #include 46 | #include 47 | #include 48 | #include 49 | 50 | using std::map; 51 | using std::string; 52 | using std::vector; 53 | using std::cout; 54 | using std::cerr; 55 | using std::endl; 56 | 57 | string ToString(int64_t i); 58 | string ToString(int i); 59 | 60 | string Flatten(const map& m); 61 | 62 | bool ContainsKey(const map m, const string& key); 63 | 64 | inline string& StrToLower(string& s) { 65 | std::transform(s.begin(), s.end(), s.begin(), ::tolower); 66 | return s; 67 | } 68 | 69 | /* 70 | Usage: 71 | 72 | vector tokens; 73 | string str("Split,me up!Word2 Word3."); 74 | Tokenize(str, tokens, ",<'> " ); 75 | vector ::iterator iter; 76 | for(iter = tokens.begin();iter!=tokens.end();iter++){ 77 | cout<< (*iter) << endl; 78 | } 79 | 80 | */ 81 | void Tokenize(const string& str, 82 | vector& tokens, 83 | const string& delimiters); 84 | 85 | #define ARRAY_LENGTH(array_) \ 86 | (sizeof(array_)/sizeof(array_[0])) 87 | 88 | #endif 89 | -------------------------------------------------------------------------------- /Sockets.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2011, Chris Pearce 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions 6 | are met: 7 | 8 | - Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 11 | - Redistributions in binary form must reproduce the above copyright 12 | notice, this list of conditions and the following disclaimer in the 13 | documentation and/or other materials provided with the distribution. 14 | 15 | - Neither the name of Chris Pearce nor the names of its 16 | contributors may be used to endorse or promote products derived from 17 | this software without specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 22 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE ORGANISATION OR 23 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 24 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 25 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | #ifndef __SOCKETS_H__ 33 | #define __SOCKETS_H__ 34 | 35 | #include 36 | 37 | using std::string; 38 | 39 | // Wraps platform-specific socket API. 40 | class Socket { 41 | protected: 42 | int mSocket; 43 | 44 | Socket(int aSocket) : mSocket(aSocket) {} 45 | 46 | // Wait on the socket for a read to become available 47 | bool WaitForRead(unsigned timeout); 48 | 49 | public: 50 | // Initialize sockets library. 51 | static int Init(); 52 | 53 | // Shutdown sockets library. 54 | static int Shutdown(); 55 | 56 | virtual ~Socket() {} 57 | 58 | // Opens a port for inbound connections. Use this to create a socket 59 | // and open an inbound port. 60 | static Socket* Open(int aPort); 61 | 62 | // Accepts connections on an open port. 63 | virtual Socket* Accept() = 0; 64 | 65 | // Shutsdown connection. 66 | virtual void Close() = 0; 67 | 68 | // Sends data over socket. Returns number of bytes sent, or -1 on error. 69 | // Blocking. 70 | virtual int Send(const char* aBuf, int aSize) = 0; 71 | 72 | // Reads data from an open socket. Returns number of bytes read, or 0 if 73 | // connection is closing. <0 on error. Blocking. 74 | virtual int Receive(char* aBuf, int aSize) = 0; 75 | 76 | // Returns a string representation of the local IP address. 77 | string GetIP() const; 78 | 79 | protected: 80 | Socket() {} 81 | }; 82 | 83 | #endif 84 | -------------------------------------------------------------------------------- /Utils.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2011, Chris Pearce 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions 6 | are met: 7 | 8 | - Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 11 | - Redistributions in binary form must reproduce the above copyright 12 | notice, this list of conditions and the following disclaimer in the 13 | documentation and/or other materials provided with the distribution. 14 | 15 | - Neither the name of Chris Pearce nor the names of its 16 | contributors may be used to endorse or promote products derived from 17 | this software without specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 22 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE ORGANISATION OR 23 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 24 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 25 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | #include "Utils.h" 33 | #include 34 | 35 | string ToString(int64_t i) { 36 | char buf[256]; 37 | snprintf(buf, 256, "%lld", i); 38 | return string(buf); 39 | } 40 | 41 | string ToString(int i) { 42 | char buf[12]; 43 | snprintf(buf, 12, "%d", i); 44 | return string(buf); 45 | } 46 | 47 | string Flatten(const map& m) { 48 | string s; 49 | map::const_iterator itr = m.begin(); 50 | while (itr != m.end()) { 51 | string key = itr->first; 52 | string value = itr->second; 53 | s.append(key); 54 | s.append("='"); 55 | s.append(value); 56 | s.append("'"); 57 | itr++; 58 | if (itr != m.end()) { 59 | s.append(" "); 60 | } 61 | } 62 | return s; 63 | } 64 | 65 | bool ContainsKey(const map m, const string& key) { 66 | return m.count(key) > 0; 67 | } 68 | 69 | void Tokenize(const string& str, 70 | vector& tokens, 71 | const string& delimiters) 72 | { 73 | // Skip delimiters at beginning. 74 | string::size_type lastPos = str.find_first_not_of(delimiters, 0); 75 | // Find first "non-delimiter". 76 | string::size_type pos = str.find_first_of(delimiters, lastPos); 77 | while (string::npos != pos || string::npos != lastPos) 78 | { 79 | // Found a token, add it to the vector. 80 | tokens.push_back(str.substr(lastPos, pos - lastPos)); 81 | // Skip delimiters. Note the "not_of" 82 | lastPos = str.find_first_not_of(delimiters, pos); 83 | // Find next "non-delimiter" 84 | pos = str.find_first_of(delimiters, lastPos); 85 | } 86 | } -------------------------------------------------------------------------------- /RequestParser.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2011, Chris Pearce 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions 6 | are met: 7 | 8 | - Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 11 | - Redistributions in binary form must reproduce the above copyright 12 | notice, this list of conditions and the following disclaimer in the 13 | documentation and/or other materials provided with the distribution. 14 | 15 | - Neither the name of Chris Pearce nor the names of its 16 | contributors may be used to endorse or promote products derived from 17 | this software without specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 22 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE ORGANISATION OR 23 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 24 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 25 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | #ifndef __REQUEST_PARSER_H__ 33 | #define __REQUEST_PARSER_H__ 34 | 35 | #include "Utils.h" 36 | 37 | enum eMethod { UNKNOWN, HEAD, GET, POST }; 38 | 39 | class RequestParser { 40 | public: 41 | RequestParser(); 42 | 43 | void Add(const char* buf, unsigned len); 44 | 45 | bool IsComplete() { 46 | return complete; 47 | } 48 | 49 | eMethod GetMethod() { 50 | return method; 51 | } 52 | 53 | string GetTarget() { 54 | return target; 55 | } 56 | 57 | const map& GetParams() const { 58 | return params; 59 | } 60 | 61 | void GetRange(int64_t& start, int64_t& end) const; 62 | 63 | bool IsRangeRequest() const { 64 | return hasRange; 65 | } 66 | 67 | bool IsLive() const { 68 | return ContainsKey(GetParams(), "live"); 69 | } 70 | 71 | bool HasSpecifiedMimeType() const { 72 | return ContainsKey(GetParams(), "mime"); 73 | } 74 | 75 | string GetSpecifiedMimeType() const { 76 | assert(HasSpecifiedMimeType()); 77 | return GetParams().find("mime")->second; 78 | } 79 | 80 | const int id; 81 | 82 | #ifdef _DEBUG 83 | static void Test(); 84 | #endif 85 | 86 | private: 87 | 88 | static bool TestRange(bool expected, 89 | const string& s, 90 | int64_t expStart, 91 | int64_t expEnd); 92 | 93 | void Parse(const string& s); 94 | 95 | static bool ParseRange(const string& s, int64_t& start, int64_t& end); 96 | 97 | static eMethod ExtractMethod(const string& request); 98 | 99 | static string ExtractTarget(const string& request); 100 | 101 | static map ExtractQueryParams(const string& request); 102 | 103 | void ParseRequestLine(const string& request); 104 | 105 | string request; 106 | size_t start; 107 | vector lines; 108 | bool complete; 109 | eMethod method; 110 | string target; 111 | map params; 112 | int64_t rangeStart, rangeEnd; 113 | bool hasRange; 114 | }; 115 | 116 | #endif 117 | -------------------------------------------------------------------------------- /PathEnumerator.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2011, Nils Maier 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions 6 | are met: 7 | 8 | - Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 11 | - Redistributions in binary form must reproduce the above copyright 12 | notice, this list of conditions and the following disclaimer in the 13 | documentation and/or other materials provided with the distribution. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 16 | ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 17 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 18 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE ORGANISATION OR 19 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 20 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 21 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 22 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 23 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 24 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | 28 | #include "PathEnumerator.h" 29 | 30 | using std::string; 31 | 32 | #ifdef _WIN32 33 | 34 | #include 35 | 36 | class Win32PathEnumerator : public PathEnumerator { 37 | private: 38 | const string mPath; 39 | mutable HANDLE hFind; 40 | mutable WIN32_FIND_DATAA fad; 41 | 42 | static string initPath(const string& path) { 43 | string rv(path); 44 | 45 | if (rv.empty()) { 46 | return string(".\\*"); 47 | } 48 | rv.append("\\*"); 49 | return rv; 50 | } 51 | 52 | public: 53 | Win32PathEnumerator(const std::string& aPath) 54 | : mPath(initPath(aPath)), hFind(0) {} 55 | virtual ~Win32PathEnumerator() { 56 | if (hFind) { 57 | FindClose(hFind); 58 | } 59 | } 60 | virtual bool next(string& file) const { 61 | if (!hFind) { 62 | if (!(hFind = FindFirstFileA(mPath.c_str(), &fad))) { 63 | return false; 64 | } 65 | file.assign(fad.cFileName); 66 | return true; 67 | } 68 | 69 | if (FindNextFileA(hFind, &fad)) { 70 | file.assign(fad.cFileName); 71 | return true; 72 | } 73 | 74 | return false; 75 | } 76 | }; 77 | 78 | PathEnumerator* PathEnumerator::getEnumerator(const string& path) { 79 | return new Win32PathEnumerator(path); 80 | } 81 | 82 | #else 83 | 84 | #include 85 | #include 86 | 87 | class PosixPathEnumerator : public PathEnumerator { 88 | private: 89 | const string mPath; 90 | mutable DIR* dir; 91 | 92 | static string initPath(const string& path) { 93 | string rv(path); 94 | 95 | if (rv.empty()) { 96 | return string("."); 97 | } 98 | return rv; 99 | } 100 | 101 | public: 102 | PosixPathEnumerator(const std::string& aPath) 103 | : mPath(initPath(aPath)), dir(0) {} 104 | virtual ~PosixPathEnumerator() { 105 | if (dir) { 106 | closedir(dir); 107 | } 108 | } 109 | virtual bool next(string& file) const { 110 | if (!dir) { 111 | if (!(dir = opendir(mPath.c_str()))) { 112 | return false; 113 | } 114 | } 115 | 116 | struct dirent *ent; 117 | if (!(ent = readdir(dir))) { 118 | return false; 119 | } 120 | file.assign(ent->d_name); 121 | return true; 122 | } 123 | }; 124 | 125 | 126 | PathEnumerator* PathEnumerator::getEnumerator(const string& path) { 127 | return new PosixPathEnumerator(path); 128 | } 129 | 130 | #endif 131 | -------------------------------------------------------------------------------- /HttpMediaServer.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2011, Chris Pearce 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions 6 | are met: 7 | 8 | - Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 11 | - Redistributions in binary form must reproduce the above copyright 12 | notice, this list of conditions and the following disclaimer in the 13 | documentation and/or other materials provided with the distribution. 14 | 15 | - Neither the name of Chris Pearce nor the names of its 16 | contributors may be used to endorse or promote products derived from 17 | this software without specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 22 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE ORGANISATION OR 23 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 24 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 25 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | #include 38 | #include 39 | #include 40 | #include 41 | 42 | #include "Utils.h" 43 | #include "Thread.h" 44 | #include "RequestParser.h" 45 | #include "Sockets.h" 46 | #include "Response.h" 47 | 48 | using std::auto_ptr; 49 | 50 | #ifdef WIN32 51 | #define PORT 80 52 | #else 53 | #define PORT 8080 54 | #endif 55 | 56 | #define DEFAULT_BUFLEN 512 57 | 58 | class Connection : public Runnable{ 59 | public: 60 | Connection(Socket* s) 61 | : mClientSocket(s) 62 | { 63 | mThread = Thread::Create(this); 64 | } 65 | 66 | void Start() { 67 | mThread->Start(); 68 | } 69 | 70 | virtual void Run() { 71 | char recvbuf[DEFAULT_BUFLEN]; 72 | int recvbuflen = DEFAULT_BUFLEN; 73 | 74 | // Receive until the peer shuts down the connection 75 | while (!parser.IsComplete()) { 76 | int r = mClientSocket->Receive(recvbuf, recvbuflen); 77 | if (r > 0) { 78 | parser.Add(recvbuf, r); 79 | } else if (r == 0) { 80 | cout << "Connection closing..." << std::endl; 81 | break; 82 | } else { 83 | mClientSocket->Close(); 84 | return; 85 | } 86 | } 87 | 88 | Response response(parser); 89 | 90 | if (!response.SendHeaders(mClientSocket.get())) { 91 | mClientSocket->Close(); 92 | return; 93 | } 94 | 95 | while (response.SendBody(mClientSocket.get())) { 96 | // Transmit the body. 97 | } 98 | 99 | // cleanup 100 | mClientSocket->Close(); 101 | } 102 | private: 103 | auto_ptr mClientSocket; 104 | RequestParser parser; 105 | Thread* mThread; 106 | }; 107 | 108 | static bool gRunning = true; 109 | 110 | void sighandler(int signal) 111 | { 112 | gRunning = false; 113 | }; 114 | 115 | 116 | int main(int argc, char* argv[]) 117 | { 118 | #ifdef _DEBUG 119 | RequestParser::Test(); 120 | Response::Test(); 121 | Thread_Test(); 122 | #endif 123 | 124 | signal(SIGINT, sighandler); 125 | #ifdef SIGQUIT 126 | signal(SIGQUIT, sighandler); 127 | #endif 128 | 129 | Socket::Init(); 130 | 131 | auto_ptr listener(Socket::Open(PORT)); 132 | if (!listener.get()) { 133 | Socket::Shutdown(); 134 | return 1; 135 | } 136 | 137 | cout << "Local IP: " << listener->GetIP().c_str() << std::endl; 138 | cout << "Now listening on port: " << PORT << std::endl; 139 | 140 | vector Connections; 141 | while (gRunning) { 142 | // Accept a single connection. 143 | Socket* client = listener->Accept(); 144 | if (!client) { 145 | continue; 146 | } 147 | 148 | Connection *c = new Connection(client); 149 | c->Start(); 150 | Connections.push_back(c); 151 | } 152 | 153 | Socket::Shutdown(); 154 | 155 | return 0; 156 | } 157 | -------------------------------------------------------------------------------- /HttpMediaServer.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | 14 | {1F99DEE3-AF25-4BE4-87F4-546C9887C968} 15 | Win32Proj 16 | HttpMediaServer 17 | 18 | 19 | 20 | Application 21 | true 22 | Unicode 23 | 24 | 25 | Application 26 | false 27 | true 28 | Unicode 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | true 42 | 43 | 44 | false 45 | 46 | 47 | 48 | 49 | 50 | Level3 51 | Disabled 52 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 53 | 54 | 55 | Console 56 | true 57 | 58 | 59 | 60 | 61 | Level3 62 | 63 | 64 | MaxSpeed 65 | true 66 | true 67 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 68 | MultiThreaded 69 | 70 | 71 | Console 72 | true 73 | true 74 | true 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | -------------------------------------------------------------------------------- /Thread.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2011, Chris Pearce 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions 6 | are met: 7 | 8 | - Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 11 | - Redistributions in binary form must reproduce the above copyright 12 | notice, this list of conditions and the following disclaimer in the 13 | documentation and/or other materials provided with the distribution. 14 | 15 | - Neither the name of Chris Pearce nor the names of its 16 | contributors may be used to endorse or promote products derived from 17 | this software without specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 22 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE ORGANISATION OR 23 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 24 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 25 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | #include "Thread.h" 33 | #include "stdio.h" 34 | #include 35 | #include "assert.h" 36 | 37 | 38 | #ifdef _WIN32 39 | 40 | #include "windows.h" 41 | #include 42 | 43 | using namespace std; 44 | 45 | class Win32Thread : public Thread { 46 | public: 47 | Win32Thread(Runnable* aRunnable); 48 | ~Win32Thread(); 49 | void Join(); 50 | void Start(); 51 | 52 | private: 53 | HANDLE mHandle; 54 | static unsigned __stdcall ThreadEntry(void *aThis); 55 | 56 | enum ThreadState { 57 | eError = -1, 58 | eInitialized = 1, 59 | eStarted = 2, 60 | eFinished = 3, 61 | }; 62 | 63 | // State tracking, for debug purposes. 64 | ThreadState mState; 65 | }; 66 | 67 | Win32Thread::Win32Thread(Runnable* aRunnable) 68 | :Thread(aRunnable) 69 | { 70 | mHandle = (HANDLE)_beginthreadex( 71 | 0, // Security attributes 72 | 0, // Stack size: auto 73 | ThreadEntry, 74 | (void*)this, 75 | CREATE_SUSPENDED, 76 | 0); 77 | assert(mHandle != 0); 78 | if (!mHandle) { 79 | mState = eError; 80 | cerr << "Can't create thread!\n"; 81 | } else { 82 | mState = eInitialized; 83 | } 84 | } 85 | 86 | unsigned __stdcall Win32Thread::ThreadEntry(void *aThis) { 87 | Win32Thread* t = static_cast(aThis); 88 | t->mState = eStarted; 89 | t->mRunnable->Run(); 90 | t->mState = eFinished; 91 | t->mHandle = 0; 92 | return 0; 93 | } 94 | 95 | Win32Thread::~Win32Thread() { 96 | assert(mState == eFinished); 97 | CloseHandle(mHandle); 98 | } 99 | 100 | void Win32Thread::Join() { 101 | if (mState == eStarted || mState == eInitialized) { 102 | WaitForSingleObject(mHandle, INFINITE); 103 | assert(mState == eFinished); 104 | } 105 | } 106 | 107 | void Win32Thread::Start() { 108 | if (mState == eInitialized) { 109 | mState = eStarted; 110 | ResumeThread(mHandle); 111 | } 112 | } 113 | 114 | Thread* Thread::Create(Runnable *aRunnable) { 115 | return new Win32Thread(aRunnable); 116 | } 117 | 118 | #else 119 | // Assume pthreads are supported... 120 | 121 | #include "pthread.h" 122 | 123 | class PThread : public Thread { 124 | public: 125 | 126 | static void* PThreadRunner(void* aThread); 127 | 128 | PThread(Runnable* aRunnable) 129 | : Thread(aRunnable) {} 130 | 131 | ~PThread() { 132 | } 133 | 134 | void Join() { 135 | pthread_join(mThread, NULL); 136 | } 137 | 138 | void Start() { 139 | pthread_create(&mThread, 0, &PThreadRunner, this); 140 | } 141 | 142 | void CallRun() { 143 | mRunnable->Run(); 144 | } 145 | 146 | private: 147 | pthread_t mThread; 148 | }; 149 | 150 | void* PThread::PThreadRunner(void* aThread) { 151 | PThread* p = static_cast(aThread); 152 | p->CallRun(); 153 | } 154 | 155 | Thread* Thread::Create(Runnable *aRunnable) { 156 | return new PThread(aRunnable); 157 | } 158 | 159 | #endif // LINUX 160 | 161 | 162 | // Simple thread to sum the numbers between in range. 163 | class Sum : public Runnable { 164 | public: 165 | Sum(int start, int end) 166 | : mStart(start) 167 | , mEnd(end) 168 | , mResult(0) 169 | { } 170 | 171 | virtual void Run() { 172 | mResult = 0; 173 | for (int i=mStart; iJoin(); 189 | mWasRun = true; 190 | } 191 | bool mWasRun; 192 | Thread* mSumThread; 193 | }; 194 | 195 | #ifdef _DEBUG 196 | void Thread_Test() { 197 | Sum s1(0,10); 198 | Sum *s2 = new Sum(10,20); 199 | 200 | Thread* t1 = Thread::Create(&s1); 201 | Thread* t2 = Thread::Create(s2); 202 | t1->Start(); 203 | t2->Start(); 204 | 205 | t1->Join(); 206 | t2->Join(); 207 | 208 | assert(s1.mResult == 45); 209 | assert(s2->mResult == 145); 210 | 211 | delete s2; 212 | delete t2; 213 | delete t1; 214 | 215 | Sum s3(20,30); 216 | Thread* t3 = Thread::Create(&s3); 217 | Waiter w(t3); 218 | Thread* t4 = Thread::Create(&w); 219 | t4->Start(); 220 | t3->Start(); 221 | 222 | t3->Join(); 223 | t4->Join(); 224 | 225 | assert(w.mWasRun); 226 | 227 | delete t3; 228 | delete t4; 229 | } 230 | #endif 231 | -------------------------------------------------------------------------------- /RequestParser.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2011, Chris Pearce 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions 6 | are met: 7 | 8 | - Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 11 | - Redistributions in binary form must reproduce the above copyright 12 | notice, this list of conditions and the following disclaimer in the 13 | documentation and/or other materials provided with the distribution. 14 | 15 | - Neither the name of Chris Pearce nor the names of its 16 | contributors may be used to endorse or promote products derived from 17 | this software without specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 22 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE ORGANISATION OR 23 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 24 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 25 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | #include 33 | #include 34 | #include 35 | 36 | #include 37 | 38 | #include "RequestParser.h" 39 | 40 | static int gCount = 0; 41 | 42 | RequestParser::RequestParser() 43 | : start(0), 44 | complete(false), 45 | method(UNKNOWN), 46 | rangeStart(-1), 47 | rangeEnd(-1), 48 | hasRange(false), 49 | id(gCount++) 50 | { 51 | } 52 | 53 | void RequestParser::Add(const char* buf, unsigned len) { 54 | assert(!complete); 55 | string str(buf, len); 56 | request += str; 57 | const char* linebr = "\r\n"; 58 | assert(linebr[0] == 13); 59 | assert(linebr[1] == 10); 60 | // Continue to parse request. 61 | while (start < request.size()) { 62 | size_t end = request.find(linebr, start, 2); 63 | if (end == string::npos) 64 | break; 65 | if (start == end) 66 | complete = true; 67 | string l = string(request, start, end - start); 68 | Parse(l); 69 | start = end + 2; 70 | } 71 | cout << "Request " << id << std::endl << buf; 72 | } 73 | 74 | #ifdef _DEBUG 75 | void RequestParser::Test() { 76 | assert(ExtractMethod("GET / HTTP1.1") == GET); 77 | assert(ExtractMethod("HEAD / HTTP1.1") == HEAD); 78 | assert(ExtractMethod("POST / HTTP1.1") == POST); 79 | assert(ExtractMethod("Error / HTTP1.1") == UNKNOWN); 80 | 81 | assert(ExtractTarget("GET / HTTP1.1") == ""); 82 | assert(ExtractTarget("GET // HTTP1.1") == ""); 83 | assert(ExtractTarget("GET /// HTTP1.1") == ""); 84 | assert(ExtractTarget("GET /dir/file.txt HTTP1.1") == "dir/file.txt"); 85 | assert(ExtractTarget("GET /dir/file.txt?params HTTP1.1") == "dir/file.txt"); 86 | assert(ExtractTarget("GET /?params HTTP1.1") == ""); 87 | assert(ExtractTarget("GET //?params HTTP1.1") == ""); 88 | 89 | assert(Flatten(ExtractQueryParams("GET / HTTP1.1")) == ""); 90 | 91 | assert(Flatten(ExtractQueryParams("GET // HTTP1.1")) == ""); 92 | assert(Flatten(ExtractQueryParams("GET /// HTTP1.1")) == ""); 93 | assert(Flatten(ExtractQueryParams("GET /dir/file.txt HTTP1.1")) == ""); 94 | assert(Flatten(ExtractQueryParams("GET /dir/file.txt? HTTP1.1")) == ""); 95 | assert(Flatten(ExtractQueryParams("GET /dir/file.txt?params HTTP1.1")) == "params=''"); 96 | assert(Flatten(ExtractQueryParams("GET /dir/file.txt?param1¶m2¶m3 HTTP1.1")) == "param1='' param2='' param3=''"); 97 | assert(Flatten(ExtractQueryParams("GET /dir/file.txt?param1=val1¶m2=val2¶m3=val3 HTTP1.1")) == "param1='val1' param2='val2' param3='val3'"); 98 | assert(Flatten(ExtractQueryParams("GET /?params HTTP1.1")) == "params=''"); 99 | assert(Flatten(ExtractQueryParams("GET /? HTTP1.1")) == ""); 100 | assert(Flatten(ExtractQueryParams("GET //?params HTTP1.1")) == "params=''"); 101 | 102 | assert(TestRange(true, "Range: bytes=0-1024", 0, 1024)); 103 | assert(TestRange(false, "Range: time=0-1024", 0, 0)); 104 | assert(TestRange(true, "Range: bytes=0-", 0, -1)); 105 | assert(TestRange(true, "Range: bytes=1024-", 1024, -1)); 106 | assert(TestRange(true, "Range: bytes=232128512-", 232128512, -1)); 107 | } 108 | #endif 109 | 110 | void RequestParser::GetRange(int64_t& start, int64_t& end) const { 111 | start = rangeStart; 112 | end = rangeEnd; 113 | } 114 | 115 | bool RequestParser::TestRange(bool expected, 116 | const string& s, 117 | int64_t expStart, 118 | int64_t expEnd) 119 | { 120 | int64_t start=-2, end=-2; 121 | bool r = ParseRange(s, start, end); 122 | return r == expected && 123 | (!expected || start == expStart && end == expEnd); 124 | } 125 | 126 | void RequestParser::Parse(const string& s) { 127 | if (start == 0) { 128 | // Request line. 129 | ParseRequestLine(s); 130 | } else if (s.find("Range") == 0) { 131 | int64_t start,end; 132 | if (ParseRange(s, start, end)) { 133 | rangeStart = start; 134 | rangeEnd = end; 135 | hasRange = true; 136 | } 137 | } 138 | } 139 | 140 | bool RequestParser::ParseRange(const string& s, 141 | int64_t& start, 142 | int64_t& end) 143 | { 144 | // Range: bytes=start,end 145 | size_t sp = s.find(" "); 146 | size_t eq = s.find("="); 147 | if (sp == string::npos || eq == string::npos) 148 | return false; 149 | string bytes(s, sp + 1, eq - sp - 1); 150 | if (bytes != "bytes") { 151 | // The space is not followed by "bytes=", this is an invalid range param. 152 | return false; 153 | } 154 | 155 | string range(s, eq + 1); 156 | size_t dash = range.find("-"); 157 | if (dash == string::npos) 158 | return false; 159 | string startStr(range, 0, dash); 160 | string endStr(range, dash + 1); 161 | 162 | start = atoll(startStr.c_str()); 163 | if (endStr != "") { 164 | end = atoll(endStr.c_str()); 165 | } else { 166 | end = -1; 167 | } 168 | 169 | return true; 170 | } 171 | 172 | eMethod RequestParser::ExtractMethod(const string& request) { 173 | size_t sp = request.find(" "); 174 | string m(request, 0, sp); 175 | if (m == "GET") 176 | return GET; 177 | else if (m == "HEAD") 178 | return HEAD; 179 | else if (m == "POST") 180 | return POST; 181 | return UNKNOWN; 182 | } 183 | 184 | string RequestParser::ExtractTarget(const string& request) { 185 | size_t start = request.find("/") + 1; 186 | size_t query = request.find("?", start); // Location of query params. 187 | size_t end = (query != string::npos) ? query : request.find(" ", start); 188 | // Remove trailing slashes. 189 | while (end > start && request.at(end - 1) == '/') { 190 | end--; 191 | } 192 | return string(request, start, end - start); 193 | } 194 | 195 | map RequestParser::ExtractQueryParams(const string& request) { 196 | map m; 197 | size_t query = request.find("?"); 198 | if (query == string::npos) 199 | return m; 200 | size_t start = query + 1; 201 | size_t end = request.find(" ", start); 202 | vector v; 203 | Tokenize(string(request, start, end - start), v, "&"); 204 | for (unsigned i=0; i 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | #include 39 | 40 | #include "Sockets.h" 41 | #include "Utils.h" 42 | 43 | #ifdef _WIN32 44 | 45 | #include 46 | #include 47 | 48 | #pragma comment(lib, "Ws2_32.lib") 49 | 50 | class Win32Socket : public Socket { 51 | public: 52 | virtual ~Win32Socket(); 53 | Socket* Accept(); 54 | Win32Socket(SOCKET aSocket); 55 | void Close(); 56 | int Send(const char* aBuf, int aSize); 57 | int Receive(char* aBuf, int aSize); 58 | 59 | static WSADATA sWsaData; 60 | }; 61 | 62 | WSADATA Win32Socket::sWsaData = {0}; 63 | 64 | Win32Socket::Win32Socket(SOCKET aSocket) 65 | : Socket((int)aSocket) 66 | { } 67 | 68 | Win32Socket::~Win32Socket() 69 | { 70 | Close(); 71 | } 72 | 73 | Socket* Socket::Open(int aPort) { 74 | // Init a server port. 75 | struct addrinfo *addr = NULL, *ptr = NULL, hints; 76 | 77 | ZeroMemory(&hints, sizeof (hints)); 78 | hints.ai_family = AF_INET; 79 | hints.ai_socktype = SOCK_STREAM; 80 | hints.ai_protocol = IPPROTO_TCP; 81 | hints.ai_flags = AI_PASSIVE; 82 | 83 | // Resolve the local address and port to be used by the server 84 | string port = ToString(aPort); 85 | int res = getaddrinfo(NULL, port.c_str(), &hints, &addr); 86 | if (res != 0) { 87 | cerr << "getaddrinfo failed: " << res << std::endl; 88 | return 0; 89 | } 90 | 91 | SOCKET serverSocket = INVALID_SOCKET; 92 | // Create a SOCKET for the server to listen for client connections 93 | serverSocket = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol); 94 | 95 | if (serverSocket == INVALID_SOCKET) { 96 | cerr << "Error at socket(): " << WSAGetLastError() << std::endl; 97 | freeaddrinfo(addr); 98 | return 0; 99 | } 100 | 101 | // Setup the TCP listening socket 102 | res = bind( serverSocket, addr->ai_addr, (int)addr->ai_addrlen); 103 | if (res == SOCKET_ERROR) { 104 | cerr << "bind failed: " << WSAGetLastError() << std::endl; 105 | freeaddrinfo(addr); 106 | closesocket(serverSocket); 107 | return 0; 108 | } 109 | 110 | // Address info is no longer needed, we've bound the socket. 111 | freeaddrinfo(addr); 112 | 113 | if ( listen( serverSocket, SOMAXCONN ) == SOCKET_ERROR ) { 114 | cerr << "Listen failed with error: " << WSAGetLastError() << std::endl; 115 | closesocket(serverSocket); 116 | return 0; 117 | } 118 | 119 | return new Win32Socket(serverSocket); 120 | } 121 | 122 | Socket* Win32Socket::Accept() { 123 | SOCKET client = accept(mSocket, NULL, NULL); 124 | if (client == INVALID_SOCKET) { 125 | cerr << "accept failed: " << WSAGetLastError() << std::endl; 126 | closesocket(mSocket); 127 | return 0; 128 | } 129 | return new Win32Socket(client); 130 | } 131 | 132 | void Win32Socket::Close() { 133 | if (mSocket != INVALID_SOCKET) { 134 | closesocket(mSocket); 135 | mSocket = (int)INVALID_SOCKET; 136 | } 137 | } 138 | 139 | int Win32Socket::Receive(char* aBuf, int aSize) { 140 | memset(aBuf, 0, aSize); 141 | int r = recv(mSocket, aBuf, aSize, 0); 142 | if (r < 0) { 143 | cerr << "recv failed: " << WSAGetLastError() << std::endl; 144 | } 145 | return r; 146 | } 147 | 148 | int Win32Socket::Send(const char* aBuf, int aSize) { 149 | return send(mSocket, aBuf, aSize, 0); 150 | } 151 | 152 | int Socket::Init() { 153 | // Initialize Winsock 154 | int err = WSAStartup(MAKEWORD(2,2), &Win32Socket::sWsaData); 155 | if (err) { 156 | cerr << "WSAStartup failed: " << err << std::endl; 157 | return 1; 158 | } 159 | return 0; 160 | } 161 | 162 | int Socket::Shutdown() { 163 | WSACleanup(); 164 | return 0; 165 | } 166 | 167 | #else 168 | 169 | #include 170 | #include 171 | #include 172 | #include 173 | #include 174 | #include 175 | 176 | #define SOCKET_ERROR -1 177 | 178 | class UnixSocket : public Socket { 179 | public: 180 | virtual ~UnixSocket(); 181 | Socket* Accept(); 182 | UnixSocket(int aSocket); 183 | void Close(); 184 | int Send(const char* aBuf, int aSize); 185 | int Receive(char* aBuf, int aSize); 186 | }; 187 | 188 | Socket* Socket::Open(int aPort) { 189 | int sockfd; 190 | struct sockaddr_in serv_addr; 191 | sockfd = socket(AF_INET, SOCK_STREAM, 0); 192 | if (sockfd < 0) { 193 | perror("ERROR opening socket"); 194 | return 0; 195 | } 196 | bzero((char *) &serv_addr, sizeof(serv_addr)); 197 | serv_addr.sin_family = AF_INET; 198 | serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); 199 | serv_addr.sin_port = htons(aPort); 200 | if (bind(sockfd, 201 | (struct sockaddr *) &serv_addr, 202 | sizeof(serv_addr)) < 0) 203 | { 204 | perror("ERROR on binding"); 205 | return 0; 206 | } 207 | listen(sockfd,5); 208 | 209 | return new UnixSocket(sockfd); 210 | } 211 | 212 | UnixSocket::UnixSocket(int aSocket) 213 | : Socket(aSocket) 214 | { 215 | } 216 | 217 | UnixSocket::~UnixSocket() { 218 | Close(); 219 | } 220 | 221 | Socket* UnixSocket::Accept() { 222 | socklen_t clilen; 223 | struct sockaddr_in cli_addr; 224 | clilen = sizeof(cli_addr); 225 | 226 | if (!WaitForRead(500)) { 227 | return 0; 228 | } 229 | 230 | int client = accept(mSocket, 231 | (struct sockaddr *) &cli_addr, 232 | &clilen); 233 | if (client < 0) { 234 | perror("ERROR on accept"); 235 | close(mSocket); 236 | return 0; 237 | } 238 | return new UnixSocket(client); 239 | } 240 | 241 | void UnixSocket::Close() { 242 | if (mSocket != 0) { 243 | close(mSocket); 244 | mSocket = 0; 245 | } 246 | } 247 | 248 | int UnixSocket::Receive(char* aBuf, int aSize) { 249 | memset(aBuf, 0, aSize); 250 | int r = read(mSocket, aBuf, aSize); 251 | if (r < 0) { 252 | fprintf(stderr, "Receive failed\n"); 253 | } 254 | return r; 255 | } 256 | 257 | int UnixSocket::Send(const char* aBuf, int aSize) { 258 | return write(mSocket, aBuf, aSize); 259 | } 260 | 261 | int Socket::Init() { 262 | return 0; 263 | } 264 | 265 | int Socket::Shutdown() { 266 | return 0; 267 | } 268 | 269 | #endif 270 | 271 | bool Socket::WaitForRead(unsigned timeout) { 272 | fd_set socks; 273 | FD_ZERO(&socks); 274 | FD_SET((unsigned)mSocket, &socks); 275 | 276 | struct timeval to; 277 | to.tv_sec = 0; 278 | to.tv_usec = timeout; 279 | 280 | return select(mSocket + 1, &socks, 0, 0, &to) > 0; 281 | } 282 | 283 | string Socket::GetIP() const { 284 | char buf[1024]; 285 | if (gethostname(buf, sizeof(buf)) == SOCKET_ERROR) { 286 | return "Unknown"; 287 | } 288 | 289 | struct hostent *host = gethostbyname(buf); 290 | if(host == NULL) { 291 | return "Unknown"; 292 | } 293 | 294 | char* localIP; 295 | localIP = inet_ntoa(*(struct in_addr *)*host->h_addr_list); 296 | return string(localIP); 297 | } 298 | -------------------------------------------------------------------------------- /HttpMediaServer_VS9.vcproj: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 14 | 17 | 18 | 19 | 20 | 21 | 28 | 31 | 34 | 37 | 40 | 43 | 52 | 55 | 58 | 61 | 66 | 69 | 72 | 75 | 78 | 81 | 84 | 87 | 88 | 95 | 98 | 101 | 104 | 107 | 111 | 120 | 123 | 126 | 129 | 134 | 137 | 140 | 143 | 146 | 149 | 152 | 155 | 156 | 164 | 167 | 170 | 173 | 176 | 179 | 187 | 190 | 193 | 196 | 203 | 206 | 209 | 212 | 215 | 218 | 221 | 224 | 225 | 233 | 236 | 239 | 242 | 245 | 249 | 257 | 260 | 263 | 266 | 273 | 276 | 279 | 282 | 285 | 288 | 291 | 294 | 295 | 296 | 297 | 298 | 299 | 304 | 307 | 308 | 311 | 312 | 315 | 316 | 319 | 320 | 323 | 324 | 327 | 328 | 331 | 332 | 333 | 338 | 341 | 342 | 345 | 346 | 349 | 350 | 353 | 354 | 357 | 358 | 361 | 362 | 363 | 368 | 369 | 370 | 371 | 372 | 373 | -------------------------------------------------------------------------------- /Response.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2011, Chris Pearce 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions 6 | are met: 7 | 8 | - Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 11 | - Redistributions in binary form must reproduce the above copyright 12 | notice, this list of conditions and the following disclaimer in the 13 | documentation and/or other materials provided with the distribution. 14 | 15 | - Neither the name of Chris Pearce nor the names of its 16 | contributors may be used to endorse or promote products derived from 17 | this software without specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 | ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 22 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE ORGANISATION OR 23 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 24 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 25 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | 39 | #include 40 | #include 41 | 42 | #include "PathEnumerator.h" 43 | #include "Response.h" 44 | #include "Utils.h" 45 | 46 | #ifdef _WIN32 47 | #include 48 | #define S_ISDIR(m) ((m & _S_IFDIR) == _S_IFDIR) 49 | #define fseek64 _fseeki64 50 | #define ftell64 _ftelli64 51 | 52 | static int gmtime_r(const time_t *timep, struct tm *result) { 53 | return gmtime_s(result, timep) == 0; 54 | } 55 | 56 | #define DIR_LIST_CHARSET "text/html; charset=windows" 57 | 58 | #else 59 | #define __stat64 stat64 60 | #define _stat64 stat64 61 | #include 62 | #define fseek64 fseeko64 63 | #define ftell64 ftello64 64 | 65 | void Sleep(int ms) { 66 | usleep(ms * 1000); 67 | } 68 | 69 | static int fopen_s(FILE** file, const char* path, const char* mode) { 70 | FILE *f = fopen(path, mode); 71 | if (!f) { 72 | return 1; 73 | } 74 | *file = f; 75 | return 0; 76 | } 77 | 78 | #define DIR_LIST_CHARSET "text/html; charset=utf-8" 79 | 80 | #endif 81 | 82 | #define MIN(a, b) (((a) < (b)) ? (a) : (b)) 83 | 84 | 85 | static const char* gContentTypes[][2] = { 86 | {"ogv", "video/ogg"}, 87 | {"ogg", "video/ogg"}, 88 | {"oga", "audio/ogg"}, 89 | {"webm", "video/webm"}, 90 | {"wav", "audio/x-wav"}, 91 | {"html", "text/html; charset=utf-8"}, 92 | {"txt", "text/plain; charset=utf-8"}, 93 | {"js", "text/javascript; charset=utf-8"}, 94 | {"js", "text/css; charset=utf-8"}, 95 | {"jpg", "image/jpeg"}, 96 | {"jpeg", "image/jpeg"}, 97 | {"png", "image/png"}, 98 | {"gif", "image/gif"} 99 | }; 100 | 101 | Response::Response(RequestParser p) 102 | : parser(p), 103 | mode(INTERNAL_ERROR), 104 | fileLength(-1), 105 | file(0), 106 | rangeStart(0), 107 | rangeEnd(0), 108 | offset(0), 109 | bytesRemaining(0) 110 | { 111 | string target = parser.GetTarget(); 112 | if (target == "") { 113 | mode = DIR_LIST; 114 | path = "."; 115 | } else if (target.find("..") != string::npos) { 116 | mode = ERROR_FILE_NOT_EXIST; 117 | } else { 118 | // Determine if the file exists, and if it is a directory. 119 | struct __stat64 buf; 120 | int result; 121 | result = _stat64(target.c_str(), &buf ); 122 | if (result == -1) { 123 | cerr << "File not found" << std::endl; 124 | mode = ERROR_FILE_NOT_EXIST; 125 | } else if (result != 0) { 126 | mode = INTERNAL_ERROR; 127 | } else if (S_ISDIR(buf.st_mode)) { 128 | mode = DIR_LIST; 129 | path = parser.GetTarget(); 130 | } else if (parser.IsRangeRequest() && !parser.IsLive()) { 131 | mode = GET_FILE_RANGE; 132 | path = parser.GetTarget(); 133 | parser.GetRange(rangeStart, rangeEnd); 134 | if (rangeEnd == -1) { 135 | rangeEnd = buf.st_size; 136 | } 137 | fileLength = buf.st_size; 138 | } else { 139 | mode = GET_ENTIRE_FILE; 140 | path = parser.GetTarget(); 141 | fileLength = buf.st_size; 142 | } 143 | } 144 | } 145 | 146 | bool Response::SendHeaders(Socket* aSocket) { 147 | string headers; 148 | headers.append("HTTP/1.1 "); 149 | headers.append(StatusCode(mode)); 150 | headers.append("\r\n"); 151 | headers.append("Connection: close\r\n"); 152 | headers.append(GetDate()); 153 | headers.append("\r\n"); 154 | headers.append("Server: HttpMediaServer/0.1\r\n"); 155 | 156 | if (ContainsKey(parser.GetParams(), "delay")) { 157 | const map params = parser.GetParams(); 158 | string delayStr = params.find("delay")->second; 159 | double delay = atof(delayStr.c_str()); 160 | if (delay > 0.0) { 161 | Sleep((unsigned)(delay+0.5)); 162 | } 163 | } 164 | 165 | if (!parser.IsLive()) { 166 | if (mode == GET_ENTIRE_FILE) { 167 | headers.append("Accept-Ranges: bytes\r\n"); 168 | headers.append("Content-Length: "); 169 | headers.append(ToString(fileLength)); 170 | headers.append("\r\n"); 171 | } else if (mode == GET_FILE_RANGE) { 172 | headers.append("Accept-Ranges: bytes\r\n"); 173 | headers.append("Content-Length: "); 174 | headers.append(ToString(rangeEnd - rangeStart)); 175 | headers.append("\r\n"); 176 | headers.append("Content-Range: bytes "); 177 | headers.append(ToString(rangeStart)); 178 | headers.append("-"); 179 | headers.append(ToString(rangeEnd)); 180 | headers.append("/"); 181 | headers.append(ToString(fileLength)); 182 | headers.append("\r\n"); 183 | } 184 | } 185 | //headers.append("Last-Modified: Wed, 10 Nov 2009 04:58:08 GMT\r\n"); 186 | 187 | headers.append("Content-Type: "); 188 | if (parser.HasSpecifiedMimeType()) { 189 | headers.append(parser.GetSpecifiedMimeType()); 190 | } else { 191 | headers.append(ExtractContentType(path, mode)); 192 | } 193 | headers.append("\r\n\r\n"); 194 | 195 | cout << "Sending Headers " << parser.id << std::endl << headers; 196 | 197 | return aSocket->Send(headers.c_str(), (int)headers.size()) != -1; 198 | } 199 | 200 | // Returns true if we need to call again. 201 | bool Response::SendBody(Socket *aSocket) { 202 | if (mode == ERROR_FILE_NOT_EXIST) { 203 | cout << "Sent (empty) body (" << parser.id << ")" << std::endl; 204 | return false; 205 | } 206 | 207 | int len = 1024; 208 | unsigned wait = 0; 209 | string rateStr; 210 | if (ContainsKey(parser.GetParams(), "rate")) { 211 | const map params = parser.GetParams(); 212 | rateStr = params.find("rate")->second; 213 | double rate = atof(rateStr.c_str()); 214 | const double period = 0.1; 215 | if (rate <= 0.0) { 216 | len = 1024; 217 | wait = 0; 218 | } else { 219 | len = (unsigned)(rate * 1024 * period); 220 | wait = (unsigned)(period * 1000.0); // ms 221 | } 222 | } 223 | 224 | if (mode == GET_ENTIRE_FILE) { 225 | if (!file) { 226 | if (fopen_s(&file, path.c_str(), "rb")) { 227 | file = 0; 228 | return false; 229 | } 230 | } 231 | if (feof(file)) { 232 | // Transmitted entire file! 233 | fclose(file); 234 | file = 0; 235 | return false; 236 | } 237 | 238 | int64_t tell = ftell64(file); 239 | 240 | // Transmit the next segment. 241 | char* buf = new char[len]; 242 | int x = (int)fread(buf, 1, len, file); 243 | int r = aSocket->Send(buf, x); 244 | delete buf; 245 | if (r < 0) { 246 | // Some kind of error. 247 | return false; 248 | } 249 | 250 | if (wait > 0) { 251 | Sleep(wait); 252 | } 253 | // Else we tranmitted that segment, we're ok. 254 | return true; 255 | 256 | } else if (mode == GET_FILE_RANGE) { 257 | if (!file) { 258 | if (fopen_s(&file, path.c_str(), "rb")) { 259 | file = 0; 260 | return false; 261 | } 262 | fseek64(file, rangeStart, SEEK_SET); 263 | offset = rangeStart; 264 | bytesRemaining = rangeEnd - rangeStart; 265 | } 266 | if (feof(file) || bytesRemaining == 0) { 267 | // Transmitted entire range. 268 | fclose(file); 269 | file = 0; 270 | return false; 271 | } 272 | 273 | // Transmit the next segment. 274 | char* buf = new char[len]; 275 | 276 | len = (unsigned)MIN(bytesRemaining, len); 277 | size_t bytesSent = fread(buf, 1, len, file); 278 | bytesRemaining -= bytesSent; 279 | int r = aSocket->Send(buf, (int)bytesSent); 280 | delete buf; 281 | if (r < 0) { 282 | // Some kind of error. 283 | return false; 284 | } 285 | offset += bytesSent; 286 | assert(ftell64(file) == offset); 287 | 288 | if (wait > 0) { 289 | Sleep(wait); 290 | } 291 | 292 | // Else we tranmitted that segment, we're ok. 293 | return true; 294 | } 295 | else if (mode == DIR_LIST) { 296 | std::stringstream response; 297 | PathEnumerator *enumerator = PathEnumerator::getEnumerator(path); 298 | if (enumerator) { 299 | response << "\n
    "; 300 | string href; 301 | while (enumerator->next(href)) { 302 | if (href == "." || path == "." && href == "..") { 303 | continue; 304 | } 305 | response << "
  • " << href << "
  • "; 310 | } 311 | response << "
"; 312 | delete enumerator; 313 | } 314 | string _r = response.str(); 315 | aSocket->Send(_r.c_str(), (int)_r.size()); 316 | return false; 317 | } 318 | 319 | return false; 320 | } 321 | 322 | #ifdef _DEBUG 323 | void Response::Test() { 324 | assert(ExtractContentType("dir1/dir2/file.ogv", GET_ENTIRE_FILE) == string("video/ogg")); 325 | assert(ExtractContentType("dir1/dir2/file.ogg", GET_ENTIRE_FILE) == string("video/ogg")); 326 | assert(ExtractContentType("dir1/dir2/file.oga", GET_ENTIRE_FILE) == string("audio/ogg")); 327 | assert(ExtractContentType("dir1/dir2/file.wav", GET_ENTIRE_FILE) == string("audio/x-wav")); 328 | assert(ExtractContentType("dir1/dir2/file.webm", GET_ENTIRE_FILE) == string("video/webm")); 329 | assert(ExtractContentType("dir1/dir2/file.txt", GET_ENTIRE_FILE) == string("text/plain; charset=utf-8")); 330 | assert(ExtractContentType("dir1/dir2/file.html", GET_ENTIRE_FILE) == string("text/html; charset=utf-8")); 331 | assert(ExtractContentType("", DIR_LIST) == string(DIR_LIST_CHARSET)); 332 | } 333 | #endif 334 | 335 | 336 | string Response::StatusCode(eMode mode) { 337 | switch (mode) { 338 | case GET_ENTIRE_FILE: return string("200 OK"); 339 | case GET_FILE_RANGE: return string("206 OK"); 340 | case DIR_LIST: return string("200 OK"); 341 | case ERROR_FILE_NOT_EXIST: return string("404 File Not Found"); 342 | case INTERNAL_ERROR: 343 | default: 344 | return string("500 Internal Server Error"); 345 | }; 346 | } 347 | 348 | string Response::ExtractContentType(const string& file, eMode mode) { 349 | if (mode == ERROR_FILE_NOT_EXIST || mode == INTERNAL_ERROR) 350 | return "text/html; charset=utf-8"; 351 | if (mode == DIR_LIST) 352 | return DIR_LIST_CHARSET; 353 | 354 | size_t dot = file.rfind("."); 355 | if (dot == string::npos) { 356 | // File has no extension. Just send it as binary. 357 | return "application/octet-stream"; 358 | } 359 | 360 | string extension(file, dot+1, file.size() - dot - 1); 361 | StrToLower(extension); 362 | 363 | for (unsigned i=0; i