Blame view

trunk/src/app/srs_app_http_conn.cpp 15.5 KB
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
/*
The MIT License (MIT)

Copyright (c) 2013-2014 winlin

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

#include <srs_app_http_conn.hpp>
26
#ifdef SRS_AUTO_HTTP_SERVER
27 28 29 30

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
31 32 33 34
#include <stdlib.h>

#include <sstream>
using namespace std;
35 36 37

#include <srs_kernel_log.hpp>
#include <srs_kernel_error.hpp>
38
#include <srs_app_st_socket.hpp>
39 40
#include <srs_core_autofree.hpp>
#include <srs_app_config.hpp>
41
#include <srs_kernel_flv.hpp>
42
#include <srs_kernel_utility.hpp>
43
#include <srs_kernel_file.hpp>
44
45 46
#define SRS_HTTP_DEFAULT_PAGE "index.html"
47 48 49 50 51 52 53 54 55 56 57 58
SrsHttpRoot::SrsHttpRoot()
{
    // TODO: FIXME: support reload vhosts.
}

SrsHttpRoot::~SrsHttpRoot()
{
}

int SrsHttpRoot::initialize()
{
    int ret = ERROR_SUCCESS;
59 60
    
    bool default_root_exists = false;
61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
    
    // add other virtual path
    SrsConfDirective* root = _srs_config->get_root();
    for (int i = 0; i < (int)root->directives.size(); i++) {
        SrsConfDirective* conf = root->at(i);
        
        if (!conf->is_vhost()) {
            continue;
        }
        
        std::string vhost = conf->arg0();
        if (!_srs_config->get_vhost_http_enabled(vhost)) {
            continue;
        }
        
        std::string mount = _srs_config->get_vhost_http_mount(vhost);
        std::string dir = _srs_config->get_vhost_http_dir(vhost);
        
        handlers.push_back(new SrsHttpVhost(vhost, mount, dir));
80 81 82 83 84 85 86 87 88 89
        
        if (mount == "/") {
            default_root_exists = true;
        }
    }
    
    if (!default_root_exists) {
        // add root
        handlers.push_back(new SrsHttpVhost(
            "__http__", "/", _srs_config->get_http_stream_dir()));
90 91 92 93 94
    }
    
    return ret;
}
95
int SrsHttpRoot::best_match(const char* path, int length, SrsHttpHandlerMatch** ppmatch)
96
{
97 98 99 100 101 102 103 104 105 106
    int ret = ERROR_SUCCESS;
        
    // find the best matched child handler.
    std::vector<SrsHttpHandler*>::iterator it;
    for (it = handlers.begin(); it != handlers.end(); ++it) {
        SrsHttpHandler* h = *it;
        
        // search all child handlers.
        h->best_match(path, length, ppmatch);
    }
107
    
108 109 110 111 112 113 114
    // if already matched by child, return.
    if (*ppmatch) {
        return ret;
    }
    
    // not matched, error.
    return ERROR_HTTP_HANDLER_MATCH_URL;
115 116
}
117
bool SrsHttpRoot::is_handler_valid(SrsHttpMessage* /*req*/, int& status_code, std::string& reason_phrase)
118
{
winlin authored
119 120
    status_code = SRS_CONSTS_HTTP_InternalServerError;
    reason_phrase = SRS_CONSTS_HTTP_InternalServerError_str;
121 122 123 124
    
    return false;
}
125
int SrsHttpRoot::do_process_request(SrsStSocket* /*skt*/, SrsHttpMessage* /*req*/)
126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
{
    int ret = ERROR_SUCCESS;
    return ret;
}

SrsHttpVhost::SrsHttpVhost(std::string vhost, std::string mount, std::string dir)
{
    _vhost = vhost;
    _mount = mount;
    _dir = dir;
}

SrsHttpVhost::~SrsHttpVhost()
{
}

bool SrsHttpVhost::can_handle(const char* path, int length, const char** /*pchild*/)
{
144
    return srs_path_like(_mount.c_str(), path, length);
145 146 147 148
}

