1 /*
2  * Copyright 2008-2018 Max Kellermann <max.kellermann@gmail.com>
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  * - Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *
11  * - Redistributions in binary form must reproduce the above copyright
12  * notice, this list of conditions and the following disclaimer in the
13  * documentation and/or other materials provided with the
14  * distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
19  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
20  * FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
23  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
25  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
27  * OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29 
30 #ifndef CURL_REQUEST_HXX
31 #define CURL_REQUEST_HXX
32 
33 #include "Easy.hxx"
34 
35 #include <map>
36 #include <string>
37 
38 struct StringView;
39 class CurlGlobal;
40 class CurlResponseHandler;
41 
42 class CurlRequest final {
43 	CurlGlobal &global;
44 
45 	CurlResponseHandler &handler;
46 
47 	/** the curl handle */
48 	CurlEasy easy;
49 
50 	enum class State {
51 		HEADERS,
52 		BODY,
53 		CLOSED,
54 	} state = State::HEADERS;
55 
56 	std::multimap<std::string, std::string> headers;
57 
58 	/** error message provided by libcurl */
59 	char error_buffer[CURL_ERROR_SIZE];
60 
61 	bool registered = false;
62 
63 public:
64 	/**
65 	 * To start sending the request, call Start().
66 	 */
67 	CurlRequest(CurlGlobal &_global,
68 		    CurlResponseHandler &_handler);
69 
CurlRequest(CurlGlobal & _global,const char * url,CurlResponseHandler & _handler)70 	CurlRequest(CurlGlobal &_global, const char *url,
71 		    CurlResponseHandler &_handler)
72 		:CurlRequest(_global, _handler) {
73 		SetUrl(url);
74 	}
75 
76 	~CurlRequest() noexcept;
77 
78 	CurlRequest(const CurlRequest &) = delete;
79 	CurlRequest &operator=(const CurlRequest &) = delete;
80 
81 	/**
82 	 * Register this request via CurlGlobal::Add(), which starts
83 	 * the request.
84 	 *
85 	 * This method must be called in the event loop thread.
86 	 */
87 	void Start();
88 
89 	/**
90 	 * A thread-safe version of Start().
91 	 */
92 	void StartIndirect();
93 
94 	/**
95 	 * Unregister this request via CurlGlobal::Remove().
96 	 *
97 	 * This method must be called in the event loop thread.
98 	 */
99 	void Stop() noexcept;
100 
101 	/**
102 	 * A thread-safe version of Stop().
103 	 */
104 	void StopIndirect();
105 
Get()106 	CURL *Get() noexcept {
107 		return easy.Get();
108 	}
109 
110 	template<typename T>
SetOption(CURLoption option,T value)111 	void SetOption(CURLoption option, T value) {
112 		easy.SetOption(option, value);
113 	}
114 
SetUrl(const char * url)115 	void SetUrl(const char *url) {
116 		easy.SetURL(url);
117 	}
118 
SetRequestHeaders(struct curl_slist * request_headers)119 	void SetRequestHeaders(struct curl_slist *request_headers) {
120 		easy.SetRequestHeaders(request_headers);
121 	}
122 
SetVerifyHost(bool value)123 	void SetVerifyHost(bool value) {
124 		easy.SetVerifyHost(value);
125 	}
126 
SetVerifyPeer(bool value)127 	void SetVerifyPeer(bool value) {
128 		easy.SetVerifyPeer(value);
129 	}
130 
SetNoBody(bool value=true)131 	void SetNoBody(bool value=true) {
132 		easy.SetNoBody(value);
133 	}
134 
SetPost(bool value=true)135 	void SetPost(bool value=true) {
136 		easy.SetPost(value);
137 	}
138 
SetRequestBody(const void * data,size_t size)139 	void SetRequestBody(const void *data, size_t size) {
140 		easy.SetRequestBody(data, size);
141 	}
142 
143 	void Resume() noexcept;
144 
145 	/**
146 	 * A HTTP request is finished.  Called by #CurlGlobal.
147 	 */
148 	void Done(CURLcode result) noexcept;
149 
150 private:
151 	/**
152 	 * Frees the current "libcurl easy" handle, and everything
153 	 * associated with it.
154 	 */
155 	void FreeEasy() noexcept;
156 
157 	void FinishHeaders();
158 	void FinishBody();
159 
160 	size_t DataReceived(const void *ptr, size_t size) noexcept;
161 
162 	void HeaderFunction(StringView s) noexcept;
163 
164 	/** called by curl when new data is available */
165 	static size_t _HeaderFunction(char *ptr, size_t size, size_t nmemb,
166 				      void *stream) noexcept;
167 
168 	/** called by curl when new data is available */
169 	static size_t WriteFunction(char *ptr, size_t size, size_t nmemb,
170 				    void *stream) noexcept;
171 };
172 
173 #endif
174