kythe.io@v0.0.68-0.20240422202219-7225dbc01741/kythe/cxx/common/net_client.cc (about)

     1  /*
     2   * Copyright 2015 The Kythe Authors. All rights reserved.
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *   http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  
    17  #include "kythe/cxx/common/net_client.h"
    18  
    19  #include <curl/curl.h>
    20  #include <curl/easy.h>
    21  #include <rapidjson/document.h>
    22  
    23  #include <algorithm>
    24  #include <cstddef>
    25  #include <cstring>
    26  #include <string>
    27  
    28  #include "absl/log/check.h"
    29  #include "absl/log/log.h"
    30  #include "google/protobuf/io/coded_stream.h"
    31  #include "google/protobuf/io/zero_copy_stream_impl_lite.h"
    32  #include "google/protobuf/message.h"
    33  #include "kythe/cxx/common/json_proto.h"
    34  #include "rapidjson/error/en.h"
    35  #include "rapidjson/stringbuffer.h"
    36  #include "rapidjson/writer.h"
    37  
    38  namespace kythe {
    39  
    40  JsonClient::JsonClient() : curl_(::curl_easy_init()) {
    41    CHECK(curl_ != nullptr);
    42  }
    43  
    44  JsonClient::~JsonClient() {
    45    if (curl_) {
    46      ::curl_easy_cleanup(curl_);
    47      curl_ = nullptr;
    48    }
    49  }
    50  
    51  void JsonClient::InitNetwork() {
    52    CHECK(::curl_global_init(CURL_GLOBAL_ALL) == 0);
    53  }
    54  
    55  size_t JsonClient::CurlWriteCallback(void* data, size_t size, size_t nmemb,
    56                                       void* user) {
    57    JsonClient* client = static_cast<JsonClient*>(user);
    58    size_t receive_head = client->received_.size();
    59    client->received_.resize(receive_head + size * nmemb);
    60    ::memcpy(&client->received_[receive_head], data, size * nmemb);
    61    return size * nmemb;
    62  }
    63  
    64  size_t JsonClient::CurlReadCallback(void* data, size_t size, size_t nmemb,
    65                                      void* user) {
    66    JsonClient* client = static_cast<JsonClient*>(user);
    67    if (client->send_head_ >= client->to_send_.size()) {
    68      return 0;
    69    }
    70    size_t bytes_to_send =
    71        std::min(size * nmemb, client->to_send_.size() - client->send_head_);
    72    ::memcpy(data, client->to_send_.data() + client->send_head_, bytes_to_send);
    73    client->send_head_ += bytes_to_send;
    74    return bytes_to_send;
    75  }
    76  
    77  bool JsonClient::Request(const std::string& uri, bool post,
    78                           const rapidjson::Document& request,
    79                           rapidjson::Document* response) {
    80    rapidjson::StringBuffer string_buffer;
    81    rapidjson::Writer<rapidjson::StringBuffer> writer(string_buffer);
    82    request.Accept(writer);
    83    return Request(uri, post, string_buffer.GetString(), response);
    84  }
    85  
    86  bool JsonClient::Request(const std::string& uri, bool post,
    87                           const std::string& request,
    88                           rapidjson::Document* response) {
    89    std::string to_decode;
    90    if (!Request(uri, post, request, &to_decode)) {
    91      return false;
    92    }
    93    response->Parse(to_decode.c_str());
    94    if (response->HasParseError()) {
    95      LOG(ERROR) << "(uri: " << uri << "): bad JSON at offset "
    96                 << response->GetErrorOffset() << ": "
    97                 << rapidjson::GetParseError_En(response->GetParseError());
    98      return false;
    99    }
   100    return true;
   101  }
   102  
   103  bool JsonClient::Request(const std::string& uri, bool post,
   104                           const std::string& request, std::string* response) {
   105    to_send_ = request;
   106    send_head_ = 0;
   107    received_.clear();
   108  
   109    ::curl_easy_setopt(curl_, CURLOPT_URL, uri.c_str());
   110    ::curl_easy_setopt(curl_, CURLOPT_POST, post ? 1L : 0L);
   111    ::curl_easy_setopt(curl_, CURLOPT_READFUNCTION, CurlReadCallback);
   112    ::curl_easy_setopt(curl_, CURLOPT_READDATA, this);
   113    ::curl_easy_setopt(curl_, CURLOPT_WRITEFUNCTION, CurlWriteCallback);
   114    ::curl_easy_setopt(curl_, CURLOPT_WRITEDATA, this);
   115    ::curl_slist* headers = nullptr;
   116    if (post) {
   117      headers = ::curl_slist_append(headers, "Content-Type: application/json");
   118      ::curl_easy_setopt(curl_, CURLOPT_HTTPHEADER, headers);
   119      ::curl_easy_setopt(curl_, CURLOPT_POSTFIELDSIZE, request.size());
   120    }
   121  
   122    ::CURLcode res = ::curl_easy_perform(curl_);
   123    ::curl_slist_free_all(headers);
   124  
   125    if (res) {
   126      LOG(ERROR) << "(uri: " << uri << "): " << ::curl_easy_strerror(res);
   127      return false;
   128    }
   129  
   130    long response_code;
   131    res = ::curl_easy_getinfo(curl_, CURLINFO_RESPONSE_CODE, &response_code);
   132    if (res) {
   133      LOG(ERROR) << "(uri: " << uri << "): " << ::curl_easy_strerror(res);
   134      return false;
   135    }
   136    if (response_code != 200) {
   137      LOG(ERROR) << "(uri: " << uri << "): response " << response_code;
   138      return false;
   139    }
   140    if (response) {
   141      *response = received_;
   142    }
   143    return true;
   144  }
   145  
   146  bool XrefsJsonClient::Roundtrip(const std::string& endpoint,
   147                                  const google::protobuf::Message& request,
   148                                  google::protobuf::Message* response,
   149                                  std::string* error_text) {
   150    std::string request_json;
   151    if (!WriteMessageAsJsonToString(request, &request_json)) {
   152      if (error_text) {
   153        *error_text = "Couldn't serialize message.";
   154      }
   155      return false;
   156    }
   157    std::string response_buffer;
   158    if (!client_->Request(endpoint, true, request_json, &response_buffer)) {
   159      if (error_text) {
   160        *error_text = "Network client error.";
   161      }
   162      return false;
   163    }
   164    if (response) {
   165      google::protobuf::io::ArrayInputStream stream(response_buffer.data(),
   166                                                    response_buffer.size());
   167      google::protobuf::io::CodedInputStream coded_stream(&stream);
   168      if (!response->ParseFromCodedStream(&coded_stream)) {
   169        if (error_text) {
   170          *error_text = "Error decoding response protobuf.";
   171        }
   172        return false;
   173      }
   174    }
   175    return true;
   176  }
   177  }  // namespace kythe