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