Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / content / browser / streams / stream_url_request_job.cc
blobd2bce89aca6834085319d19d84405c92184e26bf
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/location.h"
8 #include "base/single_thread_task_runner.h"
9 #include "base/strings/string_number_conversions.h"
10 #include "base/thread_task_runner_handle.h"
11 #include "content/browser/streams/stream.h"
12 #include "net/base/io_buffer.h"
13 #include "net/base/net_errors.h"
14 #include "net/http/http_byte_range.h"
15 #include "net/http/http_response_headers.h"
16 #include "net/http/http_response_info.h"
17 #include "net/http/http_util.h"
18 #include "net/url_request/url_request.h"
20 namespace content {
22 StreamURLRequestJob::StreamURLRequestJob(
23 net::URLRequest* request,
24 net::NetworkDelegate* network_delegate,
25 scoped_refptr<Stream> stream)
26 : net::URLRequestJob(request, network_delegate),
27 stream_(stream),
28 headers_set_(false),
29 pending_buffer_size_(0),
30 total_bytes_read_(0),
31 max_range_(0),
32 request_failed_(false),
33 weak_factory_(this) {
34 DCHECK(stream_.get());
35 stream_->SetReadObserver(this);
38 StreamURLRequestJob::~StreamURLRequestJob() {
39 ClearStream();
42 void StreamURLRequestJob::OnDataAvailable(Stream* stream) {
43 // Clear the IO_PENDING status.
44 SetStatus(net::URLRequestStatus());
45 // Do nothing if pending_buffer_ is empty, i.e. there's no ReadRawData()
46 // operation waiting for IO completion.
47 if (!pending_buffer_.get())
48 return;
50 // pending_buffer_ is set to the IOBuffer instance provided to ReadRawData()
51 // by URLRequestJob.
53 int bytes_read;
54 switch (stream_->ReadRawData(
55 pending_buffer_.get(), pending_buffer_size_, &bytes_read)) {
56 case Stream::STREAM_HAS_DATA:
57 DCHECK_GT(bytes_read, 0);
58 break;
59 case Stream::STREAM_COMPLETE:
60 // Ensure this. Calling NotifyReadComplete call with 0 signals
61 // completion.
62 bytes_read = 0;
63 break;
64 case Stream::STREAM_EMPTY:
65 NOTREACHED();
66 break;
67 case Stream::STREAM_ABORTED:
68 // Handle this as connection reset.
69 NotifyDone(net::URLRequestStatus(net::URLRequestStatus::FAILED,
70 net::ERR_CONNECTION_RESET));
71 break;
74 // Clear the buffers before notifying the read is complete, so that it is
75 // safe for the observer to read.
76 pending_buffer_ = NULL;
77 pending_buffer_size_ = 0;
79 total_bytes_read_ += bytes_read;
80 NotifyReadComplete(bytes_read);
83 // net::URLRequestJob methods.
84 void StreamURLRequestJob::Start() {
85 // Continue asynchronously.
86 base::ThreadTaskRunnerHandle::Get()->PostTask(
87 FROM_HERE,
88 base::Bind(&StreamURLRequestJob::DidStart, weak_factory_.GetWeakPtr()));
91 void StreamURLRequestJob::Kill() {
92 net::URLRequestJob::Kill();
93 weak_factory_.InvalidateWeakPtrs();
94 ClearStream();
97 bool StreamURLRequestJob::ReadRawData(net::IOBuffer* buf,
98 int buf_size,
99 int* bytes_read) {
100 if (request_failed_)
101 return true;
103 DCHECK(buf);
104 DCHECK(bytes_read);
105 int to_read = buf_size;
106 if (max_range_ && to_read) {
107 if (to_read + total_bytes_read_ > max_range_)
108 to_read = max_range_ - total_bytes_read_;
110 if (to_read <= 0) {
111 *bytes_read = 0;
112 return true;
116 switch (stream_->ReadRawData(buf, to_read, bytes_read)) {
117 case Stream::STREAM_HAS_DATA:
118 case Stream::STREAM_COMPLETE:
119 total_bytes_read_ += *bytes_read;
120 return true;
121 case Stream::STREAM_EMPTY:
122 pending_buffer_ = buf;
123 pending_buffer_size_ = to_read;
124 SetStatus(net::URLRequestStatus(net::URLRequestStatus::IO_PENDING, 0));
125 return false;
126 case Stream::STREAM_ABORTED:
127 // Handle this as connection reset.
128 NotifyDone(net::URLRequestStatus(net::URLRequestStatus::FAILED,
129 net::ERR_CONNECTION_RESET));
130 return false;
132 NOTREACHED();
133 return false;
136 bool StreamURLRequestJob::GetMimeType(std::string* mime_type) const {
137 if (!response_info_)
138 return false;
140 // TODO(zork): Support registered MIME types if needed.
141 return response_info_->headers->GetMimeType(mime_type);
144 void StreamURLRequestJob::GetResponseInfo(net::HttpResponseInfo* info) {
145 if (response_info_)
146 *info = *response_info_;
149 int StreamURLRequestJob::GetResponseCode() const {
150 if (!response_info_)
151 return -1;
153 return response_info_->headers->response_code();
156 void StreamURLRequestJob::SetExtraRequestHeaders(
157 const net::HttpRequestHeaders& headers) {
158 std::string range_header;
159 if (headers.GetHeader(net::HttpRequestHeaders::kRange, &range_header)) {
160 std::vector<net::HttpByteRange> ranges;
161 if (net::HttpUtil::ParseRangeHeader(range_header, &ranges)) {
162 if (ranges.size() == 1) {
163 // Streams don't support seeking, so a non-zero starting position
164 // doesn't make sense.
165 if (ranges[0].first_byte_position() == 0) {
166 max_range_ = ranges[0].last_byte_position() + 1;
167 } else {
168 NotifyFailure(net::ERR_METHOD_NOT_SUPPORTED);
169 return;
171 } else {
172 NotifyFailure(net::ERR_METHOD_NOT_SUPPORTED);
173 return;
179 void StreamURLRequestJob::DidStart() {
180 // We only support GET request.
181 if (request()->method() != "GET") {
182 NotifyFailure(net::ERR_METHOD_NOT_SUPPORTED);
183 return;
186 HeadersCompleted(net::HTTP_OK);
189 void StreamURLRequestJob::NotifyFailure(int error_code) {
190 request_failed_ = true;
192 // If we already return the headers on success, we can't change the headers
193 // now. Instead, we just error out.
194 if (headers_set_) {
195 NotifyDone(net::URLRequestStatus(net::URLRequestStatus::FAILED,
196 error_code));
197 return;
200 // TODO(zork): Share these with BlobURLRequestJob.
201 net::HttpStatusCode status_code = net::HTTP_INTERNAL_SERVER_ERROR;
202 switch (error_code) {
203 case net::ERR_ACCESS_DENIED:
204 status_code = net::HTTP_FORBIDDEN;
205 break;
206 case net::ERR_FILE_NOT_FOUND:
207 status_code = net::HTTP_NOT_FOUND;
208 break;
209 case net::ERR_METHOD_NOT_SUPPORTED:
210 status_code = net::HTTP_METHOD_NOT_ALLOWED;
211 break;
212 case net::ERR_FAILED:
213 break;
214 default:
215 DCHECK(false);
216 break;
218 HeadersCompleted(status_code);
221 void StreamURLRequestJob::HeadersCompleted(net::HttpStatusCode status_code) {
222 std::string status("HTTP/1.1 ");
223 status.append(base::IntToString(status_code));
224 status.append(" ");
225 status.append(net::GetHttpReasonPhrase(status_code));
226 status.append("\0\0", 2);
227 net::HttpResponseHeaders* headers = new net::HttpResponseHeaders(status);
229 if (status_code == net::HTTP_OK) {
230 std::string content_type_header(net::HttpRequestHeaders::kContentType);
231 content_type_header.append(": ");
232 content_type_header.append("text/plain");
233 headers->AddHeader(content_type_header);
236 response_info_.reset(new net::HttpResponseInfo());
237 response_info_->headers = headers;
239 headers_set_ = true;
241 NotifyHeadersComplete();
244 void StreamURLRequestJob::ClearStream() {
245 if (stream_.get()) {
246 stream_->RemoveReadObserver(this);
247 stream_ = NULL;
251 } // namespace content