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