/*** * ==++== * * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * ==--== * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ * * HTTP Library: implementation of HTTP server API built on Windows HTTP Server APIs. * * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- ****/ #pragma once #if _WIN32_WINNT < _WIN32_WINNT_VISTA #error "Error: http server APIs are not supported in XP" #endif //_WIN32_WINNT < _WIN32_WINNT_VISTA // Windows Sockets are not code analysis clean. #pragma warning(push) #pragma warning(disable : 6386) #include <http.h> #pragma warning(pop) #include <atomic> #include <mutex> #include "cpprest/details/http_server.h" namespace web { namespace http { namespace experimental { namespace details { class http_windows_server; struct windows_request_context; /// <summary> /// Class used to wrap OVERLAPPED I/O with any HTTP I/O. /// </summary> class http_overlapped : public OVERLAPPED { public: void set_http_io_completion(std::function<void(DWORD, DWORD)> http_io_completion) { ZeroMemory(this, sizeof(OVERLAPPED)); m_http_io_completion = http_io_completion; } /// <summary> /// Callback for all I/O completions. /// </summary> static void CALLBACK io_completion_callback( PTP_CALLBACK_INSTANCE instance, PVOID context, PVOID pOverlapped, ULONG result, ULONG_PTR numberOfBytesTransferred, PTP_IO io) { CASABLANCA_UNREFERENCED_PARAMETER(io); CASABLANCA_UNREFERENCED_PARAMETER(context); CASABLANCA_UNREFERENCED_PARAMETER(instance); http_overlapped *p_http_overlapped = (http_overlapped *)pOverlapped; p_http_overlapped->m_http_io_completion(result, (DWORD) numberOfBytesTransferred); } private: std::function<void(DWORD, DWORD)> m_http_io_completion; }; /// <summary> /// Context for http request through Windows HTTP Server API. /// </summary> struct windows_request_context : http::details::_http_server_context { windows_request_context(); virtual ~windows_request_context(); // Asynchronously starts processing the current request. void async_process_request(HTTP_REQUEST_ID request_id, http::http_request msg, const unsigned long headers_size); // Dispatch request to the provided http_listener. void dispatch_request_to_listener(_In_ web::http::experimental::listener::details::http_listener_impl *pListener); // Read in a portion of the request body. void read_request_body_chunk(); // Start processing the response. void async_process_response(); void transmit_body(); // Read request headers io completion callback function . void read_headers_io_completion(DWORD error_code, DWORD bytes_read); // Read request body io completion callback function. void read_body_io_completion(DWORD error_code, DWORD bytes_read); // Send response io completion callback function . void send_response_io_completion(DWORD error_code, DWORD bytes_read); // Send response body io completion callback function. void send_response_body_io_completion(DWORD error_code, DWORD bytes_read); // Cancel request io completion callback function. void cancel_request_io_completion(DWORD error_code, DWORD bytes_read); // TCE that indicates the completion of response // Workaround for ppl task_completion_event bug. std::mutex m_responseCompletedLock; pplx::task_completion_event<void> m_response_completed; // Id of the currently processed request on this connection. HTTP_REQUEST_ID m_request_id; bool m_sending_in_chunks; bool m_transfer_encoding; size_t m_remaining_to_write; HTTP_REQUEST *m_request; std::unique_ptr<unsigned char[]> m_request_buffer; std::unique_ptr<HTTP_UNKNOWN_HEADER []> m_headers; std::vector<std::string> m_headers_buffer; http_overlapped m_overlapped; http_request m_msg; http_response m_response; std::exception_ptr m_except_ptr; private: windows_request_context(const windows_request_context &); windows_request_context& operator=(const windows_request_context &); // Sends entity body chunk. void send_entity_body(_In_reads_(data_length) unsigned char * data, _In_ size_t data_length); // Cancels this request. void cancel_request(std::exception_ptr except_ptr); std::vector<unsigned char> m_body_data; }; /// <summary> /// Class to implement HTTP server API on Windows. /// </summary> class http_windows_server : public http_server { public: /// <summary> /// Constructs a http_windows_server. /// </summary> http_windows_server(); /// <summary> /// Releases resources held. /// </summary> ~http_windows_server(); /// <summary> /// Start listening for incoming requests. /// </summary> virtual pplx::task<void> start(); /// <summary> /// Registers an http listener. /// </summary> virtual pplx::task<void> register_listener(_In_ web::http::experimental::listener::details::http_listener_impl *pListener); /// <summary> /// Unregisters an http listener. /// </summary> virtual pplx::task<void> unregister_listener(_In_ web::http::experimental::listener::details::http_listener_impl *pListener); /// <summary> /// Stop processing and listening for incoming requests. /// </summary> virtual pplx::task<void> stop(); /// <summary> /// Asynchronously sends the specified http response. /// </summary> /// <param name="response">The http_response to send.</param> /// <returns>A operation which is completed once the response has been sent.</returns> virtual pplx::task<void> respond(http::http_response response); private: friend struct details::windows_request_context; // Structure to hold each registered listener. class listener_registration { public: listener_registration(HTTP_URL_GROUP_ID urlGroupId) : m_urlGroupId(urlGroupId) {} // URL group id for this listener. Each listener needs it own URL group // because configuration like timeouts, authentication, etc... HTTP_URL_GROUP_ID m_urlGroupId; // Request handler lock to guard against removing a listener while in user code. pplx::extensibility::reader_writer_lock_t m_requestHandlerLock; }; // Registered listeners pplx::extensibility::reader_writer_lock_t _M_listenersLock; std::unordered_map<web::http::experimental::listener::details::http_listener_impl *, std::unique_ptr<listener_registration>> _M_registeredListeners; // HTTP Server API server session id. HTTP_SERVER_SESSION_ID m_serverSessionId; // Tracks the number of outstanding requests being processed. std::atomic<int> m_numOutstandingRequests; pplx::extensibility::event_t m_zeroOutstandingRequests; // Handle to HTTP Server API request queue. HANDLE m_hRequestQueue; // Threadpool I/O structure for overlapped I/O. TP_IO * m_threadpool_io; // Task which actually handles receiving requests from HTTP Server API request queue. pplx::task<void> m_receivingTask; void receive_requests(); }; } // namespace details; } // namespace experimental }} // namespace web::http