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