srs提供http相关功能。包括http_server http_api查询功能和http_heartbeat ,http_callback等。
首先分析http_api,这个提供一系列的http的接口,可以用来查询服务器的状态,但它的重要性并不只限于http查询,更重要的是在将来上线运营中,随着机器部署的增加。在需要在线修改机器配置的时候,也可以用这个接口。
下面来分析下一个http请求到来到回复出去所有的流程过程。
首先是http_api的配置如图
三个参数 开关 监听端口号 和跨域开关。
http_api的监听过程和我们在srs代码学习(1)中的流程是一样的,都是主线程创建一个监听线程,在连接上来以后,经srever的中转,把连接交给对应的connect类中,具体看看代码
首先是开始监听的代码
int SrsServer::listen_http_api()
{
int ret = ERROR_SUCCESS;
#ifdef SRS_AUTO_HTTP_API
close_listeners(SrsListenerHttpApi);
if (_srs_config->get_http_api_enabled()) {
SrsListener* listener = new SrsStreamListener(this, SrsListenerHttpApi);
listeners.push_back(listener);
std::string ep = _srs_config->get_http_api_listen();
std::string ip;
int port;
srs_parse_endpoint(ep, ip, port);
if ((ret = listener->listen(ip, port)) != ERROR_SUCCESS) {
srs_error("HTTP api listen at %s:%d failed. ret=%d", ip.c_str(), port, ret);
return ret;
}
}
#endif
return ret;
}
在一个http client连接到后,通过int SrsServer::accept_client(SrsListenerType type, st_netfd_t client_stfd),来建立对应的连接,
if (type == SrsListenerHttpApi) {
#ifdef SRS_AUTO_HTTP_API
conn = new SrsHttpApi(this, client_stfd, http_api_mux);
#else
srs_warn("close http client for server not support http-api");
srs_close_stfd(client_stfd);
return ret;
#endif
}
这个列是继承与SrsConnection类中的,上次我们分析这类中有一个线程在跑。主要的循环函数是do_cycle。这个是个虚函数,要由派生类来觉得具体动作。看看它的实现
int SrsHttpApi::do_cycle()
{
int ret = ERROR_SUCCESS;
srs_trace("api get peer ip success. ip=%s", ip.c_str());
// initialize parser
if ((ret = parser->initialize(HTTP_REQUEST)) != ERROR_SUCCESS) {
srs_error("api initialize http parser failed. ret=%d", ret);
return ret;
}
// underlayer socket
SrsStSocket skt(stfd);
// set the recv timeout, for some clients never disconnect the connection.
// @see https://github.com/ossrs/srs/issues/398
skt.set_recv_timeout(SRS_HTTP_RECV_TIMEOUT_US);
// process http messages.
while(!disposed) {
ISrsHttpMessage* req = NULL;
// get a http message
if ((ret = parser->parse_message(&skt, this, &req)) != ERROR_SUCCESS) {
return ret;
}
// if SUCCESS, always NOT-NULL.
srs_assert(req);
// always free it in this scope.
SrsAutoFree(ISrsHttpMessage, req);
// ok, handle http request.
SrsHttpResponseWriter writer(&skt);
if ((ret = process_request(&writer, req)) != ERROR_SUCCESS) {
return ret;
}
// read all rest bytes in request body.
char buf[SRS_HTTP_READ_CACHE_BYTES];
ISrsHttpResponseReader* br = req->body_reader();
while (!br->eof()) {
if ((ret = br->read(buf, SRS_HTTP_READ_CACHE_BYTES, NULL)) != ERROR_SUCCESS) {
return ret;
}
}
// donot keep alive, disconnect it.
// @see https://github.com/ossrs/srs/issues/399
if (!req->is_keep_alive()) {
break;
}
}
return ret;
}
int SrsHttpApi::process_request(ISrsHttpResponseWriter* w, ISrsHttpMessage* r)
{
int ret = ERROR_SUCCESS;
SrsHttpMessage* hm = dynamic_cast(r);
srs_assert(hm);
srs_trace("HTTP API %s %s, content-length=%"PRId64", chunked=%d/%d",
r->method_str().c_str(), r->url().c_str(), r->content_length(),
hm->is_chunked(), hm->is_infinite_chunked());
// method is OPTIONS and enable crossdomain, required crossdomain header.
if (r->is_http_options() && _srs_config->get_http_api_crossdomain()) {
crossdomain_required = true;
}
// whenever crossdomain required, set crossdomain header.
if (crossdomain_required) {
w->header()->set("Access-Control-Allow-Origin", "*");
w->header()->set("Access-Control-Allow-Methods", "GET, POST, HEAD, PUT, DELETE");
w->header()->set("Access-Control-Allow-Headers", "Cache-Control,X-Proxy-Authorization,X-Requested-With,Content-Type");
}
// handle the http options.
if (r->is_http_options()) {
w->header()->set_content_length(0);
if (_srs_config->get_http_api_crossdomain()) {
w->write_header(SRS_CONSTS_HTTP_OK);
} else {
w->write_header(SRS_CONSTS_HTTP_MethodNotAllowed);
}
return w->final_request();
}
// use default server mux to serve http request.
if ((ret = mux->serve_http(w, r)) != ERROR_SUCCESS) {
if (!srs_is_client_gracefully_close(ret)) {
srs_error("serve http msg failed. ret=%d", ret);
}
return ret;
}
return ret;
}
这个函数首先是做一些判断,包括跨域(这个我还不太明白),是否支持一些设置的操作。之后就是处理具体的命令里的。处理类是SrsHttpServeMux* mux,先看这个类的结构
可以看大。这个类中有两个map结构,一个vector接口,这个类是采用注册---回调的方法来处理http请求的,就是注册一个url的pattern的回调,然后发现这pattern的时候,会调用这个回调。看看作者的说明
// 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.
恩,回调函数基本都放在了
std::map entries
这个结构中,通过函数handle()来放
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_Found);
entry->pattern = pattern;
entry->handler->entry = entry;
entries[rpattern] = entry;
}
}
return ret;
}
int SrsServer::http_handle()
{
int ret = ERROR_SUCCESS;
#ifdef SRS_AUTO_HTTP_API
srs_assert(http_api_mux);
if ((ret = http_api_mux->handle("/", new SrsHttpNotFoundHandler())) != ERROR_SUCCESS) {
return ret;
}
if ((ret = http_api_mux->handle("/api/", new SrsGoApiApi())) != ERROR_SUCCESS) {
return ret;
}
if ((ret = http_api_mux->handle("/api/v1/", new SrsGoApiV1())) != ERROR_SUCCESS) {
return ret;
}
if ((ret = http_api_mux->handle("/api/v1/versions", new SrsGoApiVersion())) != ERROR_SUCCESS) {
return ret;
}
if ((ret = http_api_mux->handle("/api/v1/summaries", new SrsGoApiSummaries())) != ERROR_SUCCESS) {
return ret;
}
if ((ret = http_api_mux->handle("/api/v1/rusages", new SrsGoApiRusages())) != ERROR_SUCCESS) {
return ret;
}
if ((ret = http_api_mux->handle("/api/v1/self_proc_stats", new SrsGoApiSelfProcStats())) != ERROR_SUCCESS) {
return ret;
}
if ((ret = http_api_mux->handle("/api/v1/system_proc_stats", new SrsGoApiSystemProcStats())) != ERROR_SUCCESS) {
return ret;
}
if ((ret = http_api_mux->handle("/api/v1/meminfos", new SrsGoApiMemInfos())) != ERROR_SUCCESS) {
return ret;
}
if ((ret = http_api_mux->handle("/api/v1/authors", new SrsGoApiAuthors())) != ERROR_SUCCESS) {
return ret;
}
if ((ret = http_api_mux->handle("/api/v1/features", new SrsGoApiFeatures())) != ERROR_SUCCESS) {
return ret;
}
if ((ret = http_api_mux->handle("/api/v1/vhosts/", new SrsGoApiVhosts())) != ERROR_SUCCESS) {
return ret;
}
if ((ret = http_api_mux->handle("/api/v1/streams/", new SrsGoApiStreams())) != ERROR_SUCCESS) {
return ret;
}
if ((ret = http_api_mux->handle("/api/v1/clients/", new SrsGoApiClients())) != ERROR_SUCCESS) {
return ret;
}
// test the request info.
if ((ret = http_api_mux->handle("/api/v1/tests/requests", new SrsGoApiRequests())) != ERROR_SUCCESS) {
return ret;
}
// test the error code response.
if ((ret = http_api_mux->handle("/api/v1/tests/errors", new SrsGoApiError())) != ERROR_SUCCESS) {
return ret;
}
// test the redirect mechenism.
if ((ret = http_api_mux->handle("/api/v1/tests/redirects", new SrsHttpRedirectHandler("/api/v1/tests/errors", SRS_CONSTS_HTTP_MovedPermanently))) != ERROR_SUCCESS) {
return ret;
}
// test the http vhost.
if ((ret = http_api_mux->handle("error.srs.com/api/v1/tests/errors", new SrsGoApiError())) != ERROR_SUCCESS) {
return ret;
}
// TODO: FIXME: for console.
// TODO: FIXME: support reload.
std::string dir = _srs_config->get_http_stream_dir() + "/console";
if ((ret = http_api_mux->handle("/console/", new SrsHttpFileServer(dir))) != ERROR_SUCCESS) {
srs_error("http: mount console dir=%s failed. ret=%d", dir.c_str(), ret);
return ret;
}
srs_trace("http: api mount /console to %s", dir.c_str());
#endif
return ret;
}
int run_master()
{
int ret = ERROR_SUCCESS;
if ((ret = _srs_server->initialize_st()) != ERROR_SUCCESS) {
return ret;
}
if ((ret = _srs_server->initialize_signal()) != ERROR_SUCCESS) {
return ret;
}
if ((ret = _srs_server->acquire_pid_file()) != ERROR_SUCCESS) {
return ret;
}
if ((ret = _srs_server->listen()) != ERROR_SUCCESS) {
return ret;
}
if ((ret = _srs_server->register_signal()) != ERROR_SUCCESS) {
return ret;
}
if ((ret = _srs_server->http_handle()) != ERROR_SUCCESS) {
return ret;
}
if ((ret = _srs_server->ingest()) != ERROR_SUCCESS) {
return ret;
}
if ((ret = _srs_server->cycle()) != ERROR_SUCCESS) {
return ret;
}
return 0;
}