mirror of
https://github.com/ossrs/srs.git
synced 2025-02-13 03:41:55 +00:00
For #1657, support HTTPS client, for http-callback. 4.0.45
This commit is contained in:
parent
08e19406ef
commit
d552a1a5fb
13 changed files with 343 additions and 31 deletions
|
@ -155,6 +155,7 @@ For previous versions, please read:
|
|||
|
||||
## V4 changes
|
||||
|
||||
* v4.0, 2020-11-03, For [#1657][bug #1657-1], support HTTPS client, for http-callback. 4.0.45
|
||||
* v4.0, 2020-10-31, Support gdb/srs.py to stat coroutines. 4.0.44
|
||||
* v4.0, 2020-09-19, RTC: Extract resource manager. Use any UDP packet to keep alive. 4.0.43
|
||||
* v4.0, 2020-09-09, RTC: Refine NACK RTT and efficiency. 4.0.42
|
||||
|
@ -1781,6 +1782,7 @@ Winlin
|
|||
[bug #1636]: https://github.com/ossrs/srs/issues/1636
|
||||
[bug #1657]: https://github.com/ossrs/srs/issues/1657
|
||||
[bug #1830]: https://github.com/ossrs/srs/issues/1830
|
||||
[bug #1657-1]: https://github.com/ossrs/srs/issues/1657#issuecomment-720889906
|
||||
[bug #zzzzzzzzzzzzz]: https://github.com/ossrs/srs/issues/zzzzzzzzzzzzz
|
||||
|
||||
[exo #828]: https://github.com/google/ExoPlayer/pull/828
|
||||
|
|
|
@ -105,6 +105,12 @@ else
|
|||
srs_undefine_macro "SRS_GB28181" $SRS_AUTO_HEADERS_H
|
||||
fi
|
||||
|
||||
if [ $SRS_HTTPS = YES ]; then
|
||||
srs_define_macro "SRS_HTTPS" $SRS_AUTO_HEADERS_H
|
||||
else
|
||||
srs_undefine_macro "SRS_HTTPS" $SRS_AUTO_HEADERS_H
|
||||
fi
|
||||
|
||||
if [ $SRS_MEM_WATCH = YES ]; then
|
||||
srs_define_macro "SRS_MEM_WATCH" $SRS_AUTO_HEADERS_H
|
||||
else
|
||||
|
|
|
@ -35,6 +35,7 @@ SRS_GPROF=NO # Performance test: gprof
|
|||
SRS_STREAM_CASTER=YES
|
||||
SRS_INGEST=YES
|
||||
SRS_SSL=YES
|
||||
SRS_HTTPS=NO
|
||||
SRS_STAT=YES
|
||||
SRS_TRANSCODE=YES
|
||||
SRS_HTTP_CALLBACK=YES
|
||||
|
@ -145,6 +146,7 @@ Features:
|
|||
-h, --help Print this message and exit 0.
|
||||
|
||||
--ssl=on|off Whether build the rtmp complex handshake, requires openssl-devel installed.
|
||||
--https=on|off Whether enable HTTPS client and server. Default: off
|
||||
--hds=on|off Whether build the hds streaming, mux RTMP to F4M/F4V files.
|
||||
--stream-caster=on|off Whether build the stream caster to serve other stream over other protocol.
|
||||
--stat=on|off Whether build the the data statistic, for http api.
|
||||
|
@ -272,6 +274,7 @@ function parse_user_option() {
|
|||
|
||||
--with-ssl) SRS_SSL=YES ;;
|
||||
--ssl) if [[ $value == off ]]; then SRS_SSL=NO; else SRS_SSL=YES; fi ;;
|
||||
--https) if [[ $value == off ]]; then SRS_HTTPS=NO; else SRS_HTTPS=YES; fi ;;
|
||||
|
||||
--with-hds) SRS_HDS=YES ;;
|
||||
--without-hds) SRS_HDS=NO ;;
|
||||
|
@ -526,6 +529,7 @@ function regenerate_options() {
|
|||
if [ $SRS_HDS = YES ]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --hds=on"; else SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --hds=off"; fi
|
||||
if [ $SRS_DVR = YES ]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --dvr=on"; else SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --dvr=off"; fi
|
||||
if [ $SRS_SSL = YES ]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --ssl=on"; else SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --ssl=off"; fi
|
||||
if [ $SRS_HTTPS = YES ]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --https=on"; else SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --https=off"; fi
|
||||
if [ $SRS_USE_SYS_SSL = YES ]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --sys-ssl=on"; else SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --sys-ssl=off"; fi
|
||||
if [ $SRS_TRANSCODE = YES ]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --transcode=on"; else SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --transcode=off"; fi
|
||||
if [ $SRS_INGEST = YES ]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --ingest=on"; else SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --ingest=off"; fi
|
||||
|
|
|
@ -1090,6 +1090,8 @@ vhost hooks.callback.srs.com {
|
|||
# 0
|
||||
# support multiple api hooks, format:
|
||||
# on_connect http://xxx/api0 http://xxx/api1 http://xxx/apiN
|
||||
# @remark For SRS4, the HTTPS url is supported, for example:
|
||||
# on_connect https://xxx/api0 https://xxx/api1 https://xxx/apiN
|
||||
on_connect http://127.0.0.1:8085/api/v1/clients http://localhost:8085/api/v1/clients;
|
||||
# when client close/disconnect to vhost/app/stream, call the hook,
|
||||
# the request in the POST data string is a object encode by json:
|
||||
|
@ -1104,6 +1106,8 @@ vhost hooks.callback.srs.com {
|
|||
# 0
|
||||
# support multiple api hooks, format:
|
||||
# on_close http://xxx/api0 http://xxx/api1 http://xxx/apiN
|
||||
# @remark For SRS4, the HTTPS url is supported, for example:
|
||||
# on_close https://xxx/api0 https://xxx/api1 https://xxx/apiN
|
||||
on_close http://127.0.0.1:8085/api/v1/clients http://localhost:8085/api/v1/clients;
|
||||
# when client(encoder) publish to vhost/app/stream, call the hook,
|
||||
# the request in the POST data string is a object encode by json:
|
||||
|
@ -1118,6 +1122,8 @@ vhost hooks.callback.srs.com {
|
|||
# 0
|
||||
# support multiple api hooks, format:
|
||||
# on_publish http://xxx/api0 http://xxx/api1 http://xxx/apiN
|
||||
# @remark For SRS4, the HTTPS url is supported, for example:
|
||||
# on_publish https://xxx/api0 https://xxx/api1 https://xxx/apiN
|
||||
on_publish http://127.0.0.1:8085/api/v1/streams http://localhost:8085/api/v1/streams;
|
||||
# when client(encoder) stop publish to vhost/app/stream, call the hook,
|
||||
# the request in the POST data string is a object encode by json:
|
||||
|
@ -1132,6 +1138,8 @@ vhost hooks.callback.srs.com {
|
|||
# 0
|
||||
# support multiple api hooks, format:
|
||||
# on_unpublish http://xxx/api0 http://xxx/api1 http://xxx/apiN
|
||||
# @remark For SRS4, the HTTPS url is supported, for example:
|
||||
# on_unpublish https://xxx/api0 https://xxx/api1 https://xxx/apiN
|
||||
on_unpublish http://127.0.0.1:8085/api/v1/streams http://localhost:8085/api/v1/streams;
|
||||
# when client start to play vhost/app/stream, call the hook,
|
||||
# the request in the POST data string is a object encode by json:
|
||||
|
@ -1147,6 +1155,8 @@ vhost hooks.callback.srs.com {
|
|||
# 0
|
||||
# support multiple api hooks, format:
|
||||
# on_play http://xxx/api0 http://xxx/api1 http://xxx/apiN
|
||||
# @remark For SRS4, the HTTPS url is supported, for example:
|
||||
# on_play https://xxx/api0 https://xxx/api1 https://xxx/apiN
|
||||
on_play http://127.0.0.1:8085/api/v1/sessions http://localhost:8085/api/v1/sessions;
|
||||
# when client stop to play vhost/app/stream, call the hook,
|
||||
# the request in the POST data string is a object encode by json:
|
||||
|
@ -1161,6 +1171,8 @@ vhost hooks.callback.srs.com {
|
|||
# 0
|
||||
# support multiple api hooks, format:
|
||||
# on_stop http://xxx/api0 http://xxx/api1 http://xxx/apiN
|
||||
# @remark For SRS4, the HTTPS url is supported, for example:
|
||||
# on_stop https://xxx/api0 https://xxx/api1 https://xxx/apiN
|
||||
on_stop http://127.0.0.1:8085/api/v1/sessions http://localhost:8085/api/v1/sessions;
|
||||
# when srs reap a dvr file, call the hook,
|
||||
# the request in the POST data string is a object encode by json:
|
||||
|
|
5
trunk/configure
vendored
5
trunk/configure
vendored
|
@ -699,6 +699,11 @@ if [ $SRS_RTC = YES ]; then
|
|||
else
|
||||
echo -e "${GREEN}Warning: RTC is disabled.${BLACK}"
|
||||
fi
|
||||
if [ $SRS_HTTPS = YES ]; then
|
||||
echo -e "${YELLOW}Experiment: HTTPS is enabled. https://github.com/ossrs/srs/issues/1657${BLACK}"
|
||||
else
|
||||
echo -e "${GREEN}Warning: HTTPS is disabled.${BLACK}"
|
||||
fi
|
||||
if [ $SRS_DVR = YES ]; then
|
||||
echo -e "${GREEN}DVR is enabled.${BLACK}"
|
||||
else
|
||||
|
|
|
@ -87,7 +87,7 @@ srs_error_t SrsHttpHeartbeat::do_heartbeat()
|
|||
}
|
||||
|
||||
SrsHttpClient http;
|
||||
if ((err = http.initialize(uri.get_host(), uri.get_port())) != srs_success) {
|
||||
if ((err = http.initialize(uri.get_schema(), uri.get_host(), uri.get_port())) != srs_success) {
|
||||
return srs_error_wrap(err, "init uri=%s", uri.get_url().c_str());
|
||||
}
|
||||
|
||||
|
|
|
@ -371,7 +371,7 @@ srs_error_t SrsHttpHooks::on_hls_notify(SrsContextId c, std::string url, SrsRequ
|
|||
}
|
||||
|
||||
SrsHttpClient http;
|
||||
if ((err = http.initialize(uri.get_host(), uri.get_port(), SRS_HLS_NOTIFY_TIMEOUT)) != srs_success) {
|
||||
if ((err = http.initialize(uri.get_schema(), uri.get_host(), uri.get_port(), SRS_HLS_NOTIFY_TIMEOUT)) != srs_success) {
|
||||
return srs_error_wrap(err, "http: init client for %s", url.c_str());
|
||||
}
|
||||
|
||||
|
@ -478,7 +478,7 @@ srs_error_t SrsHttpHooks::do_post(SrsHttpClient* hc, std::string url, std::strin
|
|||
return srs_error_wrap(err, "http: post failed. url=%s", url.c_str());
|
||||
}
|
||||
|
||||
if ((err = hc->initialize(uri.get_host(), uri.get_port())) != srs_success) {
|
||||
if ((err = hc->initialize(uri.get_schema(), uri.get_host(), uri.get_port())) != srs_success) {
|
||||
return srs_error_wrap(err, "http: init client");
|
||||
}
|
||||
|
||||
|
|
|
@ -40,20 +40,8 @@ using namespace std;
|
|||
#include <openssl/ssl.h>
|
||||
#include <openssl/err.h>
|
||||
|
||||
// The return value of verify_callback controls the strategy of the further verification process. If verify_callback
|
||||
// returns 0, the verification process is immediately stopped with "verification failed" state. If SSL_VERIFY_PEER is
|
||||
// set, a verification failure alert is sent to the peer and the TLS/SSL handshake is terminated. If verify_callback
|
||||
// returns 1, the verification process is continued. If verify_callback always returns 1, the TLS/SSL handshake will
|
||||
// not be terminated with respect to verification failures and the connection will be established. The calling process
|
||||
// can however retrieve the error code of the last verification error using SSL_get_verify_result(3) or by maintaining
|
||||
// its own error storage managed by verify_callback.
|
||||
// @see https://www.openssl.org/docs/man1.0.2/man3/SSL_CTX_set_verify.html
|
||||
int srs_verify_callback(int preverify_ok, X509_STORE_CTX *ctx)
|
||||
{
|
||||
// Always OK, we don't check the certificate of client,
|
||||
// because we allow client self-sign certificate.
|
||||
return 1;
|
||||
}
|
||||
// Defined in HTTP/HTTPS client.
|
||||
extern int srs_verify_callback(int preverify_ok, X509_STORE_CTX *ctx);
|
||||
|
||||
// Print the information of SSL, DTLS alert as such.
|
||||
void ssl_on_info(const SSL* dtls, int where, int ret)
|
||||
|
@ -213,6 +201,11 @@ srs_error_t SrsDtlsCertificate::initialize()
|
|||
// @see https://www.openssl.org/docs/man1.1.0/man3/OpenSSL_add_ssl_algorithms.html
|
||||
// @see https://web.archive.org/web/20150806185102/http://sctp.fh-muenster.de:80/dtls/dtls_udp_echo.c
|
||||
OpenSSL_add_ssl_algorithms();
|
||||
#else
|
||||
// As of version 1.1.0 OpenSSL will automatically allocate all resources that it needs so no explicit
|
||||
// initialisation is required. Similarly it will also automatically deinitialise as required.
|
||||
// @see https://www.openssl.org/docs/man1.1.0/man3/OPENSSL_init_ssl.html
|
||||
// OPENSSL_init_ssl();
|
||||
#endif
|
||||
|
||||
// Initialize SRTP first.
|
||||
|
@ -456,6 +449,7 @@ srs_error_t SrsDtlsImpl::do_on_dtls(char* data, int nb_data)
|
|||
srs_error_t err = srs_success;
|
||||
|
||||
int r0 = 0;
|
||||
// TODO: FIXME: Why reset it before writing?
|
||||
if ((r0 = BIO_reset(bio_in)) != 1) {
|
||||
return srs_error_new(ERROR_OpenSslBIOReset, "BIO_reset r0=%d", r0);
|
||||
}
|
||||
|
|
|
@ -24,6 +24,6 @@
|
|||
#ifndef SRS_CORE_VERSION4_HPP
|
||||
#define SRS_CORE_VERSION4_HPP
|
||||
|
||||
#define SRS_VERSION4_REVISION 44
|
||||
#define SRS_VERSION4_REVISION 45
|
||||
|
||||
#endif
|
||||
|
|
|
@ -324,6 +324,10 @@
|
|||
#define ERROR_HTTP_302_INVALID 4038
|
||||
#define ERROR_BASE64_DECODE 4039
|
||||
#define ERROR_HTTP_STREAM_EOF 4040
|
||||
#define ERROR_HTTPS_NOT_SUPPORTED 4041
|
||||
#define ERROR_HTTPS_HANDSHAKE 4042
|
||||
#define ERROR_HTTPS_READ 4043
|
||||
#define ERROR_HTTPS_WRITE 4044
|
||||
|
||||
///////////////////////////////////////////////////////
|
||||
// RTC protocol error.
|
||||
|
|
|
@ -370,7 +370,7 @@ int SrsIngestHlsInput::parseM3u8(SrsHttpUri* url, double& td, double& duration)
|
|||
SrsHttpClient client;
|
||||
srs_trace("parse input hls %s", url->get_url().c_str());
|
||||
|
||||
if ((err = client.initialize(url->get_host(), url->get_port())) != srs_success) {
|
||||
if ((err = client.initialize(url->get_schema(), url->get_host(), url->get_port())) != srs_success) {
|
||||
// TODO: FIXME: Use error
|
||||
ret = srs_error_code(err);
|
||||
srs_freep(err);
|
||||
|
@ -609,7 +609,7 @@ int SrsIngestHlsInput::SrsTsPiece::fetch(string m3u8)
|
|||
}
|
||||
|
||||
// initialize the fresh http client.
|
||||
if ((ret = client.initialize(uri.get_host(), uri.get_port()) != ERROR_SUCCESS)) {
|
||||
if ((ret = client.initialize(uri.get_schema(), uri.get_host(), uri.get_port()) != ERROR_SUCCESS)) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
|
@ -35,9 +35,225 @@ using namespace std;
|
|||
#include <srs_core_autofree.hpp>
|
||||
#include <srs_service_http_conn.hpp>
|
||||
|
||||
// The return value of verify_callback controls the strategy of the further verification process. If verify_callback
|
||||
// returns 0, the verification process is immediately stopped with "verification failed" state. If SSL_VERIFY_PEER is
|
||||
// set, a verification failure alert is sent to the peer and the TLS/SSL handshake is terminated. If verify_callback
|
||||
// returns 1, the verification process is continued. If verify_callback always returns 1, the TLS/SSL handshake will
|
||||
// not be terminated with respect to verification failures and the connection will be established. The calling process
|
||||
// can however retrieve the error code of the last verification error using SSL_get_verify_result(3) or by maintaining
|
||||
// its own error storage managed by verify_callback.
|
||||
// @see https://www.openssl.org/docs/man1.0.2/man3/SSL_CTX_set_verify.html
|
||||
int srs_verify_callback(int preverify_ok, X509_STORE_CTX *ctx)
|
||||
{
|
||||
// Always OK, we don't check the certificate of client,
|
||||
// because we allow client self-sign certificate.
|
||||
return 1;
|
||||
}
|
||||
|
||||
SrsSslClient::SrsSslClient(SrsTcpClient* tcp)
|
||||
{
|
||||
transport = tcp;
|
||||
ssl_ctx = NULL;
|
||||
ssl = NULL;
|
||||
}
|
||||
|
||||
SrsSslClient::~SrsSslClient()
|
||||
{
|
||||
if (ssl_ctx) {
|
||||
SSL_CTX_free(ssl_ctx);
|
||||
ssl_ctx = NULL;
|
||||
}
|
||||
|
||||
if (ssl) {
|
||||
// this function will free bio_in and bio_out
|
||||
SSL_free(ssl);
|
||||
ssl = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
srs_error_t SrsSslClient::handshake()
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
// For HTTPS, try to connect over security transport.
|
||||
SSL_CTX* ssl_ctx = SSL_CTX_new(TLS_method());
|
||||
SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, srs_verify_callback);
|
||||
srs_assert(SSL_CTX_set_cipher_list(ssl_ctx, "ALL") == 1);
|
||||
|
||||
// TODO: Setup callback, see SSL_set_ex_data and SSL_set_info_callback
|
||||
if ((ssl = SSL_new(ssl_ctx)) == NULL) {
|
||||
return srs_error_new(ERROR_HTTPS_HANDSHAKE, "SSL_new ssl");
|
||||
}
|
||||
|
||||
if ((bio_in = BIO_new(BIO_s_mem())) == NULL) {
|
||||
return srs_error_new(ERROR_HTTPS_HANDSHAKE, "BIO_new in");
|
||||
}
|
||||
|
||||
if ((bio_out = BIO_new(BIO_s_mem())) == NULL) {
|
||||
BIO_free(bio_in);
|
||||
return srs_error_new(ERROR_HTTPS_HANDSHAKE, "BIO_new out");
|
||||
}
|
||||
|
||||
SSL_set_bio(ssl, bio_in, bio_out);
|
||||
|
||||
// SSL setup active, as client role.
|
||||
SSL_set_connect_state(ssl);
|
||||
SSL_set_mode(ssl, SSL_MODE_ENABLE_PARTIAL_WRITE);
|
||||
|
||||
// Send ClientHello.
|
||||
int r0 = SSL_do_handshake(ssl); int r1 = SSL_get_error(ssl, r0);
|
||||
if (r0 != -1 || r1 != SSL_ERROR_WANT_READ) {
|
||||
return srs_error_new(ERROR_HTTPS_HANDSHAKE, "handshake r0=%d, r1=%d", r0, r1);
|
||||
}
|
||||
|
||||
uint8_t* data = NULL;
|
||||
int size = BIO_get_mem_data(bio_out, &data);
|
||||
if (!data || size <= 0) {
|
||||
return srs_error_new(ERROR_HTTPS_HANDSHAKE, "handshake data=%p, size=%d", data, size);
|
||||
}
|
||||
if ((err = transport->write(data, size, NULL)) != srs_success) {
|
||||
return srs_error_wrap(err, "handshake: write data=%p, size=%d", data, size);
|
||||
}
|
||||
if ((r0 = BIO_reset(bio_out)) != 1) {
|
||||
return srs_error_new(ERROR_HTTPS_HANDSHAKE, "BIO_reset r0=%d", r0);
|
||||
}
|
||||
|
||||
srs_trace("https: ClientHello done");
|
||||
|
||||
// Receive ServerHello, Certificate, Server Key Exchange, Server Hello Done
|
||||
while (true) {
|
||||
char buf[512]; ssize_t nn = 0;
|
||||
if ((err = transport->read(buf, sizeof(buf), &nn)) != srs_success) {
|
||||
return srs_error_wrap(err, "handshake: read");
|
||||
}
|
||||
|
||||
if ((r0 = BIO_write(bio_in, buf, nn)) <= 0) {
|
||||
// TODO: 0 or -1 maybe block, use BIO_should_retry to check.
|
||||
return srs_error_new(ERROR_HTTPS_HANDSHAKE, "BIO_write r0=%d, data=%p, size=%d", r0, buf, nn);
|
||||
}
|
||||
|
||||
if ((r0 = SSL_do_handshake(ssl)) != -1 || (r1 = SSL_get_error(ssl, r0)) != SSL_ERROR_WANT_READ) {
|
||||
return srs_error_new(ERROR_HTTPS_HANDSHAKE, "handshake r0=%d, r1=%d", r0, r1);
|
||||
}
|
||||
|
||||
if ((size = BIO_get_mem_data(bio_out, &data)) > 0) {
|
||||
// OK, reset it for the next write.
|
||||
if ((r0 = BIO_reset(bio_in)) != 1) {
|
||||
return srs_error_new(ERROR_HTTPS_HANDSHAKE, "BIO_reset r0=%d", r0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
srs_trace("https: ServerHello done");
|
||||
|
||||
// Send Client Key Exchange, Change Cipher Spec, Encrypted Handshake Message
|
||||
if ((err = transport->write(data, size, NULL)) != srs_success) {
|
||||
return srs_error_wrap(err, "handshake: write data=%p, size=%d", data, size);
|
||||
}
|
||||
if ((r0 = BIO_reset(bio_out)) != 1) {
|
||||
return srs_error_new(ERROR_HTTPS_HANDSHAKE, "BIO_reset r0=%d", r0);
|
||||
}
|
||||
|
||||
srs_trace("https: Client done");
|
||||
|
||||
// Receive New Session Ticket, Change Cipher Spec, Encrypted Handshake Message
|
||||
while (true) {
|
||||
char buf[128];
|
||||
ssize_t nn = 0;
|
||||
if ((err = transport->read(buf, sizeof(buf), &nn)) != srs_success) {
|
||||
return srs_error_wrap(err, "handshake: read");
|
||||
}
|
||||
|
||||
if ((r0 = BIO_write(bio_in, buf, nn)) <= 0) {
|
||||
// TODO: 0 or -1 maybe block, use BIO_should_retry to check.
|
||||
return srs_error_new(ERROR_HTTPS_HANDSHAKE, "BIO_write r0=%d, data=%p, size=%d", r0, buf, nn);
|
||||
}
|
||||
|
||||
r0 = SSL_do_handshake(ssl); r1 = SSL_get_error(ssl, r0);
|
||||
if (r0 == 1 && r1 == SSL_ERROR_NONE) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (r0 != -1 || r1 != SSL_ERROR_WANT_READ) {
|
||||
return srs_error_new(ERROR_HTTPS_HANDSHAKE, "handshake r0=%d, r1=%d", r0, r1);
|
||||
}
|
||||
}
|
||||
|
||||
srs_trace("https: Server done");
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
srs_error_t SrsSslClient::read(void* plaintext, size_t nn_plaintext, ssize_t* nread)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
// TODO: Can we avoid copy?
|
||||
int nn_cipher = nn_plaintext;
|
||||
char* cipher = new char[nn_cipher];
|
||||
SrsAutoFreeA(char, cipher);
|
||||
|
||||
ssize_t nn = 0;
|
||||
// Read the cipher from SSL.
|
||||
if ((err = transport->read(cipher, nn_cipher, &nn)) != srs_success) {
|
||||
return srs_error_wrap(err, "https: read");
|
||||
}
|
||||
|
||||
int r0 = BIO_write(bio_in, cipher, nn);
|
||||
if (r0 <= 0) {
|
||||
// TODO: 0 or -1 maybe block, use BIO_should_retry to check.
|
||||
return srs_error_new(ERROR_HTTPS_READ, "BIO_write r0=%d, cipher=%p, size=%d", r0, cipher, nn);
|
||||
}
|
||||
|
||||
r0 = SSL_read(ssl, plaintext, nn);
|
||||
if (r0 <= 0) {
|
||||
return srs_error_new(ERROR_HTTPS_READ, "SSL_read r0=%d, cipher=%p, size=%d", r0, cipher, nn);
|
||||
}
|
||||
|
||||
srs_assert(r0 <= nn_plaintext);
|
||||
if (nread) {
|
||||
*nread = r0;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
srs_error_t SrsSslClient::write(void* plaintext, size_t nn_plaintext, ssize_t* nwrite)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
for (char* p = (char*)plaintext; p < (char*)plaintext + nn_plaintext;) {
|
||||
int left = (int)nn_plaintext - (p - (char*)plaintext);
|
||||
int r0 = SSL_write(ssl, (const void*)p, left);
|
||||
int r1 = SSL_get_error(ssl, r0);
|
||||
if (r0 <= 0) {
|
||||
return srs_error_new(ERROR_HTTPS_WRITE, "https: write data=%p, size=%d, r0=%d, r1=%d", p, left, r0, r1);
|
||||
}
|
||||
|
||||
// Move p to the next writing position.
|
||||
p += r0;
|
||||
if (nwrite) {
|
||||
*nwrite += (ssize_t)r0;
|
||||
}
|
||||
|
||||
uint8_t* data = NULL;
|
||||
int size = BIO_get_mem_data(bio_out, &data);
|
||||
if ((err = transport->write(data, size, NULL)) != srs_success) {
|
||||
return srs_error_wrap(err, "https: write data=%p, size=%d", data, size);
|
||||
}
|
||||
if ((r0 = BIO_reset(bio_out)) != 1) {
|
||||
return srs_error_new(ERROR_HTTPS_WRITE, "BIO_reset r0=%d", r0);
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
SrsHttpClient::SrsHttpClient()
|
||||
{
|
||||
transport = NULL;
|
||||
ssl_transport = NULL;
|
||||
clk = new SrsWallClock();
|
||||
kbps = new SrsKbps(clk);
|
||||
parser = NULL;
|
||||
|
@ -54,7 +270,7 @@ SrsHttpClient::~SrsHttpClient()
|
|||
srs_freep(parser);
|
||||
}
|
||||
|
||||
srs_error_t SrsHttpClient::initialize(string h, int p, srs_utime_t tm)
|
||||
srs_error_t SrsHttpClient::initialize(string schema, string h, int p, srs_utime_t tm)
|
||||
{
|
||||
srs_error_t err = srs_success;
|
||||
|
||||
|
@ -66,6 +282,7 @@ srs_error_t SrsHttpClient::initialize(string h, int p, srs_utime_t tm)
|
|||
}
|
||||
|
||||
// Always disconnect the transport.
|
||||
schema_ = schema;
|
||||
host = h;
|
||||
port = p;
|
||||
recv_timeout = timeout = tm;
|
||||
|
@ -116,16 +333,16 @@ srs_error_t SrsHttpClient::post(string path, string req, ISrsHttpMessage** ppmsg
|
|||
ss << key << ": " << value << SRS_HTTP_CRLF;
|
||||
}
|
||||
ss << SRS_HTTP_CRLF << req;
|
||||
|
||||
|
||||
std::string data = ss.str();
|
||||
if ((err = transport->write((void*)data.c_str(), data.length(), NULL)) != srs_success) {
|
||||
if ((err = writer()->write((void*)data.c_str(), data.length(), NULL)) != srs_success) {
|
||||
// Disconnect the transport when channel error, reconnect for next operation.
|
||||
disconnect();
|
||||
return srs_error_wrap(err, "http: write");
|
||||
}
|
||||
|
||||
ISrsHttpMessage* msg = NULL;
|
||||
if ((err = parser->parse_message(transport, &msg)) != srs_success) {
|
||||
if ((err = parser->parse_message(reader(), &msg)) != srs_success) {
|
||||
return srs_error_wrap(err, "http: parse response");
|
||||
}
|
||||
srs_assert(msg);
|
||||
|
@ -164,14 +381,14 @@ srs_error_t SrsHttpClient::get(string path, string req, ISrsHttpMessage** ppmsg)
|
|||
ss << SRS_HTTP_CRLF << req;
|
||||
|
||||
std::string data = ss.str();
|
||||
if ((err = transport->write((void*)data.c_str(), data.length(), NULL)) != srs_success) {
|
||||
if ((err = writer()->write((void*)data.c_str(), data.length(), NULL)) != srs_success) {
|
||||
// Disconnect the transport when channel error, reconnect for next operation.
|
||||
disconnect();
|
||||
return srs_error_wrap(err, "http: write");
|
||||
}
|
||||
|
||||
ISrsHttpMessage* msg = NULL;
|
||||
if ((err = parser->parse_message(transport, &msg)) != srs_success) {
|
||||
if ((err = parser->parse_message(reader(), &msg)) != srs_success) {
|
||||
return srs_error_wrap(err, "http: parse response");
|
||||
}
|
||||
srs_assert(msg);
|
||||
|
@ -207,6 +424,7 @@ void SrsHttpClient::kbps_sample(const char* label, int64_t age)
|
|||
void SrsHttpClient::disconnect()
|
||||
{
|
||||
kbps->set_io(NULL, NULL);
|
||||
srs_freep(ssl_transport);
|
||||
srs_freep(transport);
|
||||
}
|
||||
|
||||
|
@ -222,8 +440,8 @@ srs_error_t SrsHttpClient::connect()
|
|||
transport = new SrsTcpClient(host, port, timeout);
|
||||
if ((err = transport->connect()) != srs_success) {
|
||||
disconnect();
|
||||
return srs_error_wrap(err, "http: tcp connect %s:%d to=%dms, rto=%dms",
|
||||
host.c_str(), port, srsu2msi(timeout), srsu2msi(recv_timeout));
|
||||
return srs_error_wrap(err, "http: tcp connect %s %s:%d to=%dms, rto=%dms",
|
||||
schema_.c_str(), host.c_str(), port, srsu2msi(timeout), srsu2msi(recv_timeout));
|
||||
}
|
||||
|
||||
// Set the recv/send timeout in srs_utime_t.
|
||||
|
@ -231,7 +449,40 @@ srs_error_t SrsHttpClient::connect()
|
|||
transport->set_send_timeout(timeout);
|
||||
|
||||
kbps->set_io(transport, transport);
|
||||
|
||||
|
||||
if (schema_ != "https") {
|
||||
return err;
|
||||
}
|
||||
|
||||
#if !defined(SRS_HTTPS)
|
||||
return srs_error_new(ERROR_HTTPS_NOT_SUPPORTED, "should configure with --https=on");
|
||||
#else
|
||||
srs_assert(!ssl_transport);
|
||||
ssl_transport = new SrsSslClient(transport);
|
||||
|
||||
if ((err = ssl_transport->handshake()) != srs_success) {
|
||||
disconnect();
|
||||
return srs_error_wrap(err, "http: ssl connect %s %s:%d to=%dms, rto=%dms",
|
||||
schema_.c_str(), host.c_str(), port, srsu2msi(timeout), srsu2msi(recv_timeout));
|
||||
}
|
||||
|
||||
return err;
|
||||
#endif
|
||||
}
|
||||
|
||||
ISrsStreamWriter* SrsHttpClient::writer()
|
||||
{
|
||||
if (ssl_transport) {
|
||||
return ssl_transport;
|
||||
}
|
||||
return transport;
|
||||
}
|
||||
|
||||
ISrsReader* SrsHttpClient::reader()
|
||||
{
|
||||
if (ssl_transport) {
|
||||
return ssl_transport;
|
||||
}
|
||||
return transport;
|
||||
}
|
||||
|
||||
|
|
|
@ -29,6 +29,14 @@
|
|||
#include <string>
|
||||
#include <map>
|
||||
|
||||
#include <openssl/ssl.h>
|
||||
|
||||
#ifdef SRS_HTTPS
|
||||
#if (OPENSSL_VERSION_NUMBER < 0x10002000L) // v1.0.2
|
||||
#error "For https, we requires openssl 1.0.2+"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include <srs_service_st.hpp>
|
||||
#include <srs_http_stack.hpp>
|
||||
|
||||
|
@ -43,6 +51,26 @@ class SrsTcpClient;
|
|||
// The default timeout for http client.
|
||||
#define SRS_HTTP_CLIENT_TIMEOUT (30 * SRS_UTIME_SECONDS)
|
||||
|
||||
// The SSL client over TCP transport.
|
||||
class SrsSslClient : virtual public ISrsReader, virtual public ISrsStreamWriter
|
||||
{
|
||||
private:
|
||||
SrsTcpClient* transport;
|
||||
private:
|
||||
SSL_CTX* ssl_ctx;
|
||||
SSL* ssl;
|
||||
BIO* bio_in;
|
||||
BIO* bio_out;
|
||||
public:
|
||||
SrsSslClient(SrsTcpClient* tcp);
|
||||
virtual ~SrsSslClient();
|
||||
public:
|
||||
virtual srs_error_t handshake();
|
||||
public:
|
||||
virtual srs_error_t read(void* buf, size_t size, ssize_t* nread);
|
||||
virtual srs_error_t write(void* buf, size_t size, ssize_t* nwrite);
|
||||
};
|
||||
|
||||
// The client to GET/POST/PUT/DELETE over HTTP.
|
||||
// @remark We will reuse the TCP transport until initialize or channel error,
|
||||
// such as send/recv failed.
|
||||
|
@ -64,17 +92,21 @@ private:
|
|||
// The timeout in srs_utime_t.
|
||||
srs_utime_t timeout;
|
||||
srs_utime_t recv_timeout;
|
||||
// The host name or ip.
|
||||
// The schema, host name or ip.
|
||||
std::string schema_;
|
||||
std::string host;
|
||||
int port;
|
||||
private:
|
||||
SrsSslClient* ssl_transport;
|
||||
public:
|
||||
SrsHttpClient();
|
||||
virtual ~SrsHttpClient();
|
||||
public:
|
||||
// Initliaze the client, disconnect the transport, renew the HTTP parser.
|
||||
// @param schema Should be http or https.
|
||||
// @param tm The underlayer TCP transport timeout in srs_utime_t.
|
||||
// @remark we will set default values in headers, which can be override by set_header.
|
||||
virtual srs_error_t initialize(std::string h, int p, srs_utime_t tm = SRS_HTTP_CLIENT_TIMEOUT);
|
||||
virtual srs_error_t initialize(std::string schema, std::string h, int p, srs_utime_t tm = SRS_HTTP_CLIENT_TIMEOUT);
|
||||
// Set HTTP request header in header[k]=v.
|
||||
// @return the HTTP client itself.
|
||||
virtual SrsHttpClient* set_header(std::string k, std::string v);
|
||||
|
@ -98,6 +130,8 @@ public:
|
|||
private:
|
||||
virtual void disconnect();
|
||||
virtual srs_error_t connect();
|
||||
ISrsStreamWriter* writer();
|
||||
ISrsReader* reader();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Reference in a new issue