1
0
Fork 0
mirror of https://github.com/ossrs/srs.git synced 2025-03-09 15:49:59 +00:00

refine code, extrat the ISrsHttpMessage.

This commit is contained in:
winlin 2015-05-22 22:24:05 +08:00
parent e2955da78f
commit ce1bb6c605
13 changed files with 1323 additions and 1202 deletions

View file

@ -22,3 +22,717 @@
*/
#include <srs_http_stack.hpp>
#include <sstream>
using namespace std;
#include <srs_kernel_error.hpp>
#include <srs_kernel_log.hpp>
#include <srs_kernel_utility.hpp>
#include <srs_kernel_file.hpp>
#define SRS_HTTP_DEFAULT_PAGE "index.html"
// get the status text of code.
string srs_generate_http_status_text(int status)
{
static std::map<int, std::string> _status_map;
if (_status_map.empty()) {
_status_map[SRS_CONSTS_HTTP_Continue ] = SRS_CONSTS_HTTP_Continue_str ;
_status_map[SRS_CONSTS_HTTP_SwitchingProtocols ] = SRS_CONSTS_HTTP_SwitchingProtocols_str ;
_status_map[SRS_CONSTS_HTTP_OK ] = SRS_CONSTS_HTTP_OK_str ;
_status_map[SRS_CONSTS_HTTP_Created ] = SRS_CONSTS_HTTP_Created_str ;
_status_map[SRS_CONSTS_HTTP_Accepted ] = SRS_CONSTS_HTTP_Accepted_str ;
_status_map[SRS_CONSTS_HTTP_NonAuthoritativeInformation ] = SRS_CONSTS_HTTP_NonAuthoritativeInformation_str ;
_status_map[SRS_CONSTS_HTTP_NoContent ] = SRS_CONSTS_HTTP_NoContent_str ;
_status_map[SRS_CONSTS_HTTP_ResetContent ] = SRS_CONSTS_HTTP_ResetContent_str ;
_status_map[SRS_CONSTS_HTTP_PartialContent ] = SRS_CONSTS_HTTP_PartialContent_str ;
_status_map[SRS_CONSTS_HTTP_MultipleChoices ] = SRS_CONSTS_HTTP_MultipleChoices_str ;
_status_map[SRS_CONSTS_HTTP_MovedPermanently ] = SRS_CONSTS_HTTP_MovedPermanently_str ;
_status_map[SRS_CONSTS_HTTP_Found ] = SRS_CONSTS_HTTP_Found_str ;
_status_map[SRS_CONSTS_HTTP_SeeOther ] = SRS_CONSTS_HTTP_SeeOther_str ;
_status_map[SRS_CONSTS_HTTP_NotModified ] = SRS_CONSTS_HTTP_NotModified_str ;
_status_map[SRS_CONSTS_HTTP_UseProxy ] = SRS_CONSTS_HTTP_UseProxy_str ;
_status_map[SRS_CONSTS_HTTP_TemporaryRedirect ] = SRS_CONSTS_HTTP_TemporaryRedirect_str ;
_status_map[SRS_CONSTS_HTTP_BadRequest ] = SRS_CONSTS_HTTP_BadRequest_str ;
_status_map[SRS_CONSTS_HTTP_Unauthorized ] = SRS_CONSTS_HTTP_Unauthorized_str ;
_status_map[SRS_CONSTS_HTTP_PaymentRequired ] = SRS_CONSTS_HTTP_PaymentRequired_str ;
_status_map[SRS_CONSTS_HTTP_Forbidden ] = SRS_CONSTS_HTTP_Forbidden_str ;
_status_map[SRS_CONSTS_HTTP_NotFound ] = SRS_CONSTS_HTTP_NotFound_str ;
_status_map[SRS_CONSTS_HTTP_MethodNotAllowed ] = SRS_CONSTS_HTTP_MethodNotAllowed_str ;
_status_map[SRS_CONSTS_HTTP_NotAcceptable ] = SRS_CONSTS_HTTP_NotAcceptable_str ;
_status_map[SRS_CONSTS_HTTP_ProxyAuthenticationRequired ] = SRS_CONSTS_HTTP_ProxyAuthenticationRequired_str ;
_status_map[SRS_CONSTS_HTTP_RequestTimeout ] = SRS_CONSTS_HTTP_RequestTimeout_str ;
_status_map[SRS_CONSTS_HTTP_Conflict ] = SRS_CONSTS_HTTP_Conflict_str ;
_status_map[SRS_CONSTS_HTTP_Gone ] = SRS_CONSTS_HTTP_Gone_str ;
_status_map[SRS_CONSTS_HTTP_LengthRequired ] = SRS_CONSTS_HTTP_LengthRequired_str ;
_status_map[SRS_CONSTS_HTTP_PreconditionFailed ] = SRS_CONSTS_HTTP_PreconditionFailed_str ;
_status_map[SRS_CONSTS_HTTP_RequestEntityTooLarge ] = SRS_CONSTS_HTTP_RequestEntityTooLarge_str ;
_status_map[SRS_CONSTS_HTTP_RequestURITooLarge ] = SRS_CONSTS_HTTP_RequestURITooLarge_str ;
_status_map[SRS_CONSTS_HTTP_UnsupportedMediaType ] = SRS_CONSTS_HTTP_UnsupportedMediaType_str ;
_status_map[SRS_CONSTS_HTTP_RequestedRangeNotSatisfiable ] = SRS_CONSTS_HTTP_RequestedRangeNotSatisfiable_str ;
_status_map[SRS_CONSTS_HTTP_ExpectationFailed ] = SRS_CONSTS_HTTP_ExpectationFailed_str ;
_status_map[SRS_CONSTS_HTTP_InternalServerError ] = SRS_CONSTS_HTTP_InternalServerError_str ;
_status_map[SRS_CONSTS_HTTP_NotImplemented ] = SRS_CONSTS_HTTP_NotImplemented_str ;
_status_map[SRS_CONSTS_HTTP_BadGateway ] = SRS_CONSTS_HTTP_BadGateway_str ;
_status_map[SRS_CONSTS_HTTP_ServiceUnavailable ] = SRS_CONSTS_HTTP_ServiceUnavailable_str ;
_status_map[SRS_CONSTS_HTTP_GatewayTimeout ] = SRS_CONSTS_HTTP_GatewayTimeout_str ;
_status_map[SRS_CONSTS_HTTP_HTTPVersionNotSupported ] = SRS_CONSTS_HTTP_HTTPVersionNotSupported_str ;
}
std::string status_text;
if (_status_map.find(status) == _status_map.end()) {
status_text = "Status Unknown";
} else {
status_text = _status_map[status];
}
return status_text;
}
// bodyAllowedForStatus reports whether a given response status code
// permits a body. See RFC2616, section 4.4.
bool srs_go_http_body_allowd(int status)
{
if (status >= 100 && status <= 199) {
return false;
} else if (status == 204 || status == 304) {
return false;
}
return true;
}
// DetectContentType implements the algorithm described
// at http://mimesniff.spec.whatwg.org/ to determine the
// Content-Type of the given data. It considers at most the
// first 512 bytes of data. DetectContentType always returns
// a valid MIME type: if it cannot determine a more specific one, it
// returns "application/octet-stream".
string srs_go_http_detect(char* data, int size)
{
// detect only when data specified.
if (data) {
}
return "application/octet-stream"; // fallback
}
// Error replies to the request with the specified error message and HTTP code.
// The error message should be plain text.
int srs_go_http_error(ISrsHttpResponseWriter* w, int code, string error)
{
int ret = ERROR_SUCCESS;
w->header()->set_content_type("text/plain; charset=utf-8");
w->header()->set_content_length(error.length());
w->write_header(code);
w->write((char*)error.data(), (int)error.length());
return ret;
}
int srs_http_response_json(ISrsHttpResponseWriter* w, string data)
{
SrsHttpHeader* h = w->header();
h->set_content_length(data.length());
h->set_content_type("application/json");
return w->write((char*)data.data(), (int)data.length());
}
SrsHttpHeader::SrsHttpHeader()
{
}
SrsHttpHeader::~SrsHttpHeader()
{
}
void SrsHttpHeader::set(string key, string value)
{
headers[key] = value;
}
string SrsHttpHeader::get(string key)
{
std::string v;
if (headers.find(key) != headers.end()) {
v = headers[key];
}
return v;
}
int64_t SrsHttpHeader::content_length()
{
std::string cl = get("Content-Length");
if (cl.empty()) {
return -1;
}
return (int64_t)::atof(cl.c_str());
}
void SrsHttpHeader::set_content_length(int64_t size)
{
char buf[64];
snprintf(buf, sizeof(buf), "%"PRId64, size);
set("Content-Length", buf);
}
string SrsHttpHeader::content_type()
{
return get("Content-Type");
}
void SrsHttpHeader::set_content_type(string ct)
{
set("Content-Type", ct);
}
void SrsHttpHeader::write(stringstream& ss)
{
std::map<std::string, std::string>::iterator it;
for (it = headers.begin(); it != headers.end(); ++it) {
ss << it->first << ": " << it->second << SRS_HTTP_CRLF;
}
}
ISrsHttpResponseWriter::ISrsHttpResponseWriter()
{
}
ISrsHttpResponseWriter::~ISrsHttpResponseWriter()
{
}
ISrsHttpResponseReader::ISrsHttpResponseReader()
{
}
ISrsHttpResponseReader::~ISrsHttpResponseReader()
{
}
ISrsHttpHandler::ISrsHttpHandler()
{
entry = NULL;
}
ISrsHttpHandler::~ISrsHttpHandler()
{
}
SrsHttpRedirectHandler::SrsHttpRedirectHandler(string u, int c)
{
url = u;
code = c;
}
SrsHttpRedirectHandler::~SrsHttpRedirectHandler()
{
}
int SrsHttpRedirectHandler::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r)
{
int ret = ERROR_SUCCESS;
// TODO: FIXME: implements it.
return ret;
}
SrsHttpNotFoundHandler::SrsHttpNotFoundHandler()
{
}
SrsHttpNotFoundHandler::~SrsHttpNotFoundHandler()
{
}
int SrsHttpNotFoundHandler::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r)
{
return srs_go_http_error(w, SRS_CONSTS_HTTP_NotFound, SRS_CONSTS_HTTP_NotFound_str);
}
SrsHttpFileServer::SrsHttpFileServer(string root_dir)
{
dir = root_dir;
}
SrsHttpFileServer::~SrsHttpFileServer()
{
}
int SrsHttpFileServer::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r)
{
string upath = r->path();
// add default pages.
if (srs_string_ends_with(upath, "/")) {
upath += SRS_HTTP_DEFAULT_PAGE;
}
string fullpath = dir + "/";
// remove the virtual directory.
srs_assert(entry);
size_t pos = entry->pattern.find("/");
if (upath.length() > entry->pattern.length() && pos != string::npos) {
fullpath += upath.substr(entry->pattern.length() - pos);
} else {
fullpath += upath;
}
// stat current dir, if exists, return error.
if (!srs_path_exists(fullpath)) {
srs_warn("http miss file=%s, pattern=%s, upath=%s",
fullpath.c_str(), entry->pattern.c_str(), upath.c_str());
return SrsHttpNotFoundHandler().serve_http(w, r);
}
srs_trace("http match file=%s, pattern=%s, upath=%s",
fullpath.c_str(), entry->pattern.c_str(), upath.c_str());
// handle file according to its extension.
// use vod stream for .flv/.fhv
if (srs_string_ends_with(fullpath, ".flv") || srs_string_ends_with(fullpath, ".fhv")) {
return serve_flv_file(w, r, fullpath);
} else if (srs_string_ends_with(fullpath, ".mp4")) {
return serve_mp4_file(w, r, fullpath);
}
// serve common static file.
return serve_file(w, r, fullpath);
}
int SrsHttpFileServer::serve_file(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, string fullpath)
{
int ret = ERROR_SUCCESS;
// open the target file.
SrsFileReader fs;
if ((ret = fs.open(fullpath)) != ERROR_SUCCESS) {
srs_warn("open file %s failed, ret=%d", fullpath.c_str(), ret);
return ret;
}
int64_t length = fs.filesize();
// unset the content length to encode in chunked encoding.
w->header()->set_content_length(length);
static std::map<std::string, std::string> _mime;
if (_mime.empty()) {
_mime[".ts"] = "video/MP2T";
_mime[".flv"] = "video/x-flv";
_mime[".m4v"] = "video/x-m4v";
_mime[".3gpp"] = "video/3gpp";
_mime[".3gp"] = "video/3gpp";
_mime[".mp4"] = "video/mp4";
_mime[".aac"] = "audio/x-aac";
_mime[".mp3"] = "audio/mpeg";
_mime[".m4a"] = "audio/x-m4a";
_mime[".ogg"] = "audio/ogg";
// @see hls-m3u8-draft-pantos-http-live-streaming-12.pdf, page 5.
_mime[".m3u8"] = "application/vnd.apple.mpegurl"; // application/x-mpegURL
_mime[".rss"] = "application/rss+xml";
_mime[".json"] = "application/json";
_mime[".swf"] = "application/x-shockwave-flash";
_mime[".doc"] = "application/msword";
_mime[".zip"] = "application/zip";
_mime[".rar"] = "application/x-rar-compressed";
_mime[".xml"] = "text/xml";
_mime[".html"] = "text/html";
_mime[".js"] = "text/javascript";
_mime[".css"] = "text/css";
_mime[".ico"] = "image/x-icon";
_mime[".png"] = "image/png";
_mime[".jpeg"] = "image/jpeg";
_mime[".jpg"] = "image/jpeg";
_mime[".gif"] = "image/gif";
}
if (true) {
size_t pos;
std::string ext = fullpath;
if ((pos = ext.rfind(".")) != string::npos) {
ext = ext.substr(pos);
}
if (_mime.find(ext) == _mime.end()) {
w->header()->set_content_type("application/octet-stream");
} else {
w->header()->set_content_type(_mime[ext]);
}
}
// write body.
int64_t left = length;
if ((ret = copy(w, &fs, r, (int)left)) != ERROR_SUCCESS) {
if (!srs_is_client_gracefully_close(ret)) {
srs_error("read file=%s size=%d failed, ret=%d", fullpath.c_str(), left, ret);
}
return ret;
}
return w->final_request();
}
int SrsHttpFileServer::serve_flv_file(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, string fullpath)
{
std::string start = r->query_get("start");
if (start.empty()) {
return serve_file(w, r, fullpath);
}
int offset = ::atoi(start.c_str());
if (offset <= 0) {
return serve_file(w, r, fullpath);
}
return serve_flv_stream(w, r, fullpath, offset);
}
int SrsHttpFileServer::serve_mp4_file(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, string fullpath)
{
// for flash to request mp4 range in query string.
// for example, http://digitalprimates.net/dash/DashTest.html?url=http://dashdemo.edgesuite.net/digitalprimates/nexus/oops-20120802-manifest.mpd
std::string range = r->query_get("range");
// or, use bytes to request range,
// for example, http://dashas.castlabs.com/demo/try.html
if (range.empty()) {
range = r->query_get("bytes");
}
// rollback to serve whole file.
size_t pos = string::npos;
if (range.empty() || (pos = range.find("-")) == string::npos) {
return serve_file(w, r, fullpath);
}
// parse the start in query string
int start = 0;
if (pos > 0) {
start = ::atoi(range.substr(0, pos).c_str());
}
// parse end in query string.
int end = -1;
if (pos < range.length() - 1) {
end = ::atoi(range.substr(pos + 1).c_str());
}
// invalid param, serve as whole mp4 file.
if (start < 0 || (end != -1 && start > end)) {
return serve_file(w, r, fullpath);
}
return serve_mp4_stream(w, r, fullpath, start, end);
}
int SrsHttpFileServer::serve_flv_stream(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, string fullpath, int offset)
{
return serve_file(w, r, fullpath);
}
int SrsHttpFileServer::serve_mp4_stream(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, string fullpath, int start, int end)
{
return serve_file(w, r, fullpath);
}
int SrsHttpFileServer::copy(ISrsHttpResponseWriter* w, SrsFileReader* fs, ISrsHttpMessage* r, int size)
{
int ret = ERROR_SUCCESS;
int left = size;
char* buf = r->http_ts_send_buffer();
while (left > 0) {
ssize_t nread = -1;
int max_read = srs_min(left, SRS_HTTP_TS_SEND_BUFFER_SIZE);
if ((ret = fs->read(buf, max_read, &nread)) != ERROR_SUCCESS) {
break;
}
left -= nread;
if ((ret = w->write(buf, (int)nread)) != ERROR_SUCCESS) {
break;
}
}
return ret;
}
SrsHttpMuxEntry::SrsHttpMuxEntry()
{
enabled = true;
explicit_match = false;
handler = NULL;
}
SrsHttpMuxEntry::~SrsHttpMuxEntry()
{
srs_freep(handler);
}
ISrsHttpMatchHijacker::ISrsHttpMatchHijacker()
{
}
ISrsHttpMatchHijacker::~ISrsHttpMatchHijacker()
{
}
SrsHttpServeMux::SrsHttpServeMux()
{
}
SrsHttpServeMux::~SrsHttpServeMux()
{
std::map<std::string, SrsHttpMuxEntry*>::iterator it;
for (it = entries.begin(); it != entries.end(); ++it) {
SrsHttpMuxEntry* entry = it->second;
srs_freep(entry);
}
entries.clear();
vhosts.clear();
hijackers.clear();
}
int SrsHttpServeMux::initialize()
{
int ret = ERROR_SUCCESS;
// TODO: FIXME: implements it.
return ret;
}
void SrsHttpServeMux::hijack(ISrsHttpMatchHijacker* h)
{
std::vector<ISrsHttpMatchHijacker*>::iterator it = ::find(hijackers.begin(), hijackers.end(), h);
if (it != hijackers.end()) {
return;
}
hijackers.push_back(h);
}
void SrsHttpServeMux::unhijack(ISrsHttpMatchHijacker* h)
{
std::vector<ISrsHttpMatchHijacker*>::iterator it = ::find(hijackers.begin(), hijackers.end(), h);
if (it == hijackers.end()) {
return;
}
hijackers.erase(it);
}
int SrsHttpServeMux::handle(std::string pattern, ISrsHttpHandler* handler)
{
int ret = ERROR_SUCCESS;
srs_assert(handler);
if (pattern.empty()) {
ret = ERROR_HTTP_PATTERN_EMPTY;
srs_error("http: empty pattern. ret=%d", ret);
return ret;
}
if (entries.find(pattern) != entries.end()) {
SrsHttpMuxEntry* exists = entries[pattern];
if (exists->explicit_match) {
ret = ERROR_HTTP_PATTERN_DUPLICATED;
srs_error("http: multiple registrations for %s. ret=%d", pattern.c_str(), ret);
return ret;
}
}
std::string vhost = pattern;
if (pattern.at(0) != '/') {
if (pattern.find("/") != string::npos) {
vhost = pattern.substr(0, pattern.find("/"));
}
vhosts[vhost] = handler;
}
if (true) {
SrsHttpMuxEntry* entry = new SrsHttpMuxEntry();
entry->explicit_match = true;
entry->handler = handler;
entry->pattern = pattern;
entry->handler->entry = entry;
if (entries.find(pattern) != entries.end()) {
SrsHttpMuxEntry* exists = entries[pattern];
srs_freep(exists);
}
entries[pattern] = entry;
}
// Helpful behavior:
// If pattern is /tree/, insert an implicit permanent redirect for /tree.
// It can be overridden by an explicit registration.
if (pattern != "/" && !pattern.empty() && pattern.at(pattern.length() - 1) == '/') {
std::string rpattern = pattern.substr(0, pattern.length() - 1);
SrsHttpMuxEntry* entry = NULL;
// free the exists not explicit entry
if (entries.find(rpattern) != entries.end()) {
SrsHttpMuxEntry* exists = entries[rpattern];
if (!exists->explicit_match) {
entry = exists;
}
}
// create implicit redirect.
if (!entry || entry->explicit_match) {
srs_freep(entry);
entry = new SrsHttpMuxEntry();
entry->explicit_match = false;
entry->handler = new SrsHttpRedirectHandler(pattern, SRS_CONSTS_HTTP_MovedPermanently);
entry->pattern = pattern;
entry->handler->entry = entry;
entries[rpattern] = entry;
}
}
return ret;
}
int SrsHttpServeMux::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r)
{
int ret = ERROR_SUCCESS;
ISrsHttpHandler* h = NULL;
if ((ret = find_handler(r, &h)) != ERROR_SUCCESS) {
srs_error("find handler failed. ret=%d", ret);
return ret;
}
srs_assert(h);
if ((ret = h->serve_http(w, r)) != ERROR_SUCCESS) {
if (!srs_is_client_gracefully_close(ret)) {
srs_error("handler serve http failed. ret=%d", ret);
}
return ret;
}
return ret;
}
int SrsHttpServeMux::find_handler(ISrsHttpMessage* r, ISrsHttpHandler** ph)
{
int ret = ERROR_SUCCESS;
// TODO: FIXME: support the path . and ..
if (r->url().find("..") != std::string::npos) {
ret = ERROR_HTTP_URL_NOT_CLEAN;
srs_error("htt url not canonical, url=%s. ret=%d", r->url().c_str(), ret);
return ret;
}
if ((ret = match(r, ph)) != ERROR_SUCCESS) {
srs_error("http match handler failed. ret=%d", ret);
return ret;
}
// always hijack.
if (!hijackers.empty()) {
// notice all hijacker the match failed.
std::vector<ISrsHttpMatchHijacker*>::iterator it;
for (it = hijackers.begin(); it != hijackers.end(); ++it) {
ISrsHttpMatchHijacker* hijacker = *it;
if ((ret = hijacker->hijack(r, ph)) != ERROR_SUCCESS) {
srs_error("hijacker match failed. ret=%d", ret);
return ret;
}
}
}
if (*ph == NULL) {
// TODO: FIXME: memory leak.
*ph = new SrsHttpNotFoundHandler();
}
return ret;
}
int SrsHttpServeMux::match(ISrsHttpMessage* r, ISrsHttpHandler** ph)
{
int ret = ERROR_SUCCESS;
std::string path = r->path();
// Host-specific pattern takes precedence over generic ones
if (!vhosts.empty() && vhosts.find(r->host()) != vhosts.end()) {
path = r->host() + path;
}
int nb_matched = 0;
ISrsHttpHandler* h = NULL;
std::map<std::string, SrsHttpMuxEntry*>::iterator it;
for (it = entries.begin(); it != entries.end(); ++it) {
std::string pattern = it->first;
SrsHttpMuxEntry* entry = it->second;
if (!entry->enabled) {
continue;
}
if (!path_match(pattern, path)) {
continue;
}
if (!h || (int)pattern.length() > nb_matched) {
nb_matched = (int)pattern.length();
h = entry->handler;
}
}
*ph = h;
return ret;
}
bool SrsHttpServeMux::path_match(string pattern, string path)
{
if (pattern.empty()) {
return false;
}
int n = (int)pattern.length();
// not endswith '/', exactly match.
if (pattern.at(n - 1) != '/') {
return pattern == path;
}
// endswith '/', match any,
// for example, '/api/' match '/api/[N]'
if ((int)path.length() >= n) {
if (memcmp(pattern.data(), path.data(), n) == 0) {
return true;
}
}
return false;
}
ISrsHttpMessage::ISrsHttpMessage()
{
_http_ts_send_buffer = new char[SRS_HTTP_TS_SEND_BUFFER_SIZE];
}
ISrsHttpMessage::~ISrsHttpMessage()
{
srs_freep(_http_ts_send_buffer);
}
char* ISrsHttpMessage::http_ts_send_buffer()
{
return _http_ts_send_buffer;
}

