1 // Copyright 2010-2018, Google Inc.
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
6 // met:
7 //
8 //     * Redistributions of source code must retain the above copyright
9 // notice, this list of conditions and the following disclaimer.
10 //     * Redistributions in binary form must reproduce the above
11 // copyright notice, this list of conditions and the following disclaimer
12 // in the documentation and/or other materials provided with the
13 // distribution.
14 //     * Neither the name of Google Inc. nor the names of its
15 // contributors may be used to endorse or promote products derived from
16 // this software without specific prior written permission.
17 //
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 
30 #include "net/http_client.h"
31 
32 #ifdef GOOGLE_JAPANESE_INPUT_BUILD
33 
34 #if defined(OS_WIN)
35 # include <windows.h>
36 # include <wininet.h>
37 #elif defined(OS_ANDROID)
38 # include "base/android_jni_proxy.h"
39 #elif defined(HAVE_CURL)
40 # include <curl/curl.h>
41 #endif  // defined(OS_WIN), defined(OS_ANDROID), defined(HAVE_CURL)
42 
43 #endif  // GOOGLE_JAPANESE_INPUT_BUILD
44 
45 #include "base/compiler_specific.h"
46 #include "base/logging.h"
47 #include "base/port.h"
48 #include "base/singleton.h"
49 #include "base/stopwatch.h"
50 #include "base/util.h"
51 #include "net/http_client_common.h"
52 
53 #ifdef GOOGLE_JAPANESE_INPUT_BUILD
54 #if defined(OS_MACOSX)
55 #include "net/http_client_mac.h"
56 #elif defined(OS_NACL)  // OS_MACOSX
57 #include "net/http_client_pepper.h"
58 #endif  // OS_MACOSX or OS_NACL
59 #else  // GOOGLE_JAPANESE_INPUT_BUILD
60 #include "net/http_client_null.h"
61 #endif  // GOOGLE_JAPANESE_INPUT_BUILD
62 
63 #include "net/proxy_manager.h"
64 
65 namespace mozc {
66 // We use a dummy user agent.
67 const char *kUserAgent = "Mozilla/5.0";
68 const int kOKResponseCode = 200;
69 
70 namespace {
71 class HTTPStream {
72  public:
HTTPStream(string * output_string,size_t max_data_size)73   HTTPStream(string *output_string, size_t max_data_size)
74       : output_string_(output_string),
75         max_data_size_(max_data_size),
76         output_size_(0) {
77     if (NULL != output_string_) {
78       output_string_->clear();
79     }
80     VLOG(2) << "max_data_size=" << max_data_size;
81   }
82 
~HTTPStream()83   virtual ~HTTPStream() {
84     VLOG(2) << output_size_ << " bytes received";
85   }
86 
Append(const char * buf,size_t size)87   size_t Append(const char *buf, size_t size) {
88     if (output_size_ + size >= max_data_size_) {
89       size = (max_data_size_ - output_size_);
90       LOG(WARNING) << "too long data max_data_size=" << max_data_size_;
91     }
92 
93     if (output_string_ != NULL) {
94       VLOG(2) << "Recived: " << size << " bytes to std::string";
95       output_string_->append(buf, size);
96     }
97 
98     output_size_ += size;
99 
100     return size;
101   }
102 
103  private:
104   string  *output_string_;
105   size_t   max_data_size_;
106   size_t   output_size_;
107 };
108 
109 #ifdef GOOGLE_JAPANESE_INPUT_BUILD
110 
111 #ifdef OS_WIN
112 // RAII class for HINTERNET
113 class ScopedHINTERNET {
114  public:
ScopedHINTERNET(HINTERNET internet)115   explicit ScopedHINTERNET(HINTERNET internet)
116       : internet_(internet) {}
~ScopedHINTERNET()117   virtual ~ScopedHINTERNET() {
118     if (NULL != internet_) {
119       VLOG(2) << "InternetCloseHandle() called";
120       ::InternetCloseHandle(internet_);
121     }
122     internet_ = NULL;
123   }
124 
get()125   HINTERNET get() {
126     return internet_;
127   }
128 
129  private:
130   HINTERNET internet_;
131 };
132 
StatusCallback(HINTERNET internet,DWORD_PTR context,DWORD status,LPVOID status_info,DWORD status_info_size)133 void CALLBACK StatusCallback(HINTERNET internet,
134                              DWORD_PTR context,
135                              DWORD status,
136                              LPVOID status_info,
137                              DWORD status_info_size) {
138   if (status == INTERNET_STATUS_REQUEST_COMPLETE) {
139     ::SetEvent(reinterpret_cast<HANDLE>(context));
140   }
141   if (status == INTERNET_STATUS_HANDLE_CLOSING) {
142     ::CloseHandle(reinterpret_cast<HANDLE>(context));
143   }
144 }
145 
CheckTimeout(HANDLE event,int64 elapsed_msec,int32 timeout_msec)146 bool CheckTimeout(HANDLE event, int64 elapsed_msec, int32 timeout_msec) {
147   const DWORD error = ::GetLastError();
148   if (error != ERROR_IO_PENDING) {
149     LOG(ERROR) << "Unexpected error state: " << error;
150     return false;
151   }
152   const int64 time_left = timeout_msec - elapsed_msec;
153   if (time_left < 0) {
154     LOG(WARNING) << "Already timed-out: " << time_left;
155     return false;
156   }
157   DCHECK_GE(elapsed_msec, 0);
158   DCHECK_LE(time_left, static_cast<int64>(MAXDWORD))
159       << "This should always be true because |timeout_msec| <= MAXDWORD";
160   const DWORD positive_time_left = static_cast<DWORD>(time_left);
161   const DWORD wait_result = ::WaitForSingleObject(event, positive_time_left);
162   if (wait_result == WAIT_FAILED) {
163     const DWORD wait_error = ::GetLastError();
164     LOG(ERROR) << "WaitForSingleObject failed. error: " << wait_error;
165     return false;
166   }
167   if (wait_result == WAIT_TIMEOUT) {
168     LOG(WARNING) << "WaitForSingleObject timed out after "
169                  << positive_time_left << " msec.";
170     return false;
171   }
172   if (wait_result != WAIT_OBJECT_0) {
173     LOG(ERROR) << "WaitForSingleObject returned unexpected result: "
174                << wait_result;
175     return false;
176   }
177   ::ResetEvent(event);
178   return true;
179 }
180 
RequestInternal(HTTPMethodType type,const string & url,const char * post_data,size_t post_size,const HTTPClient::Option & option,string * output_string)181 bool RequestInternal(HTTPMethodType type,
182                      const string &url,
183                      const char *post_data,
184                      size_t post_size,
185                      const HTTPClient::Option &option,
186                      string *output_string) {
187   if (option.timeout <= 0) {
188     LOG(ERROR) << "timeout should not be negative nor 0";
189     return false;
190   }
191 
192   Stopwatch stopwatch = stopwatch.StartNew();
193 
194   HANDLE event = ::CreateEvent(NULL, FALSE, FALSE, NULL);
195   if (NULL == event) {
196     LOG(ERROR) << "CreateEvent() failed: " << ::GetLastError();
197     return false;
198   }
199 
200   ScopedHINTERNET internet(::InternetOpenA(kUserAgent,
201                                            INTERNET_OPEN_TYPE_PRECONFIG,
202                                            NULL,
203                                            NULL,
204                                            INTERNET_FLAG_ASYNC));
205 
206   if (NULL == internet.get()) {
207     LOG(ERROR) << "InternetOpen() failed: "
208                << ::GetLastError() << " " << url;
209     ::CloseHandle(event);
210     return false;
211   }
212 
213   ::InternetSetStatusCallback(internet.get(), StatusCallback);
214 
215   URL_COMPONENTSW uc;
216   wchar_t Scheme[128];
217   wchar_t HostName[512];
218   wchar_t UserName[64];
219   wchar_t Password[64];
220   wchar_t UrlPath[256];
221   wchar_t ExtraInfo[512];
222 
223   uc.dwStructSize  = sizeof(uc);
224   uc.lpszScheme    = Scheme;
225   uc.lpszHostName  = HostName;
226   uc.lpszUserName  = UserName;
227   uc.lpszPassword  = Password;
228   uc.lpszUrlPath   = UrlPath;
229   uc.lpszExtraInfo = ExtraInfo;
230 
231   uc.dwSchemeLength    = sizeof(Scheme);
232   uc.dwHostNameLength  = sizeof(HostName);
233   uc.dwUserNameLength  = sizeof(UserName);
234   uc.dwPasswordLength  = sizeof(Password);
235   uc.dwUrlPathLength   = sizeof(UrlPath);
236   uc.dwExtraInfoLength = sizeof(ExtraInfo);
237 
238   std::wstring wurl;
239   Util::UTF8ToWide(url, &wurl);
240 
241   if (!::InternetCrackUrlW(wurl.c_str(), 0, 0, &uc)) {
242     LOG(WARNING) << "InternetCrackUrl() failed: "
243                  << ::GetLastError() << " " << url;
244     return false;
245   }
246 
247   if (uc.nScheme != INTERNET_SCHEME_HTTP &&
248       uc.nScheme != INTERNET_SCHEME_HTTPS) {
249     LOG(WARNING) << "Only accept HTTP or HTTPS: " << url;
250     return false;
251   }
252 
253   ScopedHINTERNET session(::InternetConnect(internet.get(),
254                                             uc.lpszHostName,
255                                             uc.nPort,
256                                             NULL,
257                                             NULL,
258                                             INTERNET_SERVICE_HTTP,
259                                             0,
260                                             0));
261 
262   if (NULL == session.get()) {
263     LOG(ERROR) << "InternetConnect() failed: "
264                << ::GetLastError() << " " << url;
265     return false;
266   }
267 
268   std::wstring uri = UrlPath;
269   if (uc.dwExtraInfoLength != 0) {
270     uri += uc.lpszExtraInfo;
271   }
272 
273   const wchar_t *method_type_string[]
274       = { L"GET", L"HEAD", L"POST", L"PUT", L"DELETE" };
275   const wchar_t *method = method_type_string[static_cast<int>(type)];
276   CHECK(method);
277 
278   ScopedHINTERNET handle(::HttpOpenRequestW
279                          (session.get(),
280                           method,
281                           uri.c_str(),
282                           NULL, NULL, NULL,
283                           INTERNET_FLAG_RELOAD |
284                           INTERNET_FLAG_DONT_CACHE |
285                           INTERNET_FLAG_NO_UI |
286                           INTERNET_FLAG_PRAGMA_NOCACHE |
287                           (uc.nScheme == INTERNET_SCHEME_HTTPS ?
288                            INTERNET_FLAG_SECURE : 0),
289                           reinterpret_cast<DWORD_PTR>(event)));
290 
291   if (NULL == handle.get()) {
292     LOG(ERROR) << "HttpOpenRequest() failed: "
293                << ::GetLastError() << " " << url;
294     return false;
295   }
296 
297   for (size_t i = 0; i < option.headers.size(); ++i) {
298     const string header = option.headers[i] + "\r\n";
299     std::wstring wheader;
300     Util::UTF8ToWide(header, &wheader);
301     if (!::HttpAddRequestHeadersW(handle.get(), wheader.c_str(), -1,
302                                   HTTP_ADDREQ_FLAG_ADD |
303                                   HTTP_ADDREQ_FLAG_REPLACE)) {
304       LOG(WARNING) << "HttpAddRequestHeaders() failed: " << option.headers[i]
305                    << " " << ::GetLastError();
306       return false;
307     }
308   }
309 
310   if (!::HttpSendRequest(handle.get(),
311                          NULL, 0,
312                          (type == HTTP_POST) ? (LPVOID)post_data : NULL,
313                          (type == HTTP_POST) ? post_size : 0)) {
314     if (!CheckTimeout(event, stopwatch.GetElapsedMilliseconds(),
315                       option.timeout)) {
316       LOG(ERROR) << "HttpSendRequest() failed: "
317                  << ::GetLastError() << " " << url;
318       return false;
319     }
320   } else {
321     return false;
322   }
323 
324   if (VLOG_IS_ON(2)) {
325     char buf[8192];
326     DWORD size = sizeof(buf);
327     if (::HttpQueryInfoA(handle.get(),
328                          HTTP_QUERY_RAW_HEADERS_CRLF |
329                          HTTP_QUERY_FLAG_REQUEST_HEADERS,
330                          buf,
331                          &size,
332                          0)) {
333       LOG(INFO) << "Request Header: " << buf;
334     }
335   }
336 
337   DWORD code = 0;
338   {
339     DWORD code_size = sizeof(code);
340     if (!::HttpQueryInfoW(handle.get(),
341                           HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER,
342                           &code,
343                           &code_size,
344                           0)) {
345       LOG(ERROR) << "HttpQueryInfo() failed: "
346                  << ::GetLastError() << " " << url;
347       return false;
348     }
349   }
350 
351   // make stream
352   HTTPStream stream(output_string, option.max_data_size);
353 
354   if (option.include_header || type == HTTP_HEAD) {
355     char buf[8192];
356     DWORD buf_size = sizeof(buf);
357     if (!::HttpQueryInfoA(handle.get(),
358                           HTTP_QUERY_RAW_HEADERS_CRLF,
359                           buf,
360                           &buf_size,
361                           0)) {
362       LOG(ERROR) << "HttpQueryInfo() failed: "
363                  << ::GetLastError() << " " << url;
364       return false;
365     }
366 
367     if (buf_size != stream.Append(buf, buf_size)) {
368       return false;
369     }
370 
371     if (type == HTTP_HEAD) {
372       return true;
373     }
374   }
375 
376   if (type == HTTP_POST || type == HTTP_GET) {
377     char buf[8129];
378     INTERNET_BUFFERSA ibuf;
379     ::ZeroMemory(&ibuf, sizeof(ibuf));
380     ibuf.dwStructSize = sizeof(ibuf);
381     while (true) {
382       ibuf.lpvBuffer = buf;
383       ibuf.dwBufferLength = sizeof(buf);
384       if (::InternetReadFileExA(handle.get(),
385                                 &ibuf,
386                                 WININET_API_FLAG_ASYNC,
387                                 reinterpret_cast<DWORD_PTR>(event)) ||
388           CheckTimeout(event, stopwatch.GetElapsedMilliseconds(),
389                        option.timeout)) {
390         const DWORD size = ibuf.dwBufferLength;
391         if (size == 0) {
392           break;
393         }
394         if (size != stream.Append(buf, size)) {
395           return false;
396         }
397       } else {
398         LOG(ERROR) << "InternetReadFileEx() failed: " << ::GetLastError();
399         return false;
400       }
401     }
402   }
403 
404   if (kOKResponseCode != code) {
405     LOG(WARNING) << "status code is not 200: " << code << " " << url;
406     return false;
407   }
408 
409   return true;
410 }
411 
412 #elif defined(OS_MACOSX)  // OS_WIN
413 
RequestInternal(HTTPMethodType type,const string & url,const char * post_data,size_t post_size,const HTTPClient::Option & option,string * output_string)414 bool RequestInternal(HTTPMethodType type,
415                      const string &url,
416                      const char *post_data,
417                      size_t post_size,
418                      const HTTPClient::Option &option,
419                      string *output_string) {
420   return MacHTTPRequestHandler::Request(type, url,
421                                         post_data, post_size, option,
422                                         output_string);
423 }
424 
425 #elif defined(OS_NACL)  // OS_WIN, OS_MACOSX
426 
RequestInternal(HTTPMethodType type,const string & url,const char * post_data,size_t post_size,const HTTPClient::Option & option,string * output_string)427 bool RequestInternal(HTTPMethodType type,
428                      const string &url,
429                      const char *post_data,
430                      size_t post_size,
431                      const HTTPClient::Option &option,
432                      string *output_string) {
433   return PepperHTTPRequestHandler::Request(type, url,
434                                            post_data, post_size, option,
435                                            output_string);
436 }
437 
438 #elif defined(OS_ANDROID)  // OS_WIN, OS_MACOSX, OS_NACL
RequestInternal(HTTPMethodType type,const string & url,const char * post_data,size_t post_size,const HTTPClient::Option & option,string * output_string)439 bool RequestInternal(HTTPMethodType type,
440                      const string &url,
441                      const char *post_data,
442                      size_t post_size,
443                      const HTTPClient::Option &option,
444                      string *output_string) {
445   // TODO(matsuzakit): Put body field in HTTP response on |output_string|
446   //     if the request arrives to the server and fails.
447   return jni::JavaHttpClientProxy::Request(type, url, post_data, post_size,
448                                            option, output_string);
449 }
450 #elif defined(HAVE_CURL)
451 
452 class CurlInitializer {
453  public:
CurlInitializer()454   CurlInitializer() {
455     curl_global_init(CURL_GLOBAL_ALL);
456   }
457 
~CurlInitializer()458   ~CurlInitializer() {
459     curl_global_cleanup();
460   }
461 
Init()462   void Init() {}
463 };
464 
HTTPOutputCallback(void * ptr,size_t size,size_t nmemb,void * stream)465 size_t HTTPOutputCallback(void *ptr, size_t size, size_t nmemb, void *stream) {
466   HTTPStream *s = reinterpret_cast<HTTPStream *>(stream);
467   return s->Append(reinterpret_cast<const char *>(ptr), size * nmemb);
468 }
469 
HTTPDebugCallback(CURL * curl,curl_infotype type,char * buf,size_t size,void * data)470 int HTTPDebugCallback(CURL *curl, curl_infotype type,
471                       char *buf, size_t size, void *data) {
472   if (CURLINFO_TEXT != type) {
473     return 0;
474   }
475   string *output = reinterpret_cast<string *>(data);
476   output->append(buf, size);
477   return 0;
478 }
479 
RequestInternal(HTTPMethodType type,const string & url,const char * post_data,size_t post_size,const HTTPClient::Option & option,string * output_string)480 bool RequestInternal(HTTPMethodType type,
481                      const string &url,
482                      const char *post_data,
483                      size_t post_size,
484                      const HTTPClient::Option &option,
485                      string *output_string) {
486   if (option.timeout < 0) {
487     LOG(ERROR) << "timeout should not be negative nor 0";
488     return false;
489   }
490 
491   Singleton<CurlInitializer>::get()->Init();
492 
493   CURL *curl = curl_easy_init();
494   if (NULL == curl) {
495     LOG(ERROR) << "curl_easy_init() failed";
496     return false;
497   }
498 
499   HTTPStream stream(output_string, option.max_data_size);
500 
501   string debug;
502   if (VLOG_IS_ON(2)) {
503     curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
504     curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, HTTPDebugCallback);
505     curl_easy_setopt(curl, CURLOPT_DEBUGDATA, reinterpret_cast<void *>(&debug));
506   }
507 
508   curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
509   curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1);
510   curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
511   curl_easy_setopt(curl, CURLOPT_AUTOREFERER, 1);
512   curl_easy_setopt(curl, CURLOPT_USERAGENT, kUserAgent);
513   curl_easy_setopt(curl, CURLOPT_FORBID_REUSE, 1);
514   curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT_MS, option.timeout);
515   curl_easy_setopt(curl, CURLOPT_TIMEOUT_MS, option.timeout);
516   curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 5);
517   curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &HTTPOutputCallback);
518   curl_easy_setopt(curl, CURLOPT_WRITEDATA,
519                    reinterpret_cast<void *>(&stream));
520 
521   string proxy_host;
522   string proxy_auth;
523   if (ProxyManager::GetProxyData(url, &proxy_host, &proxy_auth)) {
524     curl_easy_setopt(curl, CURLOPT_PROXY, proxy_host.c_str());
525     if (!proxy_auth.empty()) {
526       curl_easy_setopt(curl, CURLOPT_PROXYUSERPWD, proxy_auth.c_str());
527     }
528   }
529 
530   struct curl_slist *slist = NULL;
531   for (size_t i = 0; i < option.headers.size(); ++i) {
532     VLOG(2) << "Add header: " << option.headers[i];
533     slist = curl_slist_append(slist, option.headers[i].c_str());
534   }
535 
536   if (slist != NULL) {
537     curl_easy_setopt(curl, CURLOPT_HTTPHEADER, slist);
538   }
539 
540   if (option.include_header) {
541     curl_easy_setopt(curl, CURLOPT_HEADER, 1);
542   }
543 
544   switch (type) {
545     case HTTP_GET:
546       curl_easy_setopt(curl, CURLOPT_HTTPGET, 1);
547       break;
548     case HTTP_POST:
549       curl_easy_setopt(curl, CURLOPT_HTTPPOST, 1);
550       curl_easy_setopt(curl, CURLOPT_POSTFIELDS, post_data);
551       curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, post_size);
552       break;
553     case HTTP_HEAD:
554       curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "HEAD");
555       curl_easy_setopt(curl, CURLOPT_NOBODY, 1);
556       curl_easy_setopt(curl, CURLOPT_HEADER, 1);
557       break;
558     default:
559       LOG(ERROR) << "Unknown method: " << type;
560       curl_easy_cleanup(curl);
561       return false;
562       break;
563   }
564 
565   const CURLcode ret = curl_easy_perform(curl);
566   bool result = (CURLE_OK == ret);
567 
568   if (!result) {
569     LOG(WARNING) << "curl_easy_perform() failed: " << curl_easy_strerror(ret);
570   }
571 
572   int code = 0;
573   curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &code);
574   if (kOKResponseCode != code) {
575     LOG(WARNING) << "status code is not 200: " << code;
576     result = false;
577   }
578 
579   curl_easy_cleanup(curl);
580   if (slist != NULL) {
581     curl_slist_free_all(slist);
582   }
583 
584   if (VLOG_IS_ON(2)) {
585     LOG(INFO) << debug;
586   }
587 
588   return result;
589 }
590 #else  // OS_WIN, OS_MACOSX, OS_NACL, OS_ANDROID, HAVE_CURL
591 #error "HttpClient does not support your platform."
592 #endif  // OS_WIN, OS_MACOSX, OS_NACL, OS_ANDROID, HAVE_CURL
593 
594 #else  // GOOGLE_JAPANESE_INPUT_BUILD
595 
RequestInternal(HTTPMethodType type,const string & url,const char * post_data,size_t post_size,const HTTPClient::Option & option,string * output_string)596 bool RequestInternal(HTTPMethodType type,
597                      const string &url,
598                      const char *post_data,
599                      size_t post_size,
600                      const HTTPClient::Option &option,
601                      string *output_string) {
602   return NullHTTPRequestHandler::Request(type, url,
603                                          post_data, post_size, option,
604                                          output_string);
605 }
606 
607 #endif  // GOOGLE_JAPANESE_INPUT_BUILD
608 
609 }  // namespace
610 
611 class HTTPClientImpl: public HTTPClientInterface {
612  public:
Get(const string & url,const HTTPClient::Option & option,string * output_string) const613   virtual bool Get(const string &url, const HTTPClient::Option &option,
614                    string *output_string) const {
615     return RequestInternal(HTTP_GET, url, NULL, 0, option,
616                            output_string);
617   }
618 
Head(const string & url,const HTTPClient::Option & option,string * output_string) const619   virtual bool Head(const string &url, const HTTPClient::Option &option,
620                     string *output_string) const {
621     return RequestInternal(HTTP_HEAD, url, NULL, 0, option,
622                            output_string);
623   }
624 
Post(const string & url,const string & data,const HTTPClient::Option & option,string * output_string) const625   virtual bool Post(const string &url, const string &data,
626                     const HTTPClient::Option &option,
627                     string *output_string) const {
628     return RequestInternal(HTTP_POST, url, data.data(), data.size(),
629                            option, output_string);
630   }
631 };
632 
633 namespace {
634 
635 const HTTPClientInterface *g_http_connection_handler = NULL;
636 
GetHTTPClient()637 const HTTPClientInterface &GetHTTPClient() {
638   if (g_http_connection_handler == NULL) {
639     g_http_connection_handler = Singleton<HTTPClientImpl>::get();
640   }
641   return *g_http_connection_handler;
642 }
643 }  // namespace
644 
SetHTTPClientHandler(const HTTPClientInterface * handler)645 void HTTPClient::SetHTTPClientHandler(
646     const HTTPClientInterface *handler) {
647   g_http_connection_handler = handler;
648 }
649 
Get(const string & url,string * output_string)650 bool HTTPClient::Get(const string &url, string *output_string) {
651   return GetHTTPClient().Get(url, Option(), output_string);
652 }
653 
Head(const string & url,string * output_string)654 bool HTTPClient::Head(const string &url, string *output_string) {
655   return GetHTTPClient().Head(url, Option(), output_string);
656 }
657 
Post(const string & url,const string & data,string * output_string)658 bool HTTPClient::Post(const string &url, const string &data,
659                       string *output_string) {
660   return GetHTTPClient().Post(url, data, Option(), output_string);
661 }
662 
Get(const string & url,const Option & option,string * output_string)663 bool HTTPClient::Get(const string &url, const Option &option,
664                      string *output_string) {
665   return GetHTTPClient().Get(url, option, output_string);
666 }
667 
Head(const string & url,const Option & option,string * output_string)668 bool HTTPClient::Head(const string &url, const Option &option,
669                       string *output_string) {
670   return GetHTTPClient().Head(url, option, output_string);
671 }
672 
Post(const string & url,const string & data,const Option & option,string * output_string)673 bool HTTPClient::Post(const string &url, const string &data,
674                       const Option &option, string *output_string) {
675   return GetHTTPClient().Post(url, data, option, output_string);
676 }
677 }  // namespace mozc
678