diff options
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 | } | ||