diff options
author | Kelly Rauchenberger <fefferburbia@gmail.com> | 2018-08-05 11:14:39 -0400 |
---|---|---|
committer | Kelly Rauchenberger <fefferburbia@gmail.com> | 2018-08-05 11:14:39 -0400 |
commit | 38203b745ae1903ba0804ed5532b78d255bea635 (patch) | |
tree | 52f57ce85e65361c534c461abc82fb1ed9768d49 /src/request.cpp | |
parent | 7c44fd17bb6be54a2ea4b60761e91053ca988977 (diff) | |
download | libtwittercpp-38203b745ae1903ba0804ed5532b78d255bea635.tar.gz libtwittercpp-38203b745ae1903ba0804ed5532b78d255bea635.tar.bz2 libtwittercpp-38203b745ae1903ba0804ed5532b78d255bea635.zip |
Added timeline polling
Because the streaming API will sunset on August 16th, it is essential to implement timeline polling so that the library can still be used to access tweets. This commit breaks the current API even for clients still using the streaming API because the OAuth connection data was pulled from twitter::client into twitter::auth. Other than that, almost everything works the same, and if a client wants to update their libtwitter++ within the next week and a half, they are free to.
Diffstat (limited to 'src/request.cpp')
-rw-r--r-- | src/request.cpp | 264 |
1 files changed, 264 insertions, 0 deletions
diff --git a/src/request.cpp b/src/request.cpp new file mode 100644 index 0000000..a79c7f0 --- /dev/null +++ b/src/request.cpp | |||
@@ -0,0 +1,264 @@ | |||
1 | #include "request.h" | ||
2 | #include <json.hpp> | ||
3 | #include "codes.h" | ||
4 | |||
5 | // These are here for debugging curl stuff | ||
6 | |||
7 | static | ||
8 | void dump(const char *text, | ||
9 | FILE *stream, unsigned char *ptr, size_t size) | ||
10 | { | ||
11 | size_t i; | ||
12 | size_t c; | ||
13 | unsigned int width=80; | ||
14 | |||
15 | fprintf(stream, "%s, %10.10ld bytes (0x%8.8lx)\n", | ||
16 | text, (long)size, (long)size); | ||
17 | |||
18 | for(i=0; i<size; i+= width) { | ||
19 | fprintf(stream, "%4.4lx: ", (long)i); | ||
20 | |||
21 | /* show hex to the left | ||
22 | for(c = 0; c < width; c++) { | ||
23 | if(i+c < size) | ||
24 | fprintf(stream, "%02x ", ptr[i+c]); | ||
25 | else | ||
26 | fputs(" ", stream); | ||
27 | }*/ | ||
28 | |||
29 | /* show data on the right */ | ||
30 | for(c = 0; (c < width) && (i+c < size); c++) { | ||
31 | char x = (ptr[i+c] >= 0x20 && ptr[i+c] < 0x80) ? ptr[i+c] : '.'; | ||
32 | fputc(x, stream); | ||
33 | } | ||
34 | |||
35 | fputc('\n', stream); /* newline */ | ||
36 | } | ||
37 | } | ||
38 | |||
39 | static | ||
40 | int my_trace(CURL *handle, curl_infotype type, | ||
41 | char *data, size_t size, | ||
42 | void *userp) | ||
43 | { | ||
44 | const char *text; | ||
45 | (void)handle; /* prevent compiler warning */ | ||
46 | |||
47 | switch (type) { | ||
48 | case CURLINFO_TEXT: | ||
49 | fprintf(stderr, "== Info: %s", data); | ||
50 | default: /* in case a new one is introduced to shock us */ | ||
51 | return 0; | ||
52 | |||
53 | case CURLINFO_HEADER_OUT: | ||
54 | text = "=> Send header"; | ||
55 | break; | ||
56 | case CURLINFO_DATA_OUT: | ||
57 | text = "=> Send data"; | ||
58 | break; | ||
59 | case CURLINFO_SSL_DATA_OUT: | ||
60 | text = "=> Send SSL data"; | ||
61 | break; | ||
62 | case CURLINFO_HEADER_IN: | ||
63 | text = "<= Recv header"; | ||
64 | break; | ||
65 | case CURLINFO_DATA_IN: | ||
66 | text = "<= Recv data"; | ||
67 | break; | ||
68 | case CURLINFO_SSL_DATA_IN: | ||
69 | text = "<= Recv SSL data"; | ||
70 | break; | ||
71 | } | ||
72 | |||
73 | dump(text, stderr, (unsigned char *)data, size); | ||
74 | return 0; | ||
75 | } | ||
76 | |||
77 | namespace twitter { | ||
78 | |||
79 | request::request( | ||
80 | std::string url) try : | ||
81 | ios_(output_), | ||
82 | conn_(ios_) | ||
83 | { | ||
84 | conn_.add<CURLOPT_URL>(url.c_str()); | ||
85 | } catch (const curl::curl_easy_exception& error) | ||
86 | { | ||
87 | std::throw_with_nested(connection_error()); | ||
88 | } | ||
89 | |||
90 | std::string request::perform() | ||
91 | { | ||
92 | try | ||
93 | { | ||
94 | conn_.perform(); | ||
95 | } catch (const curl::curl_easy_exception& error) | ||
96 | { | ||
97 | std::throw_with_nested(connection_error()); | ||
98 | } | ||
99 | |||
100 | int response_code = conn_.get_info<CURLINFO_RESPONSE_CODE>().get(); | ||
101 | std::string result = output_.str(); | ||
102 | |||
103 | if (response_code / 100 != 2) | ||
104 | { | ||
105 | nlohmann::json response_json; | ||
106 | |||
107 | try | ||
108 | { | ||
109 | response_json = nlohmann::json::parse(result); | ||
110 | } catch (const std::invalid_argument& e) | ||
111 | { | ||
112 | std::throw_with_nested(invalid_response(result)); | ||
113 | } | ||
114 | |||
115 | for (nlohmann::json& error : response_json["errors"]) | ||
116 | { | ||
117 | int error_code; | ||
118 | std::string error_message; | ||
119 | |||
120 | try | ||
121 | { | ||
122 | error_code = error["code"].get<int>(); | ||
123 | error_message = error["message"].get<std::string>(); | ||
124 | } catch (const std::domain_error& e) | ||
125 | { | ||
126 | std::throw_with_nested(invalid_response(result)); | ||
127 | } | ||
128 | |||
129 | switch (error_code) | ||
130 | { | ||
131 | case 32: | ||
132 | case 135: | ||
133 | case 215: | ||
134 | throw bad_auth(error_message); | ||
135 | |||
136 | case 44: | ||
137 | throw invalid_media(error_message); | ||
138 | |||
139 | case 64: | ||
140 | throw account_suspended(error_message); | ||
141 | |||
142 | case 88: | ||
143 | throw rate_limit_exceeded(error_message); | ||
144 | |||
145 | case 89: | ||
146 | throw bad_token(error_message); | ||
147 | |||
148 | case 130: | ||
149 | throw server_overloaded(error_message); | ||
150 | |||
151 | case 131: | ||
152 | throw server_error(error_message); | ||
153 | |||
154 | case 185: | ||
155 | throw update_limit_exceeded(error_message); | ||
156 | |||
157 | case 186: | ||
158 | throw bad_length(error_message); | ||
159 | |||
160 | case 187: | ||
161 | throw duplicate_status(error_message); | ||
162 | |||
163 | case 226: | ||
164 | throw suspected_spam(error_message); | ||
165 | |||
166 | case 261: | ||
167 | throw write_restricted(error_message); | ||
168 | } | ||
169 | } | ||
170 | |||
171 | if (response_code == 429) | ||
172 | { | ||
173 | throw rate_limit_exceeded("HTTP 429 Too Many Requests"); | ||
174 | } else if (response_code == 500) | ||
175 | { | ||
176 | throw server_error("HTTP 500 Internal Server Error"); | ||
177 | } else if (response_code == 502) | ||
178 | { | ||
179 | throw server_unavailable("HTTP 502 Bad Gateway"); | ||
180 | } else if (response_code == 503) | ||
181 | { | ||
182 | throw server_overloaded("HTTP 503 Service Unavailable"); | ||
183 | } else if (response_code == 504) | ||
184 | { | ||
185 | throw server_timeout("HTTP 504 Gateway Timeout"); | ||
186 | } | ||
187 | |||
188 | throw unknown_error(response_code, result); | ||
189 | } | ||
190 | |||
191 | return result; | ||
192 | } | ||
193 | |||
194 | get::get( | ||
195 | const auth& tauth, | ||
196 | std::string url) try : | ||
197 | request(url) | ||
198 | { | ||
199 | std::string oauthHeader = | ||
200 | tauth.getClient().getFormattedHttpHeader(OAuth::Http::Get, url, ""); | ||
201 | |||
202 | if (!oauthHeader.empty()) | ||
203 | { | ||
204 | headers_.add(std::move(oauthHeader)); | ||
205 | } | ||
206 | |||
207 | conn_.add<CURLOPT_HTTPHEADER>(headers_.get()); | ||
208 | } catch (const OAuth::ParseError& error) | ||
209 | { | ||
210 | std::throw_with_nested(connection_error()); | ||
211 | } catch (const curl::curl_easy_exception& error) | ||
212 | { | ||
213 | std::throw_with_nested(connection_error()); | ||
214 | } | ||
215 | |||
216 | post::post( | ||
217 | const auth& tauth, | ||
218 | std::string url, | ||
219 | std::string datastr) try : | ||
220 | request(url) | ||
221 | { | ||
222 | std::string oauthHeader = | ||
223 | tauth.getClient().getFormattedHttpHeader(OAuth::Http::Post, url, datastr); | ||
224 | |||
225 | if (!oauthHeader.empty()) | ||
226 | { | ||
227 | headers_.add(std::move(oauthHeader)); | ||
228 | } | ||
229 | |||
230 | conn_.add<CURLOPT_HTTPHEADER>(headers_.get()); | ||
231 | conn_.add<CURLOPT_COPYPOSTFIELDS>(datastr.c_str()); | ||
232 | } catch (const OAuth::ParseError& error) | ||
233 | { | ||
234 | std::throw_with_nested(connection_error()); | ||
235 | } catch (const curl::curl_easy_exception& error) | ||
236 | { | ||
237 | std::throw_with_nested(connection_error()); | ||
238 | } | ||
239 | |||
240 | multipost::multipost( | ||
241 | const auth& tauth, | ||
242 | std::string url, | ||
243 | const curl_httppost* fields) try : | ||
244 | request(url) | ||
245 | { | ||
246 | std::string oauthHeader = | ||
247 | tauth.getClient().getFormattedHttpHeader(OAuth::Http::Post, url, ""); | ||
248 | |||
249 | if (!oauthHeader.empty()) | ||
250 | { | ||
251 | headers_.add(std::move(oauthHeader)); | ||
252 | } | ||
253 | |||
254 | conn_.add<CURLOPT_HTTPHEADER>(headers_.get()); | ||
255 | conn_.add<CURLOPT_HTTPPOST>(fields); | ||
256 | } catch (const OAuth::ParseError& error) | ||
257 | { | ||
258 | std::throw_with_nested(connection_error()); | ||
259 | } catch (const curl::curl_easy_exception& error) | ||
260 | { | ||
261 | std::throw_with_nested(connection_error()); | ||
262 | } | ||
263 | |||
264 | } | ||