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