Fix a crash when a HTTP input connected to an UDP output goes unavailable.
[cubemap] / input.cpp
1 #include <stddef.h>
2 #include <string>
3
4 #include "httpinput.h"
5 #include "input.h"
6 #include "state.pb.h"
7 #include "udpinput.h"
8
9 using namespace std;
10
11 namespace {
12
13 // Does not support passwords, only user:host, since this is really only used
14 // to parse VLC's udp://source@multicastgroup:1234/ syntax (we do not support
15 // even basic auth).
16 void split_user_host(const string &user_host, string *user, string *host)
17 {
18         size_t split = user_host.find("@");
19         if (split == string::npos) {
20                 user->clear();
21                 *host = user_host;
22         } else {
23                 *user = string(user_host.begin(), user_host.begin() + split);
24                 *host = string(user_host.begin() + split + 1, user_host.end());
25         }
26 }
27
28 }  // namespace
29
30 // Extremely rudimentary URL parsing.
31 bool parse_url(const string &url, string *protocol, string *user, string *host, string *port, string *path)
32 {
33         // pipe:foo (or pipe:"foo").
34         if (url.find("pipe:") == 0) {
35                 *protocol = "pipe";
36                 *path = string(url.begin() + 5, url.end());
37                 return true;
38         }
39
40         size_t split = url.find("://");
41         if (split == string::npos) {
42                 return false;
43         }
44         *protocol = string(url.begin(), url.begin() + split);
45
46         string rest = string(url.begin() + split + 3, url.end());
47
48         // Split at the first slash, or the first colon that's not within [].
49         bool within_brackets = false;
50         for (split = 0; split < rest.size(); ++split) {
51                 if (rest[split] == '[') {
52                         if (within_brackets) {
53                                 // Can't nest brackets.
54                                 return false;
55                         }
56                         within_brackets = true;
57                 } else if (rest[split] == ']') {
58                         if (!within_brackets) {
59                                 // ] without matching [.
60                                 return false;
61                         }
62                         within_brackets = false;
63                 } else if (rest[split] == '/') {
64                         break;
65                 } else if (rest[split] == ':' && !within_brackets) {
66                         break;
67                 }
68         }
69
70         if (split == rest.size()) {
71                 // http://foo
72                 split_user_host(rest, user, host);
73                 *port = *protocol;
74                 *path = "/";
75                 return true;
76         }
77
78         split_user_host(string(rest.begin(), rest.begin() + split), user, host);
79         char ch = rest[split];  // Colon or slash.
80         rest = string(rest.begin() + split + 1, rest.end());
81
82         if (ch == ':') {
83                 // Parse the port.
84                 split = rest.find_first_of('/');
85                 if (split == string::npos) {
86                         // http://foo:1234
87                         *port = rest;
88                         *path = "/";
89                         return true;
90                 } else {
91                         // http://foo:1234/bar
92                         *port = string(rest.begin(), rest.begin() + split);
93                         *path = string(rest.begin() + split, rest.end());
94                         return true;
95                 }
96         }
97
98         // http://foo/bar
99         *port = *protocol;
100         *path = "/" + rest;
101         return true;
102 }
103
104 Input *create_input(const string &url, Input::Encoding encoding)
105 {
106         string protocol, user, host, port, path;
107         if (!parse_url(url, &protocol, &user, &host, &port, &path)) {
108                 return nullptr;
109         }
110         if (protocol == "http" || protocol == "pipe") {
111                 return new HTTPInput(url, encoding);
112         }
113         if (protocol == "udp") {
114                 assert(encoding == Input::INPUT_ENCODING_RAW);
115                 return new UDPInput(url);
116         }
117         return nullptr;
118 }
119
120 Input *create_input(const InputProto &serialized)
121 {
122         string protocol, user, host, port, path;
123         if (!parse_url(serialized.url(), &protocol, &user, &host, &port, &path)) {
124                 return nullptr;
125         }
126         if (protocol == "http" || protocol == "pipe") {
127                 return new HTTPInput(serialized);
128         }
129         if (protocol == "udp") {
130                 return new UDPInput(serialized);
131         }
132         return nullptr;
133 }
134
135 Input::~Input() {}
136