// Copyright 2014 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef LIBBRILLO_BRILLO_HTTP_HTTP_UTILS_H_
#define LIBBRILLO_BRILLO_HTTP_HTTP_UTILS_H_

#include <string>
#include <utility>
#include <vector>

#include <brillo/brillo_export.h>
#include <brillo/errors/error.h>
#include <brillo/http/http_form_data.h>
#include <brillo/http/http_request.h>

namespace base {
class Value;
class DictionaryValue;
}  // namespace base

namespace brillo {
namespace http {

using FormFieldList = std::vector<std::pair<std::string, std::string>>;

////////////////////////////////////////////////////////////////////////////////
// The following are simple utility helper functions for common HTTP operations
// that use http::Request object behind the scenes and set it up accordingly.
// The values for request method, data MIME type, request header names should
// not be directly encoded in most cases, but use predefined constants from
// http_request.h.
// So, instead of calling:
//    SendRequestAndBlock("POST",
//                        "http://url",
//                        "data", 4,
//                        "text/plain",
//                        {{"Authorization", "Bearer TOKEN"}},
//                        transport, error);
// You should do use this instead:
//    SendRequestAndBlock(brillo::http::request_type::kPost,
//                        "http://url",
//                        "data", 4,
//                        brillo::mime::text::kPlain,
//                        {{brillo::http::request_header::kAuthorization,
//                          "Bearer TOKEN"}},
//                        transport, error);
//
// For more advanced functionality you need to use Request/Response objects
// directly.
////////////////////////////////////////////////////////////////////////////////

// Performs a generic HTTP request with binary data. Success status,
// returned data and additional information (such as returned HTTP headers)
// can be obtained from the returned Response object.
BRILLO_EXPORT std::unique_ptr<Response> SendRequestAndBlock(
    const std::string& method,
    const std::string& url,
    const void* data,
    size_t data_size,
    const std::string& mime_type,
    const HeaderList& headers,
    std::shared_ptr<Transport> transport,
    brillo::ErrorPtr* error);

// Same as above, but without sending the request body.
// This is especially useful for requests like "GET" and "HEAD".
BRILLO_EXPORT std::unique_ptr<Response> SendRequestWithNoDataAndBlock(
    const std::string& method,
    const std::string& url,
    const HeaderList& headers,
    std::shared_ptr<Transport> transport,
    brillo::ErrorPtr* error);

// Same as above but asynchronous. On success, |success_callback| is called
// with the response object. On failure, |error_callback| is called with the
// error details.
// Returns the ID of the request which can be used to cancel the pending
// request using Transport::CancelRequest().
BRILLO_EXPORT RequestID SendRequest(
    const std::string& method,
    const std::string& url,
    StreamPtr stream,
    const std::string& mime_type,
    const HeaderList& headers,
    std::shared_ptr<Transport> transport,
    const SuccessCallback& success_callback,
    const ErrorCallback& error_callback);

// Same as above, but takes a memory buffer. The pointer should be valid only
// until the function returns. The data is copied into an internal buffer to be
// available for the duration of the asynchronous operation.
// Returns the ID of the request which can be used to cancel the pending
// request using Transport::CancelRequest().
BRILLO_EXPORT RequestID SendRequest(
    const std::string& method,
    const std::string& url,
    const void* data,
    size_t data_size,
    const std::string& mime_type,
    const HeaderList& headers,
    std::shared_ptr<Transport> transport,
    const SuccessCallback& success_callback,
    const ErrorCallback& error_callback);

// Asynchronous version of SendRequestNoData().
// Returns the ID of the request which can be used to cancel the pending
// request using Transport::CancelRequest().
BRILLO_EXPORT RequestID SendRequestWithNoData(
    const std::string& method,
    const std::string& url,
    const HeaderList& headers,
    std::shared_ptr<Transport> transport,
    const SuccessCallback& success_callback,
    const ErrorCallback& error_callback);

// Performs a GET request. Success status, returned data and additional
// information (such as returned HTTP headers) can be obtained from
// the returned Response object.
BRILLO_EXPORT std::unique_ptr<Response> GetAndBlock(
    const std::string& url,
    const HeaderList& headers,
    std::shared_ptr<Transport> transport,
    brillo::ErrorPtr* error);

// Asynchronous version of http::Get().
// Returns the ID of the request which can be used to cancel the pending
// request using Transport::CancelRequest().
BRILLO_EXPORT RequestID Get(
    const std::string& url,
    const HeaderList& headers,
    std::shared_ptr<Transport> transport,
    const SuccessCallback& success_callback,
    const ErrorCallback& error_callback);

// Performs a HEAD request. Success status and additional
// information (such as returned HTTP headers) can be obtained from
// the returned Response object.
BRILLO_EXPORT std::unique_ptr<Response> HeadAndBlock(
    const std::string& url,
    std::shared_ptr<Transport> transport,
    brillo::ErrorPtr* error);

// Performs an asynchronous HEAD request.
// Returns the ID of the request which can be used to cancel the pending
// request using Transport::CancelRequest().
BRILLO_EXPORT RequestID Head(
    const std::string& url,
    std::shared_ptr<Transport> transport,
    const SuccessCallback& success_callback,
    const ErrorCallback& error_callback);

// Performs a POST request with binary data. Success status, returned data
// and additional information (such as returned HTTP headers) can be obtained
// from the returned Response object.
BRILLO_EXPORT std::unique_ptr<Response> PostBinaryAndBlock(
    const std::string& url,
    const void* data,
    size_t data_size,
    const std::string& mime_type,
    const HeaderList& headers,
    std::shared_ptr<Transport> transport,
    brillo::ErrorPtr* error);

// Async version of PostBinary().
// Returns the ID of the request which can be used to cancel the pending
// request using Transport::CancelRequest().
BRILLO_EXPORT RequestID PostBinary(
    const std::string& url,
    StreamPtr stream,
    const std::string& mime_type,
    const HeaderList& headers,
    std::shared_ptr<Transport> transport,
    const SuccessCallback& success_callback,
    const ErrorCallback& error_callback);

// Same as above, but takes a memory buffer. The pointer should be valid only
// until the function returns. The data is copied into an internal buffer
// to be available for the duration of the asynchronous operation.
// Returns the ID of the request which can be used to cancel the pending
// request using Transport::CancelRequest().
BRILLO_EXPORT RequestID PostBinary(
    const std::string& url,
    const void* data,
    size_t data_size,
    const std::string& mime_type,
    const HeaderList& headers,
    std::shared_ptr<Transport> transport,
    const SuccessCallback& success_callback,
    const ErrorCallback& error_callback);

// Performs a POST request with text data. Success status, returned data
// and additional information (such as returned HTTP headers) can be obtained
// from the returned Response object.
BRILLO_EXPORT std::unique_ptr<Response> PostTextAndBlock(
    const std::string& url,
    const std::string& data,
    const std::string& mime_type,
    const HeaderList& headers,
    std::shared_ptr<Transport> transport,
    brillo::ErrorPtr* error);

// Async version of PostText().
// Returns the ID of the request which can be used to cancel the pending
// request using Transport::CancelRequest().
BRILLO_EXPORT RequestID PostText(
    const std::string& url,
    const std::string& data,
    const std::string& mime_type,
    const HeaderList& headers,
    std::shared_ptr<Transport> transport,
    const SuccessCallback& success_callback,
    const ErrorCallback& error_callback);

// Performs a POST request with form data. Success status, returned data
// and additional information (such as returned HTTP headers) can be obtained
// from the returned Response object. The form data is a list of key/value
// pairs. The data is posed as "application/x-www-form-urlencoded".
BRILLO_EXPORT std::unique_ptr<Response> PostFormDataAndBlock(
    const std::string& url,
    const FormFieldList& data,
    const HeaderList& headers,
    std::shared_ptr<Transport> transport,
    brillo::ErrorPtr* error);

// Async version of PostFormData() above.
// Returns the ID of the request which can be used to cancel the pending
// request using Transport::CancelRequest().
BRILLO_EXPORT RequestID PostFormData(
    const std::string& url,
    const FormFieldList& data,
    const HeaderList& headers,
    std::shared_ptr<Transport> transport,
    const SuccessCallback& success_callback,
    const ErrorCallback& error_callback);

// Performs a POST request with form data, including binary file uploads.
// Success status, returned data and additional information (such as returned
// HTTP headers) can be obtained from the returned Response object.
// The data is posed as "multipart/form-data".
BRILLO_EXPORT std::unique_ptr<Response> PostFormDataAndBlock(
    const std::string& url,
    std::unique_ptr<FormData> form_data,
    const HeaderList& headers,
    std::shared_ptr<Transport> transport,
    brillo::ErrorPtr* error);

// Async version of PostFormData() above.
// Returns the ID of the request which can be used to cancel the pending
// request using Transport::CancelRequest().
BRILLO_EXPORT RequestID PostFormData(
    const std::string& url,
    std::unique_ptr<FormData> form_data,
    const HeaderList& headers,
    std::shared_ptr<Transport> transport,
    const SuccessCallback& success_callback,
    const ErrorCallback& error_callback);

// Performs a POST request with JSON data. Success status, returned data
// and additional information (such as returned HTTP headers) can be obtained
// from the returned Response object. If a JSON response is expected,
// use ParseJsonResponse() method on the returned Response object.
BRILLO_EXPORT std::unique_ptr<Response> PostJsonAndBlock(
    const std::string& url,
    const base::Value* json,
    const HeaderList& headers,
    std::shared_ptr<Transport> transport,
    brillo::ErrorPtr* error);

// Async version of PostJson().
// Returns the ID of the request which can be used to cancel the pending
// request using Transport::CancelRequest().
BRILLO_EXPORT RequestID PostJson(
    const std::string& url,
    std::unique_ptr<base::Value> json,
    const HeaderList& headers,
    std::shared_ptr<Transport> transport,
    const SuccessCallback& success_callback,
    const ErrorCallback& error_callback);

// Performs a PATCH request with JSON data. Success status, returned data
// and additional information (such as returned HTTP headers) can be obtained
// from the returned Response object. If a JSON response is expected,
// use ParseJsonResponse() method on the returned Response object.
BRILLO_EXPORT std::unique_ptr<Response> PatchJsonAndBlock(
    const std::string& url,
    const base::Value* json,
    const HeaderList& headers,
    std::shared_ptr<Transport> transport,
    brillo::ErrorPtr* error);

// Async version of PatchJson().
// Returns the ID of the request which can be used to cancel the pending
// request using Transport::CancelRequest().
BRILLO_EXPORT RequestID PatchJson(
    const std::string& url,
    std::unique_ptr<base::Value> json,
    const HeaderList& headers,
    std::shared_ptr<Transport> transport,
    const SuccessCallback& success_callback,
    const ErrorCallback& error_callback);

// Given an http::Response object, parse the body data into Json object.
// Returns null if failed. Optional |error| can be passed in to
// get the extended error information as to why the parse failed.
BRILLO_EXPORT std::unique_ptr<base::DictionaryValue> ParseJsonResponse(
    Response* response,
    int* status_code,
    brillo::ErrorPtr* error);

// Converts a request header name to canonical form (lowercase with uppercase
// first letter and each letter after a hyphen ('-')).
// "content-TYPE" will be converted to "Content-Type".
BRILLO_EXPORT std::string GetCanonicalHeaderName(const std::string& name);

}  // namespace http
}  // namespace brillo

#endif  // LIBBRILLO_BRILLO_HTTP_HTTP_UTILS_H_