bool SrsHttpVhost::is_handler_valid(SrsHttpMessage* req, int& status_code, std::string& reason_phrase) 
{
149
    std::string fullpath = get_request_file(req);
150 151 152 153
    
    if (::access(fullpath.c_str(), F_OK | R_OK) < 0) {
        srs_warn("check file %s does not exists", fullpath.c_str());
        
winlin authored
154 155
        status_code = SRS_CONSTS_HTTP_NotFound;
        reason_phrase = SRS_CONSTS_HTTP_NotFound_str;
156 157 158 159 160 161
        return false;
    }
    
    return true;
}
162
int SrsHttpVhost::do_process_request(SrsStSocket* skt, SrsHttpMessage* req)
163
{
164
    std::string fullpath = get_request_file(req);
165
    
winlin authored
166
    // TODO: FIXME: support mp4, @see https://github.com/winlinvip/simple-rtmp-server/issues/174
167
    if (srs_string_ends_with(fullpath, ".ts")) {
winlin authored
168
        return response_ts_file(skt, req, fullpath);
169
    } else if (srs_string_ends_with(fullpath, ".flv") || srs_string_ends_with(fullpath, ".fhv")) {
170 171 172 173 174 175 176 177 178 179 180
        std::string start = req->query_get("start");
        if (start.empty()) {
            return response_flv_file(skt, req, fullpath);
        }

        int offset = ::atoi(start.c_str());
        if (offset <= 0) {
            return response_flv_file(skt, req, fullpath);
        }
        
        return response_flv_file2(skt, req, fullpath, offset);
winlin authored
181
    }
182 183

    return response_regular_file(skt, req, fullpath);
winlin authored
184 185
}
186
int SrsHttpVhost::response_regular_file(SrsStSocket* skt, SrsHttpMessage* req, string fullpath)
winlin authored
187 188 189
{
    int ret = ERROR_SUCCESS;
190 191 192
    SrsFileReader fs;
    
    if ((ret = fs.open(fullpath)) != ERROR_SUCCESS) {
winlin authored
193 194 195 196
        srs_warn("open file %s failed, ret=%d", fullpath.c_str(), ret);
        return ret;
    }
197
    int64_t length = fs.filesize();
198
    
winlin authored
199 200
    char* buf = new char[length];
    SrsAutoFree(char, buf);
201
    
202
    if ((ret = fs.read(buf, length, NULL)) != ERROR_SUCCESS) {
winlin authored
203
        srs_warn("read file %s failed, ret=%d", fullpath.c_str(), ret);
204
        return ret;
winlin authored
205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225
    }
    
    std::string str;
    str.append(buf, length);
    
    if (srs_string_ends_with(fullpath, ".ts")) {
        return res_mpegts(skt, req, str);
    } else if (srs_string_ends_with(fullpath, ".m3u8")) {
        return res_m3u8(skt, req, str);
    } else if (srs_string_ends_with(fullpath, ".xml")) {
        return res_xml(skt, req, str);
    } else if (srs_string_ends_with(fullpath, ".js")) {
        return res_javascript(skt, req, str);
    } else if (srs_string_ends_with(fullpath, ".json")) {
        return res_json(skt, req, str);
    } else if (srs_string_ends_with(fullpath, ".swf")) {
        return res_swf(skt, req, str);
    } else if (srs_string_ends_with(fullpath, ".css")) {
        return res_css(skt, req, str);
    } else if (srs_string_ends_with(fullpath, ".ico")) {
        return res_ico(skt, req, str);
226
    } else {
winlin authored
227 228
        return res_text(skt, req, str);
    }
229
    
winlin authored
230 231 232
    return ret;
}
233
int SrsHttpVhost::response_flv_file(SrsStSocket* skt, SrsHttpMessage* req, string fullpath)
234 235
{
    int ret = ERROR_SUCCESS;
236 237

    SrsFileReader fs;
238 239
    
    // TODO: FIXME: use more advance cache.
240
    if ((ret = fs.open(fullpath)) != ERROR_SUCCESS) {
241 242 243 244
        srs_warn("open file %s failed, ret=%d", fullpath.c_str(), ret);
        return ret;
    }
245
    int64_t length = fs.filesize();
246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269

    // write http header for ts.
    std::stringstream ss;

    res_status_line(ss)->res_content_type_flv(ss)
        ->res_content_length(ss, (int)length);
        
    if (req->requires_crossdomain()) {
        res_enable_crossdomain(ss);
    }
    
    res_header_eof(ss);
    
    // flush http header to peer
    if ((ret = res_flush(skt, ss)) != ERROR_SUCCESS) {
        return ret;
    }
    
    // write body.
    int64_t left = length;
    char* buf = req->http_ts_send_buffer();
    
    while (left > 0) {
        ssize_t nread = -1;
winlin authored
270
        if ((ret = fs.read(buf, __SRS_HTTP_TS_SEND_BUFFER_SIZE, &nread)) != ERROR_SUCCESS) {
271 272 273 274 275 276 277 278 279 280 281 282
            srs_warn("read file %s failed, ret=%d", fullpath.c_str(), ret);
            break;
        }
        
        left -= nread;
        if ((ret = skt->write(buf, nread, NULL)) != ERROR_SUCCESS) {
            break;
        }
    }
    
    return ret;
}
283
284
int SrsHttpVhost::response_flv_file2(SrsStSocket* skt, SrsHttpMessage* req, string fullpath, int offset)
285 286 287
{
    int ret = ERROR_SUCCESS;
    
288
    SrsFileReader fs;
289 290
    
    // open flv file
291
    if ((ret = fs.open(fullpath)) != ERROR_SUCCESS) {
292 293 294 295 296 297 298 299 300 301
        return ret;
    }
    
    if (offset > fs.filesize()) {
        ret = ERROR_HTTP_FLV_OFFSET_OVERFLOW;
        srs_warn("http flv streaming %s overflow. size=%"PRId64", offset=%d, ret=%d", 
            fullpath.c_str(), fs.filesize(), offset, ret);
        return ret;
    }
    
302
    SrsFlvVodStreamDecoder ffd;
303 304 305 306 307 308 309
    
    // open fast decoder
    if ((ret = ffd.initialize(&fs)) != ERROR_SUCCESS) {
        return ret;
    }
    
    // save header, send later.
310
    char flv_header[13];
311 312
    
    // send flv header
313
    if ((ret = ffd.read_header_ext(flv_header)) != ERROR_SUCCESS) {
314 315 316 317 318 319 320 321 322 323
        return ret;
    }
    
    // save sequence header, send later
    char* sh_data = NULL;
    int sh_size = 0;
    
    if (true) {
        // send sequence header
        int64_t start = 0;
324
        if ((ret = ffd.read_sequence_header_summary(&start, &sh_size)) != ERROR_SUCCESS) {
325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345
            return ret;
        }
        if (sh_size <= 0) {
            ret = ERROR_HTTP_FLV_SEQUENCE_HEADER;
            srs_warn("http flv streaming no sequence header. size=%d, ret=%d", sh_size, ret);
            return ret;
        }
    }
    sh_data = new char[sh_size];
    SrsAutoFree(char, sh_data);
    if ((ret = fs.read(sh_data, sh_size, NULL)) != ERROR_SUCCESS) {
        return ret;
    }

    // seek to data offset
    int64_t left = fs.filesize() - offset;

    // write http header for ts.
    std::stringstream ss;

    res_status_line(ss)->res_content_type_flv(ss)
346
        ->res_content_length(ss, (int)(sizeof(flv_header) + sh_size + left));
347 348 349 350 351 352 353 354 355 356 357 358
        
    if (req->requires_crossdomain()) {
        res_enable_crossdomain(ss);
    }
    
    res_header_eof(ss);
    
    // flush http header to peer
    if ((ret = res_flush(skt, ss)) != ERROR_SUCCESS) {
        return ret;
    }
    
359
    if ((ret = skt->write(flv_header, sizeof(flv_header), NULL)) != ERROR_SUCCESS) {
360 361 362 363 364 365 366 367 368 369 370 371 372 373 374
        return ret;
    }
    if (sh_size > 0 && (ret = skt->write(sh_data, sh_size, NULL)) != ERROR_SUCCESS) {
        return ret;
    }
    
    // write body.
    char* buf = req->http_ts_send_buffer();
    if ((ret = ffd.lseek(offset)) != ERROR_SUCCESS) {
        return ret;
    }
    
    // send data
    while (left > 0) {
        ssize_t nread = -1;
winlin authored
375
        if ((ret = fs.read(buf, __SRS_HTTP_TS_SEND_BUFFER_SIZE, &nread)) != ERROR_SUCCESS) {
376 377 378 379 380 381 382 383 384 385 386
            return ret;
        }
        
        left -= nread;
        if ((ret = skt->write(buf, nread, NULL)) != ERROR_SUCCESS) {
            return ret;
        }
    }
    
    return ret;
}
387
388
int SrsHttpVhost::response_ts_file(SrsStSocket* skt, SrsHttpMessage* req, string fullpath)
winlin authored
389 390 391
{
    int ret = ERROR_SUCCESS;
    
392 393
    SrsFileReader fs;
    
winlin authored
394
    // TODO: FIXME: use more advance cache.
395
    if ((ret = fs.open(fullpath)) != ERROR_SUCCESS) {
winlin authored
396 397 398 399
        srs_warn("open file %s failed, ret=%d", fullpath.c_str(), ret);
        return ret;
    }
400
    int64_t length = fs.filesize();
winlin authored
401 402 403 404 405 406

    // write http header for ts.
    std::stringstream ss;

    res_status_line(ss)->res_content_type_mpegts(ss)
        ->res_content_length(ss, (int)length);
407
        
winlin authored
408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424
    if (req->requires_crossdomain()) {
        res_enable_crossdomain(ss);
    }
    
    res_header_eof(ss);
    
    // flush http header to peer
    if ((ret = res_flush(skt, ss)) != ERROR_SUCCESS) {
        return ret;
    }
    
    // write body.
    int64_t left = length;
    char* buf = req->http_ts_send_buffer();
    
    while (left > 0) {
        ssize_t nread = -1;
winlin authored
425
        if ((ret = fs.read(buf, __SRS_HTTP_TS_SEND_BUFFER_SIZE, &nread)) != ERROR_SUCCESS) {
426
            srs_warn("read file %s failed, ret=%d", fullpath.c_str(), ret);
427
            break;
428 429
        }
        
winlin authored
430 431 432
        left -= nread;
        if ((ret = skt->write(buf, nread, NULL)) != ERROR_SUCCESS) {
            break;
433
        }
434 435 436 437 438
    }
    
    return ret;
}
439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463
string SrsHttpVhost::get_request_file(SrsHttpMessage* req)
{
    std::string fullpath = _dir + "/"; 
    
    // if root, directly use the matched url.
    if (_mount == "/") {
        // add the dir
        fullpath += req->match()->matched_url;
        // if file speicified, add the file.
        if (!req->match()->unmatched_url.empty()) {
            fullpath += "/" + req->match()->unmatched_url;
        }
    } else {
        // virtual path, ignore the virutal path.
        fullpath += req->match()->unmatched_url;
    }
    
    // add default pages.
    if (srs_string_ends_with(fullpath, "/")) {
        fullpath += SRS_HTTP_DEFAULT_PAGE;
    }
    
    return fullpath;
}
464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491
string SrsHttpVhost::vhost()
{
    return _vhost;
}

