You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

169 lines
6.5KB

  1. #ifndef _rtmp_url_h_
  2. #define _rtmp_url_h_
  3. #include <stdio.h>
  4. #include <stdlib.h>
  5. #include <string.h>
  6. #include "urlcodec.h"
  7. #include "uri-parse.h"
  8. struct rtmp_url_t
  9. {
  10. unsigned short port; // default 1935
  11. char* scheme;
  12. char* host;
  13. char* app;
  14. char* stream;
  15. char* tcurl;
  16. char* vhost; // rtmp://host/vhost/app/stream or rtmp://host/app/stream?vhost=xxx
  17. char __ptr[1024];
  18. };
  19. static int rtmp_url_parse(const char* url, struct rtmp_url_t* u)
  20. {
  21. int r, n;
  22. size_t i, j;
  23. char** v[3];
  24. const char* p[4];
  25. const char* pend[4];
  26. struct uri_t* uri;
  27. struct uri_query_t* q;
  28. static const int PORT = 1935;
  29. uri = uri_parse(url, (int)strlen(url));
  30. if (!uri) return -1;
  31. n = 0;
  32. memset(u, 0, sizeof(*u));
  33. u->port = (u_short)(0 == uri->port ? PORT : uri->port);
  34. u->host = u->__ptr + n;
  35. r = snprintf(u->host, sizeof(u->__ptr) - n, "%s", uri->host);
  36. if(r <= 0 || (size_t)r >= sizeof(u->__ptr) - n)
  37. goto FAILED_PARSE_RTMP_URL;
  38. n += r + 1;
  39. u->vhost = u->host; // default value
  40. u->scheme = u->__ptr + n;
  41. r = snprintf(u->scheme, sizeof(u->__ptr) - n, "%s", uri->scheme);
  42. if(r <= 0 || (size_t)r >= sizeof(u->__ptr) - n)
  43. goto FAILED_PARSE_RTMP_URL;
  44. n += r + 1;
  45. // parse vhost
  46. if (uri->query && *uri->query)
  47. {
  48. q = NULL;
  49. for(r = uri_query(uri->query, uri->query + strlen(uri->query), &q); r > 0; r--)
  50. {
  51. if (5 == q[r].n_name && 0 == strncmp("vhost", q[r].name, 5))
  52. {
  53. u->vhost = u->__ptr + n;
  54. r = snprintf(u->vhost, sizeof(u->__ptr) - n, "%.*s", q[r].n_value, q[r].value);
  55. if (r <= 0 || (size_t)r >= sizeof(u->__ptr) - n)
  56. {
  57. uri_query_free(&q);
  58. goto FAILED_PARSE_RTMP_URL;
  59. }
  60. n += r + 1;
  61. break;
  62. }
  63. }
  64. uri_query_free(&q);
  65. }
  66. uri_free(uri);
  67. p[0] = strstr(url, "://");
  68. p[0] = p[0] ? p[0] + 3 : url;
  69. p[0] = strchr(p[0], '/');
  70. if (!p[0]) return -1;
  71. for (i = 0; i+1 < sizeof(p)/sizeof(p[0]) && p[i]; i++)
  72. {
  73. // filter '/' (one ore more)
  74. while ('/' == *p[i]) p[i]++;
  75. pend[i] = strchr(p[i], '/');
  76. p[i+1] = pend[i];
  77. }
  78. if (i < 2 || i > 3)
  79. return -1; // invalid rtmp url
  80. j = 0;
  81. if (i > 2)
  82. v[j++] = &u->vhost;
  83. v[j++] = &u->app;
  84. v[j++] = &u->stream;
  85. for(j = 0; j < i; j++)
  86. {
  87. *v[j] = u->__ptr + n;
  88. r = url_decode(p[j], pend[j] ? (int)(pend[j] - p[j]) : -1, *v[j], sizeof(u->__ptr) - n);
  89. if (r <= 0 || (size_t)r >= sizeof(u->__ptr) - n)
  90. return -1;
  91. n += r + 1;
  92. }
  93. u->tcurl = u->__ptr + n;
  94. if(PORT != u->port)
  95. r = snprintf(u->tcurl, sizeof(u->__ptr) - n, "rtmp://%s:%d/%s", u->vhost, u->port, u->app);
  96. else
  97. r = snprintf(u->tcurl, sizeof(u->__ptr) - n, "rtmp://%s/%s", u->vhost, u->app);
  98. if(r <= 0 || (size_t)r >= sizeof(u->__ptr) - n)
  99. return -1;
  100. n += r + 1;
  101. return 0;
  102. FAILED_PARSE_RTMP_URL:
  103. uri_free(uri);
  104. return -1;
  105. }
  106. #if !defined(NDEBUG)
  107. static inline void rtmp_url_parse_test(void)
  108. {
  109. struct rtmp_url_t rtmp;
  110. assert(0 == rtmp_url_parse("rtmp://www.abc.com/app/stream", &rtmp));
  111. assert(0 == strcmp(rtmp.tcurl, "rtmp://www.abc.com/app") && 0 == strcmp(rtmp.app, "app") && 0 == strcmp(rtmp.stream, "stream"));
  112. assert(0 == strcmp(rtmp.scheme, "rtmp") && 0 == strcmp(rtmp.host, "www.abc.com") && 1935 == rtmp.port && 0 == strcmp(rtmp.vhost, "www.abc.com"));
  113. // stream name with parameter(s)
  114. assert(0 == rtmp_url_parse("rtmp://w.a.c/a/s?param1=value1&param2=value2", &rtmp));
  115. assert(0 == strcmp(rtmp.tcurl, "rtmp://w.a.c/a") && 0 == strcmp(rtmp.app, "a") && 0 == strcmp(rtmp.stream, "s?param1=value1&param2=value2"));
  116. assert(0 == strcmp(rtmp.scheme, "rtmp") && 0 == strcmp(rtmp.host, "w.a.c") && 1935 == rtmp.port && 0 == strcmp(rtmp.vhost, "w.a.c"));
  117. // tcurl with param
  118. assert(0 == rtmp_url_parse("rtmp://192.168.1.100:1936/www.abc.com/app?param1=value1&param2=value2/stream?param1=value1&param2=value2", &rtmp));
  119. assert(0 == strcmp(rtmp.tcurl, "rtmp://www.abc.com:1936/app?param1=value1&param2=value2") && 0 == strcmp(rtmp.app, "app?param1=value1&param2=value2") && 0 == strcmp(rtmp.stream, "stream?param1=value1&param2=value2"));
  120. assert(0 == strcmp(rtmp.scheme, "rtmp") && 0 == strcmp(rtmp.host, "192.168.1.100") && 1936 == rtmp.port && 0 == strcmp(rtmp.vhost, "www.abc.com"));
  121. // obs tcurl with '/'
  122. assert(0 == rtmp_url_parse("rtmp://www.abc.com:1936//app//stream", &rtmp));
  123. assert(0 == strcmp(rtmp.tcurl, "rtmp://www.abc.com:1936/app") && 0 == strcmp(rtmp.app, "app") && 0 == strcmp(rtmp.stream, "stream"));
  124. assert(0 == strcmp(rtmp.scheme, "rtmp") && 0 == strcmp(rtmp.host, "www.abc.com") && 1936 == rtmp.port && 0 == strcmp(rtmp.vhost, "www.abc.com"));
  125. // host + vhost
  126. assert(0 == rtmp_url_parse("rtmp://192.168.1.100:1936/www.abc.com/app//stream", &rtmp));
  127. assert(0 == strcmp(rtmp.tcurl, "rtmp://www.abc.com:1936/app") && 0 == strcmp(rtmp.app, "app") && 0 == strcmp(rtmp.stream, "stream"));
  128. assert(0 == strcmp(rtmp.scheme, "rtmp") && 0 == strcmp(rtmp.host, "192.168.1.100") && 1936 == rtmp.port && 0 == strcmp(rtmp.vhost, "www.abc.com"));
  129. // vhost parameter
  130. assert(0 == rtmp_url_parse("rtmp://192.168.1.100/app//stream?param1=value1&vhost=www.abc.com&param2=value2", &rtmp));
  131. assert(0 == strcmp(rtmp.tcurl, "rtmp://www.abc.com/app") && 0 == strcmp(rtmp.app, "app") && 0 == strcmp(rtmp.stream, "stream?param1=value1&vhost=www.abc.com&param2=value2"));
  132. assert(0 == strcmp(rtmp.scheme, "rtmp") && 0 == strcmp(rtmp.host, "192.168.1.100") && 1935 == rtmp.port && 0 == strcmp(rtmp.vhost, "www.abc.com"));
  133. // bad case: vhost + parameter
  134. assert(0 == rtmp_url_parse("rtmp://192.168.1.100/www.abc.com/app//stream?param1=value1&vhost=www.google.com&param2=value2", &rtmp));
  135. assert(0 == strcmp(rtmp.tcurl, "rtmp://www.abc.com/app") && 0 == strcmp(rtmp.app, "app") && 0 == strcmp(rtmp.stream, "stream?param1=value1&vhost=www.google.com&param2=value2"));
  136. assert(0 == strcmp(rtmp.scheme, "rtmp") && 0 == strcmp(rtmp.host, "192.168.1.100") && 1935 == rtmp.port && 0 == strcmp(rtmp.vhost, "www.abc.com"));
  137. // bad case: no stream
  138. assert(-1 == rtmp_url_parse("rtmp://192.168.1.100///stream?param1=value1&vhost=www.abc.com&param2=value2", &rtmp));
  139. // bad case: no path
  140. assert(-1 == rtmp_url_parse("rtmp://192.168.1.100/", &rtmp));
  141. }
  142. #endif
  143. #endif /* !_rtmp_url_h_ */