1 /*
2 * Copyright 2006-2008 The FLWOR Foundation.
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 #include <curl/curl.h>
17 #include <map>
18 #include <zorba/zorba.h>
19 #include <zorba/serializer.h>
20 #include <zorba/external_module.h>
21 #include <zorba/function.h>
22 #include <zorba/empty_sequence.h>
23 #include <zorba/user_exception.h>
24
25 #include "http_request_handler.h"
26 #include "request_parser.h"
27 #include "http_response_handler.h"
28 #include "http_response_parser.h"
29
30 #ifdef WIN32
31 # include <Windows.h>
32 # define MAX_BUF_SIZE 2048
33 #endif
34
35 namespace zorba {
36
37 namespace http_client {
38 #ifdef WIN32
set_cacert(CURL * lCurl,std::string aPath)39 static void set_cacert(CURL* lCurl, std::string aPath) {
40 TCHAR path[MAX_BUF_SIZE];
41 int r = GetModuleFileName(NULL, path, 2048);
42 if (r == -1)
43 return;
44 # ifdef UNICODE
45 char buf[MAX_BUF_SIZE];
46 memset(buf, 0, MAX_BUF_SIZE);
47 for (int i = 0; i <= r; ++i) {
48 buf[i] = (char) path[i];
49 }
50 std::string lPath(buf);
51 # else
52 std::string lPath(path);
53 # endif
54 aPath = lPath.substr(0, lPath.rfind('\\'));
55 aPath += "\\cacert.pem";
56 if(GetFileAttributesA(aPath.c_str()) != INVALID_FILE_ATTRIBUTES)
57 curl_easy_setopt(lCurl, CURLOPT_CAINFO, aPath.c_str());
58 else
59 curl_easy_setopt(lCurl, CURLOPT_SSL_VERIFYPEER, 0L);
60 }
61 #endif //WIN32
62
63 class HttpSendFunction : public ContextualExternalFunction {
64 protected:
65 const ExternalModule* theModule;
66 ItemFactory* theFactory;
67
68 public:
HttpSendFunction(const ExternalModule * aModule)69 HttpSendFunction(const ExternalModule* aModule)
70 : theModule(aModule),
71 theFactory(Zorba::getInstance(0)->getItemFactory()) {}
72
~HttpSendFunction()73 virtual ~HttpSendFunction() {}
74
75 public:
76 virtual String
getURI() const77 getURI() const { return theModule->getURI(); }
78
79 virtual String
getLocalName() const80 getLocalName() const { return "http-sequential-impl"; }
81
82 virtual ItemSequence_t
83 evaluate(const ExternalFunction::Arguments_t& args,
84 const StaticContext* aStaticContext, const DynamicContext* aDynamicContext)
85 const;
86 };
87
88 class HttpReadFunction : public HttpSendFunction {
89 public:
HttpReadFunction(const ExternalModule * aModule)90 HttpReadFunction(const ExternalModule* aModule)
91 : HttpSendFunction(aModule) {}
92
~HttpReadFunction()93 virtual ~HttpReadFunction() {}
94
95 public:
96 virtual String
getLocalName() const97 getLocalName() const { return "http-nondeterministic-impl"; }
98
99 };
100
101 class HttpClientModule : public ExternalModule {
102 protected:
103 class ltstr
104 {
105 public:
operator ()(const String & s1,const String & s2) const106 bool operator()(const String& s1, const String& s2) const
107 {
108 return s1.compare(s2) < 0;
109 }
110 };
111
112 typedef std::map<String, ExternalFunction*, ltstr> FuncMap_t;
113
114 FuncMap_t theFunctions;
115
116 public:
117 virtual ~HttpClientModule();
118
HttpClientModule()119 HttpClientModule() : theModuleUri("http://www.zorba-xquery.com/modules/http-client")
120 {
121 for (FuncMap_t::const_iterator lIter = theFunctions.begin();
122 lIter != theFunctions.end(); ++lIter) {
123 delete lIter->second;
124 }
125 theFunctions.clear();
126 }
127
128 virtual String
getURI() const129 getURI() const { return theModuleUri; }
130
131 virtual ExternalFunction*
getExternalFunction(const String & aLocalname)132 getExternalFunction(const String& aLocalname)
133 {
134 ExternalFunction*& lFunc = theFunctions[aLocalname];
135 if (!lFunc) {
136 if (aLocalname == "http-sequential-impl") {
137 lFunc = new HttpSendFunction(this);
138 } else if (aLocalname == "http-nondeterministic-impl") {
139 lFunc = new HttpReadFunction(this);
140 }
141 }
142 return lFunc;
143 }
144
145 virtual void
destroy()146 destroy()
147 {
148 if (!dynamic_cast<HttpClientModule*>(this)) {
149 return;
150 }
151 delete this;
152 }
153
154 private:
155 String theModuleUri;
156 };
157
158 ItemSequence_t
general_evaluate(const ExternalFunction::Arguments_t & args,const StaticContext * aStaticContext,const DynamicContext * aDynamicContext,ItemFactory * aFactory)159 general_evaluate(
160 const ExternalFunction::Arguments_t& args,
161 const StaticContext* aStaticContext,
162 const DynamicContext* aDynamicContext,
163 ItemFactory* aFactory)
164 {
165 CURL* lCURL = curl_easy_init();
166
167 Item lRequest;
168 Item lHref;
169 Item lContent;
170
171 Iterator_t arg0_iter = args[0]->getIterator();
172 arg0_iter->open();
173 bool lReqSet = arg0_iter->next(lRequest);
174 arg0_iter->close();
175 Iterator_t arg1_iter = args[1]->getIterator();
176 arg1_iter->open();
177 bool lHrefSet = arg1_iter->next(lHref);
178 arg1_iter->close();
179
180 std::string lData;
181
182 std::auto_ptr<HttpRequestHandler> lHandler;
183 std::auto_ptr<RequestParser> lParser;
184 struct curl_slist* lHeaderList = 0;
185
186 ErrorThrower thrower(aFactory, &lHeaderList);
187
188 if (lReqSet) {
189 lHandler.reset(new HttpRequestHandler(lCURL, args[2]));
190 lParser.reset(new RequestParser(lHandler.get(), thrower));
191 lParser->parse(lRequest);
192 }
193 if (lHrefSet) {
194 curl_easy_setopt(lCURL, CURLOPT_URL, lHref.getStringValue().c_str());
195 }
196 curl_easy_setopt(lCURL, CURLOPT_USERAGENT, "libcurl-agent/1.0");
197 //curl_easy_setopt(lCURL, CURLOPT_PROXY, "localhost:8888");
198 #ifdef WIN32
199 std::string caCertPath;
200 set_cacert(lCURL, caCertPath);
201 #endif
202 HttpResponseHandler lRespHandler(aFactory, lHeaderList);
203 String lOverrideContentType;
204 if (lHandler.get())
205 lHandler->getOverrideContentType(lOverrideContentType);
206 bool lStatusOnly =
207 lHandler.get() == NULL ? false : (lHandler->isStatusOnly() || lHandler->isHeadRequest());
208 // This gives the ownership of lCurl to the HttpResponseParser
209 std::auto_ptr<HttpResponseParser> lRespParser(new HttpResponseParser(lRespHandler, lCURL, thrower,
210 lOverrideContentType.c_str(), lStatusOnly));
211 int lRetCode = lRespParser->parse();
212
213 if (lRetCode) {
214 thrower.raiseException("http://expath.org/ns/error", "HC001", "An HTTP error occurred");
215 }
216
217 // If the Parser is "self contained", that means it didn't create any
218 // objects with a lifecycle longer than itself; therefore we should free
219 // it (by letting auto_ptr delete it). If the Parser is not self contained,
220 // then it will have arranged for some other memory manager to free it
221 // later when appropriate; therefore we should NOT let auto_ptr delete it
222 // now.
223 if ( ! lRespParser->selfContained()) {
224 lRespParser.release();
225 }
226 return ItemSequence_t(lRespHandler.releaseResult());
227 }
228
229 ItemSequence_t
evaluate(const ExternalFunction::Arguments_t & args,const StaticContext * aStaticContext,const DynamicContext * aDynamicContext) const230 HttpSendFunction::evaluate(const ExternalFunction::Arguments_t& args,
231 const StaticContext* aStaticContext, const DynamicContext* aDynamicContext) const
232 {
233 return general_evaluate(args, aStaticContext, aDynamicContext, theFactory);
234 }
235
~HttpClientModule()236 HttpClientModule::~HttpClientModule()
237 {
238 for (FuncMap_t::const_iterator lIter = theFunctions.begin();
239 lIter != theFunctions.end(); ++lIter) {
240 delete lIter->second;
241 }
242 theFunctions.clear();
243 }
244 } // namespace http_request
245 } // namespace zorba
246
247 #ifdef WIN32
248 # define DLL_EXPORT __declspec(dllexport)
249 #else
250 # define DLL_EXPORT __attribute__ ((visibility("default")))
251 #endif
252
createModule()253 extern "C" DLL_EXPORT zorba::ExternalModule* createModule() {
254 return new zorba::http_client::HttpClientModule();
255 }
256
257