1 /*
2  * Copyright (C) 2011 Emweb bv, Herent, Belgium.
3  *
4  * See the LICENSE file for terms of use.
5  */
6 
7 #include "Wt/WStreamResource.h"
8 
9 #include "Wt/Http/Request.h"
10 #include "Wt/Http/Response.h"
11 
12 #include <boost/scoped_array.hpp>
13 
14 namespace Wt {
15 
WStreamResource()16 WStreamResource::WStreamResource()
17   : mimeType_("text/plain"),
18     bufferSize_(8192)
19 { }
20 
WStreamResource(const std::string & mimeType)21 WStreamResource::WStreamResource(const std::string& mimeType)
22   : mimeType_(mimeType),
23     bufferSize_(8192)
24 { }
25 
~WStreamResource()26 WStreamResource::~WStreamResource()
27 {
28   beingDeleted();
29 }
30 
setMimeType(const std::string & mimeType)31 void WStreamResource::setMimeType(const std::string& mimeType)
32 {
33   mimeType_ = mimeType;
34   setChanged();
35 }
36 
setBufferSize(int bufferSize)37 void WStreamResource::setBufferSize(int bufferSize)
38 {
39   bufferSize_ = bufferSize;
40 }
41 
handleRequestPiecewise(const Http::Request & request,Http::Response & response,std::istream & input)42 void WStreamResource::handleRequestPiecewise(const Http::Request& request,
43                                              Http::Response& response,
44                                              std::istream& input)
45 {
46   Http::ResponseContinuation *continuation = request.continuation();
47   ::uint64_t startByte
48       = continuation ? cpp17::any_cast<::uint64_t>(continuation->data()) : 0;
49 
50   if (startByte == 0) {
51     /*
52      * Initial request (not a continuation)
53      */
54     if (!input) {
55       response.setStatus(404);
56       return;
57     } else
58       response.setStatus(200);
59 
60     /*
61      * See if we should return a range.
62      */
63     input.seekg(0, std::ios::end);
64     std::istream::pos_type isize = input.tellg();
65     input.seekg(0, std::ios::beg);
66 
67     Http::Request::ByteRangeSpecifier ranges = request.getRanges(isize);
68 
69     if (!ranges.isSatisfiable()) {
70       std::ostringstream contentRange;
71       contentRange << "bytes */" << isize;
72       response.setStatus(416); // Requested range not satisfiable
73       response.addHeader("Content-Range", contentRange.str());
74       return;
75     }
76 
77     if (ranges.size() == 1) {
78       response.setStatus(206);
79       startByte = ranges[0].firstByte();
80       beyondLastByte_ = std::streamsize(ranges[0].lastByte() + 1);
81 
82       std::ostringstream contentRange;
83       contentRange << "bytes " << startByte << "-"
84 		   << beyondLastByte_ - 1 << "/" << isize;
85       response.addHeader("Content-Range", contentRange.str());
86       response.setContentLength(::uint64_t(beyondLastByte_) - startByte);
87     } else {
88       beyondLastByte_ = std::streamsize(isize);
89       response.setContentLength(::uint64_t(beyondLastByte_));
90     }
91 
92     response.setMimeType(mimeType_);
93   }
94 
95   input.seekg(static_cast<std::istream::pos_type>(startByte));
96 
97   // According to 27.6.1.3, paragraph 1 of ISO/IEC 14882:2003(E),
98   // each unformatted input function may throw an exception.
99   boost::scoped_array<char> buf(new char[bufferSize_]);
100   std::streamsize sbufferSize = std::streamsize(bufferSize_);
101 
102   std::streamsize restSize = beyondLastByte_ - std::streamsize(startByte);
103   std::streamsize pieceSize =  sbufferSize > restSize ? restSize : sbufferSize;
104 
105   input.read(buf.get(), pieceSize);
106   std::streamsize actualPieceSize = input.gcount();
107   response.out().write(buf.get(), actualPieceSize);
108 
109   if (input.good() && actualPieceSize < restSize) {
110     continuation = response.createContinuation();
111     continuation->setData(startByte + ::uint64_t(actualPieceSize));
112   }
113 }
114 
115 }
116