int SrsHttpApi::process_request(ISrsHttpResponseWriter* w, ISrsHttpMessage* r) { int ret = ERROR_SUCCESS; SrsHttpMessage* hm = dynamic_cast<SrsHttpMessage*>(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; }
int SrsHttpConn::do_cycle() { int ret = ERROR_SUCCESS; if ((ret = get_peer_ip()) != ERROR_SUCCESS) { srs_error("get peer ip failed. ret=%d", ret); return ret; } srs_trace("http get peer ip success. ip=%s", ip); // initialize parser if ((ret = parser->initialize(HTTP_REQUEST)) != ERROR_SUCCESS) { srs_error("http initialize http parser failed. ret=%d", ret); return ret; } // underlayer socket SrsSocket skt(stfd); // process http messages. for (;;) { SrsHttpMessage* req = NULL; // get a http message if ((ret = parser->parse_message(&skt, &req)) != ERROR_SUCCESS) { return ret; } // if SUCCESS, always NOT-NULL and completed message. srs_assert(req); srs_assert(req->is_complete()); // always free it in this scope. SrsAutoFree(SrsHttpMessage, req, false); // ok, handle http request. if ((ret = process_request(&skt, req)) != ERROR_SUCCESS) { return ret; } } return ret; }
int SrsLiveStream::http_hooks_on_play(ISrsHttpMessage* r) { int ret = ERROR_SUCCESS; #ifdef SRS_AUTO_HTTP_CALLBACK if (!_srs_config->get_vhost_http_hooks_enabled(req->vhost)) { return ret; } // Create request to report for the specified connection. SrsHttpMessage* hr = dynamic_cast<SrsHttpMessage*>(r); SrsRequest* nreq = hr->to_request(req->vhost); SrsAutoFree(SrsRequest, nreq); // the http hooks will cause context switch, // so we must copy all hooks for the on_connect may freed. // @see https://github.com/ossrs/srs/issues/475 vector<string> hooks; if (true) { SrsConfDirective* conf = _srs_config->get_vhost_on_play(nreq->vhost); if (!conf) { srs_info("ignore the empty http callback: on_play"); return ret; } hooks = conf->args; } for (int i = 0; i < (int)hooks.size(); i++) { std::string url = hooks.at(i); if ((ret = SrsHttpHooks::on_play(url, nreq)) != ERROR_SUCCESS) { srs_error("hook client on_play failed. url=%s, ret=%d", url.c_str(), ret); return ret; } } #endif return ret; }
int SrsHttpParser::parse_message(SrsStSocket* skt, SrsConnection* conn, ISrsHttpMessage** ppmsg) { *ppmsg = NULL; int ret = ERROR_SUCCESS; // reset request data. field_name = ""; field_value = ""; expect_field_name = true; state = SrsHttpParseStateInit; header = http_parser(); url = ""; headers.clear(); header_parsed = 0; // do parse if ((ret = parse_message_imp(skt)) != ERROR_SUCCESS) { if (!srs_is_client_gracefully_close(ret)) { srs_error("parse http msg failed. ret=%d", ret); } return ret; } // create msg SrsHttpMessage* msg = new SrsHttpMessage(skt, conn); // initalize http msg, parse url. if ((ret = msg->update(url, &header, buffer, headers)) != ERROR_SUCCESS) { srs_error("initialize http msg failed. ret=%d", ret); srs_freep(msg); return ret; } // parse ok, return the msg. *ppmsg = msg; return ret; }
int SrsHttpClient::post(SrsHttpUri* uri, string req, string& res) { res = ""; int ret = ERROR_SUCCESS; if (!parser) { parser = new SrsHttpParser(); if ((ret = parser->initialize(HTTP_RESPONSE)) != ERROR_SUCCESS) { srs_error("initialize parser failed. ret=%d", ret); return ret; } } if ((ret = connect(uri)) != ERROR_SUCCESS) { srs_error("http connect server failed. ret=%d", ret); return ret; } // send POST request to uri // POST %s HTTP/1.1\r\nHost: %s\r\nContent-Length: %d\r\n\r\n%s std::stringstream ss; ss << "POST " << uri->get_path() << " " << "HTTP/1.1" << __CRLF << "Host: " << uri->get_host() << __CRLF << "Connection: Keep-Alive" << __CRLF << "Content-Length: " << std::dec << req.length() << __CRLF << "User-Agent: " << RTMP_SIG_SRS_NAME << RTMP_SIG_SRS_VERSION << __CRLF << "Content-Type: text/html" << __CRLF << __CRLF << req; SrsSocket skt(stfd); std::string data = ss.str(); if ((ret = skt.write(data.c_str(), data.length(), NULL)) != ERROR_SUCCESS) { // disconnect when error. disconnect(); srs_error("write http post failed. ret=%d", ret); return ret; } SrsHttpMessage* msg = NULL; if ((ret = parser->parse_message(&skt, &msg)) != ERROR_SUCCESS) { srs_error("parse http post response failed. ret=%d", ret); return ret; } srs_assert(msg); srs_assert(msg->is_complete()); // get response body. if (msg->body_size() > 0) { res = msg->body(); } srs_info("parse http post response success."); return ret; }
int SrsHttpStreamServer::hijack(ISrsHttpMessage* request, ISrsHttpHandler** ph) { int ret = ERROR_SUCCESS; // when handler not the root, we think the handler is ok. ISrsHttpHandler* h = *ph? *ph : NULL; if (h && h->entry && h->entry->pattern != "/") { return ret; } // only hijack for http streaming, http-flv/ts/mp3/aac. std::string ext = request->ext(); if (ext.empty()) { return ret; } // find the actually request vhost. SrsConfDirective* vhost = _srs_config->get_vhost(request->host()); if (!vhost || !_srs_config->get_vhost_enabled(vhost)) { return ret; } // find the entry template for the stream. SrsLiveEntry* entry = NULL; if (true) { // no http streaming on vhost, ignore. std::map<std::string, SrsLiveEntry*>::iterator it = tflvs.find(vhost->arg0()); if (it == tflvs.end()) { return ret; } // hstrs not enabled, ignore. // for origin, the http stream will be mount already when publish, // so it must never enter this line for stream already mounted. // for edge, the http stream is trigger by hstrs and mount by it, // so we only hijack when only edge and hstrs is on. entry = it->second; if (!entry->hstrs) { return ret; } // check entry and request extension. if (entry->is_flv()) { if (ext != ".flv") { return ret; } } else if (entry->is_ts()) { if (ext != ".ts") { return ret; } } else if (entry->is_mp3()) { if (ext != ".mp3") { return ret; } } else if (entry->is_aac()) { if (ext != ".aac") { return ret; } } else { return ret; } } // convert to concreate class. SrsHttpMessage* hreq = dynamic_cast<SrsHttpMessage*>(request); srs_assert(hreq); // hijack for entry. SrsRequest* r = hreq->to_request(vhost->arg0()); SrsAutoFree(SrsRequest, r); std::string sid = r->get_stream_url(); // check whether the http remux is enabled, // for example, user disable the http flv then reload. if (sflvs.find(sid) != sflvs.end()) { SrsLiveEntry* s_entry = sflvs[sid]; if (!s_entry->stream->entry->enabled) { // only when the http entry is disabled, check the config whether http flv disable, // for the http flv edge use hijack to trigger the edge ingester, we always mount it // eventhough the origin does not exists the specified stream. if (!_srs_config->get_vhost_http_remux_enabled(r->vhost)) { srs_error("stream is disabled, hijack failed. ret=%d", ret); return ret; } } } SrsSource* s = SrsSource::fetch(r); if (!s) { if ((ret = SrsSource::create(r, server, server, &s)) != ERROR_SUCCESS) { return ret; } } srs_assert(s != NULL); // create http streaming handler. if ((ret = http_mount(s, r)) != ERROR_SUCCESS) { return ret; } // use the handler if exists. if (ph) { if (sflvs.find(sid) != sflvs.end()) { entry = sflvs[sid]; *ph = entry->stream; } } // trigger edge to fetch from origin. bool vhost_is_edge = _srs_config->get_vhost_is_edge(r->vhost); srs_trace("hstrs: source url=%s, is_edge=%d, source_id=%d[%d]", r->get_stream_url().c_str(), vhost_is_edge, s->source_id(), s->source_id()); return ret; }
int SrsLiveStream::do_serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r) { int ret = ERROR_SUCCESS; ISrsStreamEncoder* enc = NULL; srs_assert(entry); if (srs_string_ends_with(entry->pattern, ".flv")) { w->header()->set_content_type("video/x-flv"); #ifdef SRS_PERF_FAST_FLV_ENCODER bool realtime = _srs_config->get_realtime_enabled(req->vhost); if (realtime) { enc = new SrsFlvStreamEncoder(); } else { enc = new SrsFastFlvStreamEncoder(); } #else enc = new SrsFlvStreamEncoder(); #endif } else if (srs_string_ends_with(entry->pattern, ".aac")) { w->header()->set_content_type("audio/x-aac"); enc = new SrsAacStreamEncoder(); } else if (srs_string_ends_with(entry->pattern, ".mp3")) { w->header()->set_content_type("audio/mpeg"); enc = new SrsMp3StreamEncoder(); } else if (srs_string_ends_with(entry->pattern, ".ts")) { w->header()->set_content_type("video/MP2T"); enc = new SrsTsStreamEncoder(); } else { ret = ERROR_HTTP_LIVE_STREAM_EXT; srs_error("http: unsupported pattern %s", entry->pattern.c_str()); return ret; } SrsAutoFree(ISrsStreamEncoder, enc); // create consumer of souce, ignore gop cache, use the audio gop cache. SrsConsumer* consumer = NULL; if ((ret = source->create_consumer(NULL, consumer, true, true, !enc->has_cache())) != ERROR_SUCCESS) { srs_error("http: create consumer failed. ret=%d", ret); return ret; } SrsAutoFree(SrsConsumer, consumer); srs_verbose("http: consumer created success."); SrsPithyPrint* pprint = SrsPithyPrint::create_http_stream(); SrsAutoFree(SrsPithyPrint, pprint); SrsMessageArray msgs(SRS_PERF_MW_MSGS); // the memory writer. SrsStreamWriter writer(w); if ((ret = enc->initialize(&writer, cache)) != ERROR_SUCCESS) { srs_error("http: initialize stream encoder failed. ret=%d", ret); return ret; } // if gop cache enabled for encoder, dump to consumer. if (enc->has_cache()) { if ((ret = enc->dump_cache(consumer, source->jitter())) != ERROR_SUCCESS) { srs_error("http: dump cache to consumer failed. ret=%d", ret); return ret; } } #ifdef SRS_PERF_FAST_FLV_ENCODER SrsFastFlvStreamEncoder* ffe = dynamic_cast<SrsFastFlvStreamEncoder*>(enc); #endif // Use receive thread to accept the close event to avoid FD leak. // @see https://github.com/ossrs/srs/issues/636#issuecomment-298208427 SrsHttpMessage* hr = dynamic_cast<SrsHttpMessage*>(r); SrsResponseOnlyHttpConn* hc = dynamic_cast<SrsResponseOnlyHttpConn*>(hr->connection()); int mw_sleep = _srs_config->get_mw_sleep_ms(req->vhost); SrsHttpRecvThread* trd = new SrsHttpRecvThread(hc); SrsAutoFree(SrsHttpRecvThread, trd); if ((ret = trd->start()) != ERROR_SUCCESS) { srs_error("http: start notify thread failed, ret=%d", ret); return ret; } // TODO: free and erase the disabled entry after all related connections is closed. while (entry->enabled) { pprint->elapse(); // Whether client closed the FD. if ((ret = trd->error_code()) != ERROR_SUCCESS) { return ret; } // get messages from consumer. // each msg in msgs.msgs must be free, for the SrsMessageArray never free them. int count = 0; if ((ret = consumer->dump_packets(&msgs, count)) != ERROR_SUCCESS) { srs_error("http: get messages from consumer failed. ret=%d", ret); return ret; } if (count <= 0) { srs_info("http: sleep %dms for no msg", SRS_CONSTS_RTMP_PULSE_TIMEOUT_US); // directly use sleep, donot use consumer wait. st_usleep(mw_sleep * 1000); // ignore when nothing got. continue; } if (pprint->can_print()) { srs_trace("-> "SRS_CONSTS_LOG_HTTP_STREAM" http: got %d msgs, age=%d, min=%d, mw=%d", count, pprint->age(), SRS_PERF_MW_MIN_MSGS, mw_sleep); } // sendout all messages. #ifdef SRS_PERF_FAST_FLV_ENCODER if (ffe) { ret = ffe->write_tags(msgs.msgs, count); } else { ret = streaming_send_messages(enc, msgs.msgs, count); } #else ret = streaming_send_messages(enc, msgs.msgs, count); #endif // free the messages. for (int i = 0; i < count; i++) { SrsSharedPtrMessage* msg = msgs.msgs[i]; srs_freep(msg); } // check send error code. if (ret != ERROR_SUCCESS) { if (!srs_is_client_gracefully_close(ret)) { srs_error("http: send messages to client failed. ret=%d", ret); } return ret; } } return ret; }