1 // This may look like C code, but it's really -*- C++ -*- 2 /* 3 * Copyright (C) 2008 Emweb bv, Herent, Belgium. 4 * 5 * See the LICENSE file for terms of use. 6 */ 7 #ifndef WRESOURCE_H_ 8 #define WRESOURCE_H_ 9 10 #include <Wt/WObject.h> 11 #include <Wt/WGlobal.h> 12 #include <Wt/WSignal.h> 13 #include <Wt/WString.h> 14 15 #include <iostream> 16 #include <condition_variable> 17 #ifdef WT_THREADED 18 #include <mutex> 19 #endif // WT_THREADED 20 21 namespace Wt { 22 23 class WebRequest; 24 class WebResponse; 25 class WebSession; 26 27 namespace Http { 28 class Request; 29 class Response; 30 class ResponseContinuation; 31 32 typedef std::shared_ptr<ResponseContinuation> ResponseContinuationPtr; 33 } 34 35 /*! \brief Values for the disposition type in the Content-Disposition header 36 */ 37 enum class ContentDisposition { 38 None, //!< Do not specify a disposition type 39 Attachment, //!< Open with a helper application or show 'Save As' dialog 40 Inline //!< View with a browser plugin 41 }; 42 43 /*! \class WResource Wt/WResource.h Wt/WResource.h 44 * \brief An object which can be rendered in the HTTP protocol. 45 * 46 * <h3>Usage</h3> 47 * 48 * Besides the main page, other objects may be rendered as additional 49 * resources, for example documents or images. Classes such as WAnchor 50 * or WImage can use a resource instead of a URL to provide their 51 * contents. Whenever the resource has changed, you should call the 52 * setChanged() method. setChanged() will make sure that the browser will 53 * use a new version of the resource by generating a new URL, and emits the 54 * dataChanged() signal to make those that refer to the resource aware 55 * that they should update their references to the new URL. 56 * 57 * You can help the browser to start a suitable helper application to 58 * handle the downloaded resource, or suggest to the user a suitable 59 * filename for saving the resource, by setting an appropriate file 60 * name using suggestFileName(). 61 * 62 * To serve resources that you create on the fly, you need to specialize 63 * this class and implement handleRequest(). 64 * 65 * \if cpp 66 * Example for a custom resource implementation: 67 * \code 68 * class MyResource : public Wt::WResource 69 * { 70 * public: 71 * MyResource() 72 * { 73 * suggestFileName("data.txt"); 74 * } 75 * 76 * ~MyResource() { 77 * beingDeleted(); // see "Concurrency issues" below. 78 * } 79 * 80 * void handleRequest(const Wt::Http::Request& request, 81 * Wt::Http::Response& response) { 82 * response.setMimeType("plain/text"); 83 * response.out() << "I am a text file." << std::endl; 84 * } 85 * }; 86 * \endcode 87 * \endif 88 * 89 * <h3>Concurrency issues</h3> 90 * 91 * Because of the nature of the web, a resource may be requested one 92 * time or multiple times at the discretion of the browser, and 93 * therefore your resource should in general not have any side-effects 94 * except for what is needed to render its own contents. Unlike event 95 * notifications to a %Wt application, resource requests are not 96 * serialized, but are handled concurrently. You need to grab the 97 * application lock if you want to access or modify other widget state 98 * from within the resource. You can enable takesUpdateLock() to let 99 * %WResource do that for you. When deleting a resource, any pending 100 * request is cancelled first. For this mechanism to work you need to 101 * specialize the destructor and call beingDeleted(). This method may 102 * safely be called multiple times (i.e. from within each destructor 103 * in the hierachy). 104 * 105 * <h3>Continuations for asynchronous I/O</h3> 106 * 107 * With respect to I/O, the current strategy is to cache the whole 108 * response first in a buffer and use async I/O to push the data to 109 * the client, in order to free the thread while waiting for I/O on a 110 * possibly slow link. You do not necessarily have to provide all 111 * output at once, instead you can obtain a Http::ResponseContinuation 112 * object for a response, construct the response piecewise. A new 113 * request() will be made to continue the response. 114 * 115 * Example for a custom resource implementation using continuations: 116 * \code 117 class MyResource : public Wt::WResource 118 { 119 public: 120 MyResource(int iterations) 121 : iterations_(iterations) 122 { 123 suggestFileName("data.txt"); 124 } 125 126 ~MyResource() { 127 beingDeleted(); 128 } 129 130 void handleRequest(const Wt::Http::Request& request, 131 Wt::Http::Response& response) { 132 // see if this request is for a continuation: 133 Wt::Http::ResponseContinuation *continuation = request.continuation(); 134 135 // calculate the current start 136 int iteration = continuation ? Wt::cpp17::any_cast<int>(continuation->data()) : 0; 137 if (iteration == 0) 138 response.setMimeType("plain/text"); 139 140 int last = std::min(iterations_, iteration + 100); 141 for (int i = iteration; i < last; ++i) 142 response.out() << "Data item " << i << std::endl; 143 144 // if we have not yet finished, create a continuation to serve more 145 if (last < iterations_) { 146 continuation = response.createContinuation(); 147 // remember what to do next 148 continuation->setData(last); 149 } 150 } 151 152 private: 153 int iterations_; 154 }; 155 * \endcode 156 * 157 * <h3>Global and private resources</h3> 158 * 159 * By default, a resource is private to an application: access to it 160 * is protected by same secret session Id that protects any other 161 * access to the application. 162 * 163 * You can also deploy static resources, which are global, using 164 * WServer::addResource(). 165 * 166 * <h3>Monitoring upload progress</h3> 167 * 168 * A resource may also handle the uploading of files (in fact, 169 * WFileUpload uses a WResource to do exactly that) or transmission of 170 * other large bodies of data being POST'ed or PUT to the resource 171 * URL. For these requests, it may be convenient to enable upload 172 * progress monitoring using setUploadProgress(), which allows you to 173 * be notified of data being received. 174 * 175 * \sa WAnchor, WImage 176 */ 177 class WT_API WResource : public WObject 178 { 179 public: 180 /*! \brief Creates a new resource. 181 */ 182 WResource(); 183 184 /*! \brief Destroys the resource. 185 * 186 * When specializing a resource, you MUST call beingDeleted() from 187 * within the specialized destructor, in order to stop any further 188 * requests to the resource, unless it is guaranteed that no requests 189 * will arrive for this resource when it is being deleted, e.g. if 190 * the server is already stopped. 191 */ 192 ~WResource(); 193 194 /*! \brief Suggests a filename to the user for the data streamed by this 195 * resource. 196 * 197 * For resources, intended to be downloaded by the user, suggest a 198 * name used for saving. The filename extension may also help the 199 * browser to identify the correct program for opening the resource. 200 * 201 * The disposition type determines if the resource is intended to 202 * be opened by a plugin in the browser (Inline), or to be saved to disk 203 * (Attachment). NoDisposition is not a valid Content-Disposition when a 204 * filename is suggested; this will be rendered as Attachment. 205 * 206 * \sa setDispositionType(). 207 */ 208 void suggestFileName(const Wt::WString &name, 209 ContentDisposition disposition 210 = ContentDisposition::Attachment); 211 212 /*! \brief Returns the suggested file name. 213 * 214 * \sa suggestFileName(); 215 */ suggestedFileName()216 const Wt::WString& suggestedFileName() const { return suggestedFileName_; } 217 218 /*! \brief Configures the Content-Disposition header 219 * 220 * The Content-Disposition header allows to instruct the browser that a 221 * resource should be shown inline or as attachment. This function enables 222 * you to set this property. 223 * 224 * This is often used in combination with suggestFilename(). The 225 * Content-Disposition must not be None when a filename is given; 226 * if this case is encountered, None will be rendered as Attachment. 227 * 228 * \sa suggestFilename(). 229 */ 230 void setDispositionType(ContentDisposition cd); 231 232 /*! \brief Returns the currently configured content disposition 233 * 234 * \sa setDispositionType() 235 */ dispositionType()236 ContentDisposition dispositionType() const { return dispositionType_; } 237 238 /*! \brief Generates a new URL for this resource and emits the changed signal 239 * 240 * This does not work when the resource is deployed at an internal path using 241 * setInternalPath(). 242 * 243 * \sa setInvalidAfterChanged() 244 */ 245 void setChanged(); 246 247 /*! \brief Return "page not found" for prior resource URLs after change 248 * 249 * This option invalidates earlier versions of the resource url prior to 250 * the last call of setChanged() or generateUrl(). The default value is false. 251 * 252 * This does not work when the resource is deployed at an internal path using 253 * setInternalPath(). 254 * 255 * \sa setChanged(), generateUrl() 256 */ 257 void setInvalidAfterChanged(bool enabled); 258 259 /*! \brief Should "page not found" be returned for outdated resource URLs 260 * 261 * \sa setInvalidAfterChanged() 262 */ invalidAfterChanged()263 bool invalidAfterChanged() const { return invalidAfterChanged_; } 264 265 /*! \brief Sets an internal path for this resource. 266 * 267 * Using this method you can deploy the resource at a fixed path. Unless 268 * you deploy using cookies for session tracking (not recommended), a 269 * session identifier will be appended to the path. 270 * 271 * You should use internal paths that are different from internal paths 272 * handled by your application (WApplication::setInternalPath()), if you 273 * do not want a conflict between these two when the browser does not use 274 * AJAX (and thus url fragments for its internal paths). 275 * 276 * The default value is empty. By default the URL for a resource is 277 * unspecified and a URL will be generated by the library. 278 * 279 * The internal path for a static resource is the deployment path 280 * specified using WServer::addResource(). 281 */ 282 void setInternalPath(const std::string& path); 283 284 /*! \brief Returns the internal path. 285 * 286 * \sa setInternalPath(). 287 */ internalPath()288 std::string internalPath() const { return internalPath_; } 289 290 /*! \brief Generates an URL for this resource. 291 * 292 * Generates a new url that refers to this resource. The url is 293 * unique to assure that it is not cached by the web browser, and 294 * can thus be used to refer to a new "version" of the resource, 295 * which can be indicated by emitting the dataChanged() signal. 296 * 297 * The old urls are not invalidated by calling this method, unless 298 * you enable setInvalidAfterChanged(). 299 */ 300 const std::string& generateUrl(); 301 302 /*! \brief Returns the current URL for this resource. 303 * 304 * Returns the url that references this resource. 305 */ 306 const std::string& url() const; 307 308 /*! \brief %Signal emitted when the data presented in this resource 309 * has changed. 310 * 311 * Widgets that reference the resource (such as anchors and images) will 312 * make sure the new data is rendered. 313 * 314 * It is better to call setChanged() than to emit this signal. setChanged 315 * generates a new URL for this resource to avoid caching problems and then 316 * emits this signal. 317 */ dataChanged()318 Signal<>& dataChanged() { return dataChanged_; } 319 320 /*! \brief Indicate interest in upload progress. 321 * 322 * When supported, you can track upload progress using this 323 * signal. While data is being received, and before handleRequest() 324 * is called, progress information is indicated using 325 * dataReceived(). 326 * 327 * We envision that in the future support will depend on a 328 * combination of browser and connector. Currently only the wthttp 329 * connector provides support for this across all AJAX browsers. In 330 * the future, we are likely to implement this also using JavaScript 331 * File API features making it independent of connectors. 332 * 333 * The default value is \c false. 334 */ 335 void setUploadProgress(bool enabled); 336 337 /*! \brief %Signal emitted when data has been received for this resource. 338 * 339 * When this signal is emitted, you have the update lock to modify 340 * the application. Because there is however no corresponding 341 * request from the browser, any update to the user interface is not 342 * immediately reflected in the client. To update the client 343 * interface, you need to use a WTimer or enable \link 344 * WApplication::enableUpdates() server-push\endlink. 345 * 346 * The first argument is the number of bytes received so far, 347 * and the second argument is the total number of bytes. 348 * 349 * \sa setUploadProgress() 350 */ dataReceived()351 Signal< ::uint64_t, ::uint64_t>& dataReceived() { return dataReceived_; } 352 dataExceeded()353 Signal< ::uint64_t >& dataExceeded() { return dataExceeded_; } 354 355 /*! \brief Stream the resource to a stream. 356 * 357 * This is a convenience method to serialize to a stream (for 358 * example a file stream). 359 */ 360 void write(WT_BOSTREAM& out, 361 const Http::ParameterMap& parameters = Http::ParameterMap(), 362 const Http::UploadedFileMap& files = Http::UploadedFileMap()); 363 364 /*! \brief Handles a request. 365 * 366 * Reimplement this method so that a proper response is generated 367 * for the given request. From the \p request object you can 368 * access request parameters and whether the request is a 369 * continuation request. In the \p response object, you should 370 * set the mime type and stream the output data. 371 * 372 * A request may also concern a continuation, indicated in 373 * Http::Request::continuation(), in which case the next part for a 374 * previously created continuation should be served. 375 * 376 * While handling a request, which may happen at any time together 377 * with event handling, the library makes sure that the resource is 378 * not being concurrently deleted, but multiple requests may happend 379 * simultaneously for a single resource. 380 */ 381 virtual void handleRequest(const Http::Request& request, 382 Http::Response& response) = 0; 383 384 /*! \brief Handles a continued request being aborted. 385 * 386 * This function is only applicable to a request for which a 387 * response continuation was created, and indicates that the client 388 * has been closed (or the resource is being deleted) before the 389 * response was completed. This function may be reimplemented so 390 * that you can close any resources associated with the response 391 * continuation. 392 * 393 * The base implementation is empty. 394 * 395 * Note that because this function could be called from within the 396 * destructor, you should not forget to call beingDeleted() from the 397 * specialized destructor of your resource. 398 */ 399 virtual void handleAbort(const Http::Request& request); 400 401 /*! \brief Indicate that more data is available. 402 * 403 * In some occasions, data may be requested for a resource which is 404 * currently not yet available. Then you can suspend the response 405 * using a continuation which you let wait for more data. 406 * 407 * Using this method you can indicate that more data is 408 * available. This will resume all responses that are currently 409 * waiting for more data. 410 * 411 * If no responses are currently waiting for data, then this method 412 * has no effect. 413 * 414 * \sa ResponseContinuation::waitForMoreData() 415 */ 416 void haveMoreData(); 417 418 /*! \brief Set whether this resource takes the %WApplication's update lock 419 * 420 * By default, %WResource does not keep the WApplication's update lock, 421 * so handleRequest() is done outside of the %WApplication's event loop. 422 * This makes sure that resources don't block the event loop, and multiple 423 * requests to a %WResource can be handled concurrently. 424 * 425 * However, if necessary you can either manually grab the update lock, 426 * (see WApplication::UpdateLock) or enable this option. 427 * 428 * \note This is not applicable to static resources, since static resources 429 * do not have a %WApplication associated with them. 430 */ 431 void setTakesUpdateLock(bool enabled); 432 433 /*! \brief Returns whether this resources takes the %WApplication's update lock 434 * 435 * \sa setTakesUpdateLock() 436 */ takesUpdateLock()437 bool takesUpdateLock() const { return takesUpdateLock_; } 438 439 unsigned long version() const; 440 441 void incrementVersion(); 442 443 protected: 444 /*! \brief Prepares the resource for deletion. 445 * 446 * When specializing a resource, you MUST call beingDeleted() from 447 * within the specialized destructor, in order to stop any further 448 * requests to the resource. 449 */ 450 void beingDeleted(); 451 452 private: 453 struct UseLock { 454 UseLock(); 455 ~UseLock(); 456 457 bool use(WResource *resource); 458 459 private: 460 WResource *resource_; 461 }; 462 463 #ifdef WT_THREADED 464 std::shared_ptr<std::recursive_mutex> mutex_; 465 bool beingDeleted_; 466 int useCount_; 467 std::condition_variable_any useDone_; 468 #endif 469 470 Signal<> dataChanged_; 471 Signal< ::uint64_t, ::uint64_t > dataReceived_; 472 Signal< ::uint64_t > dataExceeded_; 473 474 bool trackUploadProgress_; 475 bool takesUpdateLock_; 476 bool invalidAfterChanged_; 477 478 std::vector<Http::ResponseContinuationPtr> continuations_; 479 480 void removeContinuation(Http::ResponseContinuationPtr continuation); 481 Http::ResponseContinuationPtr addContinuation(Http::ResponseContinuation *c); 482 void doContinue(Http::ResponseContinuationPtr continuation); 483 void handle(WebRequest *webRequest, WebResponse *webResponse, 484 Http::ResponseContinuationPtr continuation 485 = Http::ResponseContinuationPtr()); 486 487 Wt::WString suggestedFileName_; 488 ContentDisposition dispositionType_; 489 std::string currentUrl_; 490 std::string internalPath_; 491 unsigned long version_; 492 493 WApplication *app_; // associated app (for non-static resources) 494 495 friend class Http::ResponseContinuation; 496 friend class Http::Response; 497 friend class WebSession; 498 friend class WebController; 499 friend class Configuration; 500 }; 501 502 } 503 504 #endif // WRESOURCE_H_ 505