Re-subimission of https://codereview.chromium.org/1041213003/
[chromium-blink-merge.git] / content / browser / streams / stream_url_request_job.cc
blobd526cb7742a1a2d1e3a1681d88ad52fe2edfee56
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "content/browser/streams/stream_url_request_job.h"
7 #include "base/profiler/scoped_tracker.h"
8 #include "base/strings/string_number_conversions.h"
9 #include "content/browser/streams/stream.h"
10 #include "net/base/io_buffer.h"
11 #include "net/base/net_errors.h"
12 #include "net/http/http_byte_range.h"
13 #include "net/http/http_response_headers.h"
14 #include "net/http/http_response_info.h"
15 #include "net/http/http_util.h"
16 #include "net/url_request/url_request.h"
18 namespace content {
20 StreamURLRequestJob::StreamURLRequestJob(
21 net::URLRequest* request,
22 net::NetworkDelegate* network_delegate,
23 scoped_refptr<Stream> stream)
24 : net::URLRequestJob(request, network_delegate),
25 stream_(stream),
26 headers_set_(false),
27 pending_buffer_size_(0),
28 total_bytes_read_(0),
29 max_range_(0),
30 request_failed_(false),
31 weak_factory_(this) {
32 DCHECK(stream_.get());
33 stream_->SetReadObserver(this);
36 StreamURLRequestJob::~StreamURLRequestJob() {
37 ClearStream();
40 void StreamURLRequestJob::OnDataAvailable(Stream* stream) {
41 // Clear the IO_PENDING status.
42 SetStatus(net::URLRequestStatus());
43 // Do nothing if pending_buffer_ is empty, i.e. there's no ReadRawData()
44 // operation waiting for IO completion.
45 if (!pending_buffer_.get())
46 return;
48 // pending_buffer_ is set to the IOBuffer instance provided to ReadRawData()
49 // by URLRequestJob.
51 int bytes_read;
52 switch (stream_->ReadRawData(
53 pending_buffer_.get(), pending_buffer_size_, &bytes_read)) {
54 case Stream::STREAM_HAS_DATA:
55 DCHECK_GT(bytes_read, 0);
56 break;
57 case Stream::STREAM_COMPLETE:
58 // Ensure this. Calling NotifyReadComplete call with 0 signals
59 // completion.
60 bytes_read = 0;
61 break;
62 case Stream::STREAM_EMPTY:
63 NOTREACHED();
64 break;
65 case Stream::STREAM_ABORTED:
66 // Handle this as connection reset.
67 NotifyDone(net::URLRequestStatus(net::URLRequestStatus::FAILED,
68 net::ERR_CONNECTION_RESET));
69 break;
72 // Clear the buffers before notifying the read is complete, so that it is
73 // safe for the observer to read.
74 pending_buffer_ = NULL;
75 pending_buffer_size_ = 0;
77 total_bytes_read_ += bytes_read;
78 NotifyReadComplete(bytes_read);
81 // net::URLRequestJob methods.
82 void StreamURLRequestJob::Start() {
83 // Continue asynchronously.
84 base::MessageLoop::current()->PostTask(
85 FROM_HERE,
86 base::Bind(&StreamURLRequestJob::DidStart, weak_factory_.GetWeakPtr()));
89 void StreamURLRequestJob::Kill() {
90 net::URLRequestJob::Kill();
91 weak_factory_.InvalidateWeakPtrs();
92 ClearStream();
95 bool StreamURLRequestJob::ReadRawData(net::IOBuffer* buf,
96 int buf_size,
97 int* bytes_read) {
98 // TODO(vadimt): Remove ScopedTracker below once crbug.com/423948 is fixed.
99 tracked_objects::ScopedTracker tracking_profile(
100 FROM_HERE_WITH_EXPLICIT_FUNCTION(
101 "423948 StreamURLRequestJob::ReadRawData"));
103 if (request_failed_)
104 return true;
106 DCHECK(buf);
107 DCHECK(bytes_read);
108 int to_read = buf_size;
109 if (max_range_ && to_read) {
110 if (to_read + total_bytes_read_ > max_range_)
111 to_read = max_range_ - total_bytes_read_;
113 if (to_read <= 0) {
114 *bytes_read = 0;
115 return true;
119 switch (stream_->ReadRawData(buf, to_read, bytes_read)) {
120 case Stream::STREAM_HAS_DATA:
121 case Stream::STREAM_COMPLETE:
122 total_bytes_read_ += *bytes_read;
123 return true;
124 case Stream::STREAM_EMPTY:
125 pending_buffer_ = buf;
126 pending_buffer_size_ = to_read;
127 SetStatus(net::URLRequestStatus(net::URLRequestStatus::IO_PENDING, 0));
128 return false;
129 case Stream::STREAM_ABORTED:
130 // Handle this as connection reset.
131 NotifyDone(net::URLRequestStatus(net::URLRequestStatus::FAILED,
132 net::ERR_CONNECTION_RESET));
133 return false;
135 NOTREACHED();
136 return false;
139 bool StreamURLRequestJob::GetMimeType(std::string* mime_type) const {
140 if (!response_info_)
141 return false;
143 // TODO(zork): Support registered MIME types if needed.
144 return response_info_->headers->GetMimeType(mime_type);
147 void StreamURLRequestJob::GetResponseInfo(net::HttpResponseInfo* info) {
148 if (response_info_)
149 *info = *response_info_;
152 int StreamURLRequestJob::GetResponseCode() const {
153 if (!response_info_)
154 return -1;
156 return response_info_->headers->response_code();
159 void StreamURLRequestJob::SetExtraRequestHeaders(
160 const net::HttpRequestHeaders& headers) {
161 std::string range_header;
162 if (headers.GetHeader(net::HttpRequestHeaders::kRange, &range_header)) {
163 std::vector<net::HttpByteRange> ranges;
164 if (net::HttpUtil::ParseRangeHeader(range_header, &ranges)) {
165 if (ranges.size() == 1) {
166 // Streams don't support seeking, so a non-zero starting position
167 // doesn't make sense.
168 if (ranges[0].first_byte_position() == 0) {
169 max_range_ = ranges[0].last_byte_position() + 1;
170 } else {
171 NotifyFailure(net::ERR_METHOD_NOT_SUPPORTED);
172 return;
174 } else {
175 NotifyFailure(net::ERR_METHOD_NOT_SUPPORTED);
176 return;
182 void StreamURLRequestJob::DidStart() {
183 // We only support GET request.
184 if (request()->method() != "GET") {
185 NotifyFailure(net::ERR_METHOD_NOT_SUPPORTED);
186 return;
189 HeadersCompleted(net::HTTP_OK);
192 void StreamURLRequestJob::NotifyFailure(int error_code) {
193 request_failed_ = true;
195 // If we already return the headers on success, we can't change the headers
196 // now. Instead, we just error out.
197 if (headers_set_) {
198 NotifyDone(net::URLRequestStatus(net::URLRequestStatus::FAILED,
199 error_code));
200 return;
203 // TODO(zork): Share these with BlobURLRequestJob.
204 net::HttpStatusCode status_code = net::HTTP_INTERNAL_SERVER_ERROR;
205 switch (error_code) {
206 case net::ERR_ACCESS_DENIED:
207 status_code = net::HTTP_FORBIDDEN;
208 break;
209 case net::ERR_FILE_NOT_FOUND:
210 status_code = net::HTTP_NOT_FOUND;
211 break;
212 case net::ERR_METHOD_NOT_SUPPORTED:
213 status_code = net::HTTP_METHOD_NOT_ALLOWED;
214 break;
215 case net::ERR_FAILED:
216 break;
217 default:
218 DCHECK(false);
219 break;
221 HeadersCompleted(status_code);
224 void StreamURLRequestJob::HeadersCompleted(net::HttpStatusCode status_code) {
225 std::string status("HTTP/1.1 ");
226 status.append(base::IntToString(status_code));
227 status.append(" ");
228 status.append(net::GetHttpReasonPhrase(status_code));
229 status.append("\0\0", 2);
230 net::HttpResponseHeaders* headers = new net::HttpResponseHeaders(status);
232 if (status_code == net::HTTP_OK) {
233 std::string content_type_header(net::HttpRequestHeaders::kContentType);
234 content_type_header.append(": ");
235 content_type_header.append("text/plain");
236 headers->AddHeader(content_type_header);
239 response_info_.reset(new net::HttpResponseInfo());
240 response_info_->headers = headers;
242 headers_set_ = true;
244 NotifyHeadersComplete();
247 void StreamURLRequestJob::ClearStream() {
248 if (stream_.get()) {
249 stream_->RemoveReadObserver(this);
250 stream_ = NULL;
254 } // namespace content