View file

@ -29,4 +29,466 @@
*/
#include <srs_core.hpp>
#include <map>
#include <string>
#include <vector>
class SrsFileReader;
class SrsHttpHeader;
class ISrsHttpMessage;
class SrsHttpMuxEntry;
class ISrsHttpResponseWriter;
// http specification
// CR = <US-ASCII CR, carriage return (13)>
#define SRS_HTTP_CR SRS_CONSTS_CR // 0x0D
// LF = <US-ASCII LF, linefeed (10)>
#define SRS_HTTP_LF SRS_CONSTS_LF // 0x0A
// SP = <US-ASCII SP, space (32)>
#define SRS_HTTP_SP ' ' // 0x20
// HT = <US-ASCII HT, horizontal-tab (9)>
#define SRS_HTTP_HT '\x09' // 0x09
// HTTP/1.1 defines the sequence CR LF as the end-of-line marker for all
// protocol elements except the entity-body (see appendix 19.3 for
// tolerant applications).
#define SRS_HTTP_CRLF "\r\n" // 0x0D0A
#define SRS_HTTP_CRLFCRLF "\r\n\r\n" // 0x0D0A0D0A
// @see ISrsHttpMessage._http_ts_send_buffer
#define SRS_HTTP_TS_SEND_BUFFER_SIZE 4096
// for ead all of http body, read each time.
#define SRS_HTTP_READ_CACHE_BYTES 4096
// default http listen port.
#define SRS_DEFAULT_HTTP_PORT 80
// for http parser macros
#define SRS_CONSTS_HTTP_OPTIONS HTTP_OPTIONS
#define SRS_CONSTS_HTTP_GET HTTP_GET
#define SRS_CONSTS_HTTP_POST HTTP_POST
#define SRS_CONSTS_HTTP_PUT HTTP_PUT
#define SRS_CONSTS_HTTP_DELETE HTTP_DELETE
// helper function: response in json format.
extern int srs_http_response_json(ISrsHttpResponseWriter* w, std::string data);
// get the status text of code.
extern std::string srs_generate_http_status_text(int status);
// bodyAllowedForStatus reports whether a given response status code
// permits a body. See RFC2616, section 4.4.
extern bool srs_go_http_body_allowd(int status);
// DetectContentType implements the algorithm described
// at http://mimesniff.spec.whatwg.org/ to determine the
// Content-Type of the given data. It considers at most the
// first 512 bytes of data. DetectContentType always returns
// a valid MIME type: if it cannot determine a more specific one, it
// returns "application/octet-stream".
extern std::string srs_go_http_detect(char* data, int size);
// state of message
enum SrsHttpParseState {
SrsHttpParseStateInit = 0,
SrsHttpParseStateStart,
SrsHttpParseStateHeaderComplete,
SrsHttpParseStateMessageComplete
};
// A Header represents the key-value pairs in an HTTP header.
class SrsHttpHeader
{
private:
std::map<std::string, std::string> headers;
public:
SrsHttpHeader();
virtual ~SrsHttpHeader();
public:
// Add adds the key, value pair to the header.
// It appends to any existing values associated with key.
virtual void set(std::string key, std::string value);
// Get gets the first value associated with the given key.
// If there are no values associated with the key, Get returns "".
// To access multiple values of a key, access the map directly
// with CanonicalHeaderKey.
virtual std::string get(std::string key);
public:
/**
* get the content length. -1 if not set.
*/
virtual int64_t content_length();
/**
* set the content length by header "Content-Length"
*/
virtual void set_content_length(int64_t size);
public:
/**
* get the content type. empty string if not set.
*/
virtual std::string content_type();
/**
* set the content type by header "Content-Type"
*/
virtual void set_content_type(std::string ct);
public:
/**
* write all headers to string stream.
*/
virtual void write(std::stringstream& ss);
};
// A ResponseWriter interface is used by an HTTP handler to
// construct an HTTP response.
// Usage 1, response with specified length content:
// ISrsHttpResponseWriter* w; // create or get response.
// std::string msg = "Hello, HTTP!";
// w->header()->set_content_type("text/plain; charset=utf-8");
// w->header()->set_content_length(msg.length());
// w->write_header(SRS_CONSTS_HTTP_OK);
// w->write((char*)msg.data(), (int)msg.length());
// w->final_request(); // optional flush.
// Usage 2, response with HTTP code only, zero content length.
// ISrsHttpResponseWriter* w; // create or get response.
// w->header()->set_content_length(0);
// w->write_header(SRS_CONSTS_HTTP_OK);
// w->final_request();
// Usage 3, response in chunked encoding.
// ISrsHttpResponseWriter* w; // create or get response.
// std::string msg = "Hello, HTTP!";
// w->header()->set_content_type("application/octet-stream");
// w->write_header(SRS_CONSTS_HTTP_OK);
// w->write((char*)msg.data(), (int)msg.length());
// w->write((char*)msg.data(), (int)msg.length());
// w->write((char*)msg.data(), (int)msg.length());
// w->write((char*)msg.data(), (int)msg.length());
// w->final_request(); // required to end the chunked and flush.
class ISrsHttpResponseWriter
{
public:
ISrsHttpResponseWriter();
virtual ~ISrsHttpResponseWriter();
public:
// when chunked mode,
// final the request to complete the chunked encoding.
// for no-chunked mode,
// final to send request, for example, content-length is 0.
virtual int final_request() = 0;
// Header returns the header map that will be sent by WriteHeader.
// Changing the header after a call to WriteHeader (or Write) has
// no effect.
virtual SrsHttpHeader* header() = 0;
// Write writes the data to the connection as part of an HTTP reply.
// If WriteHeader has not yet been called, Write calls WriteHeader(http.StatusOK)
// before writing the data. If the Header does not contain a
// Content-Type line, Write adds a Content-Type set to the result of passing
// the initial 512 bytes of written data to DetectContentType.
// @param data, the data to send. NULL to flush header only.
virtual int write(char* data, int size) = 0;
// WriteHeader sends an HTTP response header with status code.
// If WriteHeader is not called explicitly, the first call to Write
// will trigger an implicit WriteHeader(http.StatusOK).
// Thus explicit calls to WriteHeader are mainly used to
// send error codes.
// @remark, user must set header then write or write_header.
virtual void write_header(int code) = 0;
};
/**
* the reader interface for http response.
*/
class ISrsHttpResponseReader
{
public:
ISrsHttpResponseReader();
virtual ~ISrsHttpResponseReader();
public:
/**
* whether response read EOF.
*/
virtual bool eof() = 0;
/**
* read from the response body.
* @param data, the buffer to read data buffer to.
* @param nb_data, the max size of data buffer.
* @param nb_read, the actual read size of bytes. NULL to ignore.
* @remark when eof(), return error.
*/
virtual int read(char* data, int nb_data, int* nb_read) = 0;
};
// Objects implementing the Handler interface can be
// registered to serve a particular path or subtree
// in the HTTP server.
//
// ServeHTTP should write reply headers and data to the ResponseWriter
// and then return. Returning signals that the request is finished
// and that the HTTP server can move on to the next request on
// the connection.
class ISrsHttpHandler
{
public:
SrsHttpMuxEntry* entry;
public:
ISrsHttpHandler();
virtual ~ISrsHttpHandler();
public:
virtual int serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r) = 0;
};
// Redirect to a fixed URL
class SrsHttpRedirectHandler : public ISrsHttpHandler
{
private:
std::string url;
int code;
public:
SrsHttpRedirectHandler(std::string u, int c);
virtual ~SrsHttpRedirectHandler();
public:
virtual int serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r);
};
// NotFound replies to the request with an HTTP 404 not found error.
class SrsHttpNotFoundHandler : public ISrsHttpHandler
{
public:
SrsHttpNotFoundHandler();
virtual ~SrsHttpNotFoundHandler();
public:
virtual int serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r);
};
// FileServer returns a handler that serves HTTP requests
// with the contents of the file system rooted at root.
//
// To use the operating system's file system implementation,
// use http.Dir:
//
// http.Handle("/", SrsHttpFileServer("/tmp"))
// http.Handle("/", SrsHttpFileServer("static-dir"))
class SrsHttpFileServer : public ISrsHttpHandler
{
protected:
std::string dir;
public:
SrsHttpFileServer(std::string root_dir);
virtual ~SrsHttpFileServer();
public:
virtual int serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r);
private:
/**
* serve the file by specified path
*/
virtual int serve_file(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, std::string fullpath);
virtual int serve_flv_file(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, std::string fullpath);
virtual int serve_mp4_file(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, std::string fullpath);
protected:
/**
* when access flv file with x.flv?start=xxx
*/
virtual int serve_flv_stream(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, std::string fullpath, int offset);
/**
* when access mp4 file with x.mp4?range=start-end
* @param start the start offset in bytes.
* @param end the end offset in bytes. -1 to end of file.
* @remark response data in [start, end].
*/
virtual int serve_mp4_stream(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, std::string fullpath, int start, int end);
protected:
/**
* copy the fs to response writer in size bytes.
*/
virtual int copy(ISrsHttpResponseWriter* w, SrsFileReader* fs, ISrsHttpMessage* r, int size);
};
// the mux entry for server mux.
// the matcher info, for example, the pattern and handler.
class SrsHttpMuxEntry
{
public:
bool explicit_match;
ISrsHttpHandler* handler;
std::string pattern;
bool enabled;
public:
SrsHttpMuxEntry();
virtual ~SrsHttpMuxEntry();
};
/**
* the hijacker for http pattern match.
*/
class ISrsHttpMatchHijacker
{
public:
ISrsHttpMatchHijacker();
virtual ~ISrsHttpMatchHijacker();
public:
/**
* when match the request failed, no handler to process request.
* @param request the http request message to match the handler.
* @param ph the already matched handler, hijack can rewrite it.
*/
virtual int hijack(ISrsHttpMessage* request, ISrsHttpHandler** ph) = 0;
};
// ServeMux is an HTTP request multiplexer.
// It matches the URL of each incoming request against a list of registered
// patterns and calls the handler for the pattern that
// most closely matches the URL.
//
// Patterns name fixed, rooted paths, like "/favicon.ico",
// or rooted subtrees, like "/images/" (note the trailing slash).
// Longer patterns take precedence over shorter ones, so that
// if there are handlers registered for both "/images/"
// and "/images/thumbnails/", the latter handler will be
// called for paths beginning "/images/thumbnails/" and the
// former will receive requests for any other paths in the
// "/images/" subtree.
//
// Note that since a pattern ending in a slash names a rooted subtree,
// the pattern "/" matches all paths not matched by other registered
// patterns, not just the URL with Path == "/".
//
// Patterns may optionally begin with a host name, restricting matches to
// URLs on that host only. Host-specific patterns take precedence over
// general patterns, so that a handler might register for the two patterns
// "/codesearch" and "codesearch.google.com/" without also taking over
// requests for "http://www.google.com/".
//
// ServeMux also takes care of sanitizing the URL request path,
// redirecting any request containing . or .. elements to an
// equivalent .- and ..-free URL.
class SrsHttpServeMux
{
private:
// the pattern handler, to handle the http request.
std::map<std::string, SrsHttpMuxEntry*> entries;
// the vhost handler.
// when find the handler to process the request,
// append the matched vhost when pattern not starts with /,
// for example, for pattern /live/livestream.flv of vhost ossrs.net,
// the path will rewrite to ossrs.net/live/livestream.flv
std::map<std::string, ISrsHttpHandler*> vhosts;
// all hijackers for http match.
// for example, the hstrs(http stream trigger rtmp source)
// can hijack and install handler when request incoming and no handler.
std::vector<ISrsHttpMatchHijacker*> hijackers;
public:
SrsHttpServeMux();
virtual ~SrsHttpServeMux();
public:
/**
* initialize the http serve mux.
*/
virtual int initialize();
/**
* hijack the http match.
*/
virtual void hijack(ISrsHttpMatchHijacker* h);
virtual void unhijack(ISrsHttpMatchHijacker* h);
public:
// Handle registers the handler for the given pattern.
// If a handler already exists for pattern, Handle panics.
virtual int handle(std::string pattern, ISrsHttpHandler* handler);
// interface ISrsHttpHandler
public:
virtual int serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r);
private:
virtual int find_handler(ISrsHttpMessage* r, ISrsHttpHandler** ph);
virtual int match(ISrsHttpMessage* r, ISrsHttpHandler** ph);
virtual bool path_match(std::string pattern, std::string path);
};
// for http header.
typedef std::pair<std::string, std::string> SrsHttpHeaderField;
// A Request represents an HTTP request received by a server
// or to be sent by a client.
//
// The field semantics differ slightly between client and server
// usage. In addition to the notes on the fields below, see the
// documentation for Request.Write and RoundTripper.
/**
* the http message, request or response.
*/
class ISrsHttpMessage
{
private:
/**
* use a buffer to read and send ts file.
*/
// TODO: FIXME: remove it.
char* _http_ts_send_buffer;
public:
ISrsHttpMessage();
virtual ~ISrsHttpMessage();
public:
/**
* the http request level cache.
*/
virtual char* http_ts_send_buffer();
public:
virtual u_int8_t method() = 0;
virtual u_int16_t status_code() = 0;
/**
* method helpers.
*/
virtual std::string method_str() = 0;
virtual bool is_http_get() = 0;
virtual bool is_http_put() = 0;
virtual bool is_http_post() = 0;
virtual bool is_http_delete() = 0;
virtual bool is_http_options() = 0;
public:
/**
* whether should keep the connection alive.
*/
virtual bool is_keep_alive() = 0;
/**
* the uri contains the host and path.
*/
virtual std::string uri() = 0;
/**
* the url maybe the path.
*/
virtual std::string url() = 0;
virtual std::string host() = 0;
virtual std::string path() = 0;
virtual std::string ext() = 0;
public:
/**
* read body to string.
* @remark for small http body.
*/
virtual int body_read_all(std::string& body) = 0;
/**
* get the body reader, to read one by one.
* @remark when body is very large, or chunked, use this.
*/
virtual ISrsHttpResponseReader* body_reader() = 0;
/**
* the content length, -1 for chunked or not set.
*/
virtual int64_t content_length() = 0;
public:
/**
* get the param in query string,
* for instance, query is "start=100&end=200",
* then query_get("start") is "100", and query_get("end") is "200"
*/
virtual std::string query_get(std::string key) = 0;
/**
* get the headers.
*/
virtual int request_header_count() = 0;
virtual std::string request_header_key_at(int index) = 0;
virtual std::string request_header_value_at(int index) = 0;
};
#endif