string SrsHttpVhost::mount()
{
    return _mount;
}

string SrsHttpVhost::dir()
{
    return _dir;
}

SrsHttpConn::SrsHttpConn(SrsServer* srs_server, st_netfd_t client_stfd, SrsHttpHandler* _handler) 
    : SrsConnection(srs_server, client_stfd)
{
    parser = new SrsHttpParser();
    handler = _handler;
    requires_crossdomain = false;
}

SrsHttpConn::~SrsHttpConn()
{
    srs_freep(parser);
}
492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508
void SrsHttpConn::kbps_resample()
{
    // TODO: FIXME: implements it
}

int64_t SrsHttpConn::get_send_bytes_delta()
{
    // TODO: FIXME: implements it
    return 0;
}

int64_t SrsHttpConn::get_recv_bytes_delta()
{
    // TODO: FIXME: implements it
    return 0;
}
509 510 511 512
int SrsHttpConn::do_cycle()
{
    int ret = ERROR_SUCCESS;
    
winlin authored
513
    srs_trace("HTTP client ip=%s", ip.c_str());
514 515 516 517 518 519 520 521
    
    // initialize parser
    if ((ret = parser->initialize(HTTP_REQUEST)) != ERROR_SUCCESS) {
        srs_error("http initialize http parser failed. ret=%d", ret);
        return ret;
    }
    
    // underlayer socket
522
    SrsStSocket skt(stfd);
523 524 525 526 527 528 529 530 531 532 533 534 535 536 537
    
    // 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.
538
        SrsAutoFree(SrsHttpMessage, req);
539 540 541 542 543 544 545 546 547 548
        
        // ok, handle http request.
        if ((ret = process_request(&skt, req)) != ERROR_SUCCESS) {
            return ret;
        }
    }
        
    return ret;
}
549
int SrsHttpConn::process_request(SrsStSocket* skt, SrsHttpMessage* req) 
550 551 552 553 554 555 556 557
{
    int ret = ERROR_SUCCESS;

    // parse uri to schema/server:port/path?query
    if ((ret = req->parse_uri()) != ERROR_SUCCESS) {
        return ret;
    }
    
winlin authored
558 559
    srs_trace("HTTP %s %s, content-length=%"PRId64"", 
        req->method_str().c_str(), req->url().c_str(), req->content_length());
560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592
    
    // TODO: maybe need to parse the url.
    std::string url = req->path();
    
    SrsHttpHandlerMatch* p = NULL;
    if ((ret = handler->best_match(url.data(), url.length(), &p)) != ERROR_SUCCESS) {
        srs_warn("failed to find the best match handler for url. ret=%d", ret);
        return ret;
    }
    
    // if success, p and pstart should be valid.
    srs_assert(p);
    srs_assert(p->handler);
    srs_assert(p->matched_url.length() <= url.length());
    srs_info("best match handler, matched_url=%s", p->matched_url.c_str());
    
    req->set_match(p);
    req->set_requires_crossdomain(requires_crossdomain);
    
    // use handler to process request.
    if ((ret = p->handler->process_request(skt, req)) != ERROR_SUCCESS) {
        srs_warn("handler failed to process http request. ret=%d", ret);
        return ret;
    }
    
    if (req->requires_crossdomain()) {
        requires_crossdomain = true;
    }
    
    return ret;
}

#endif
593