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   if (pending_buffer_.get()) {
     43     int bytes_read;
     44     stream_->ReadRawData(
     45         pending_buffer_.get(), pending_buffer_size_, &bytes_read);
     46 
     47     // Clear the buffers before notifying the read is complete, so that it is
     48     // safe for the observer to read.
     49     pending_buffer_ = NULL;
     50     pending_buffer_size_ = 0;
     51 
     52     total_bytes_read_ += bytes_read;
     53     NotifyReadComplete(bytes_read);
     54   }
     55 }
     56 
     57 // net::URLRequestJob methods.
     58 void StreamURLRequestJob::Start() {
     59   // Continue asynchronously.
     60   base::MessageLoop::current()->PostTask(
     61       FROM_HERE,
     62       base::Bind(&StreamURLRequestJob::DidStart, weak_factory_.GetWeakPtr()));
     63 }
     64 
     65 void StreamURLRequestJob::Kill() {
     66   net::URLRequestJob::Kill();
     67   weak_factory_.InvalidateWeakPtrs();
     68   ClearStream();
     69 }
     70 
     71 bool StreamURLRequestJob::ReadRawData(net::IOBuffer* buf,
     72                                       int buf_size,
     73                                       int* bytes_read) {
     74   if (request_failed_)
     75     return true;
     76 
     77   DCHECK(bytes_read);
     78   int to_read = buf_size;
     79   if (max_range_ && to_read) {
     80     if (to_read + total_bytes_read_ > max_range_)
     81       to_read = max_range_ - total_bytes_read_;
     82 
     83     if (to_read <= 0) {
     84       *bytes_read = 0;
     85       return true;
     86     }
     87   }
     88 
     89   switch (stream_->ReadRawData(buf, to_read, bytes_read)) {
     90     case Stream::STREAM_HAS_DATA:
     91     case Stream::STREAM_COMPLETE:
     92       total_bytes_read_ += *bytes_read;
     93       return true;
     94     case Stream::STREAM_EMPTY:
     95       pending_buffer_ = buf;
     96       pending_buffer_size_ = to_read;
     97       SetStatus(net::URLRequestStatus(net::URLRequestStatus::IO_PENDING, 0));
     98       return false;
     99   }
    100   NOTREACHED();
    101   return false;
    102 }
    103 
    104 bool StreamURLRequestJob::GetMimeType(std::string* mime_type) const {
    105   if (!response_info_)
    106     return false;
    107 
    108   // TODO(zork): Support registered MIME types if needed.
    109   return response_info_->headers->GetMimeType(mime_type);
    110 }
    111 
    112 void StreamURLRequestJob::GetResponseInfo(net::HttpResponseInfo* info) {
    113   if (response_info_)
    114     *info = *response_info_;
    115 }
    116 
    117 int StreamURLRequestJob::GetResponseCode() const {
    118   if (!response_info_)
    119     return -1;
    120 
    121   return response_info_->headers->response_code();
    122 }
    123 
    124 void StreamURLRequestJob::SetExtraRequestHeaders(
    125     const net::HttpRequestHeaders& headers) {
    126   std::string range_header;
    127   if (headers.GetHeader(net::HttpRequestHeaders::kRange, &range_header)) {
    128     std::vector<net::HttpByteRange> ranges;
    129     if (net::HttpUtil::ParseRangeHeader(range_header, &ranges)) {
    130       if (ranges.size() == 1) {
    131         // Streams don't support seeking, so a non-zero starting position
    132         // doesn't make sense.
    133         if (ranges[0].first_byte_position() == 0) {
    134           max_range_ = ranges[0].last_byte_position() + 1;
    135         } else {
    136           NotifyFailure(net::ERR_METHOD_NOT_SUPPORTED);
    137           return;
    138         }
    139       } else {
    140         NotifyFailure(net::ERR_METHOD_NOT_SUPPORTED);
    141         return;
    142       }
    143     }
    144   }
    145 }
    146 
    147 void StreamURLRequestJob::DidStart() {
    148   // We only support GET request.
    149   if (request()->method() != "GET") {
    150     NotifyFailure(net::ERR_METHOD_NOT_SUPPORTED);
    151     return;
    152   }
    153 
    154   HeadersCompleted(net::HTTP_OK);
    155 }
    156 
    157 void StreamURLRequestJob::NotifyFailure(int error_code) {
    158   request_failed_ = true;
    159 
    160   // If we already return the headers on success, we can't change the headers
    161   // now. Instead, we just error out.
    162   if (headers_set_) {
    163     NotifyDone(net::URLRequestStatus(net::URLRequestStatus::FAILED,
    164                                      error_code));
    165     return;
    166   }
    167 
    168   // TODO(zork): Share these with BlobURLRequestJob.
    169   net::HttpStatusCode status_code = net::HTTP_INTERNAL_SERVER_ERROR;
    170   std::string status_txt;
    171   switch (error_code) {
    172     case net::ERR_ACCESS_DENIED:
    173       status_code = net::HTTP_FORBIDDEN;
    174       break;
    175     case net::ERR_FILE_NOT_FOUND:
    176       status_code = net::HTTP_NOT_FOUND;
    177       break;
    178     case net::ERR_METHOD_NOT_SUPPORTED:
    179       status_code = net::HTTP_METHOD_NOT_ALLOWED;
    180       break;
    181     case net::ERR_FAILED:
    182       break;
    183     default:
    184       DCHECK(false);
    185       break;
    186   }
    187   HeadersCompleted(status_code);
    188 }
    189 
    190 void StreamURLRequestJob::HeadersCompleted(net::HttpStatusCode status_code) {
    191   std::string status("HTTP/1.1 ");
    192   status.append(base::IntToString(status_code));
    193   status.append(" ");
    194   status.append(net::GetHttpReasonPhrase(status_code));
    195   status.append("\0\0", 2);
    196   net::HttpResponseHeaders* headers = new net::HttpResponseHeaders(status);
    197 
    198   if (status_code == net::HTTP_OK) {
    199     std::string content_type_header(net::HttpRequestHeaders::kContentType);
    200     content_type_header.append(": ");
    201     content_type_header.append("plain/text");
    202     headers->AddHeader(content_type_header);
    203   }
    204 
    205   response_info_.reset(new net::HttpResponseInfo());
    206   response_info_->headers = headers;
    207 
    208   headers_set_ = true;
    209 
    210   NotifyHeadersComplete();
    211 }
    212 
    213 void StreamURLRequestJob::ClearStream() {
    214   if (stream_.get()) {
    215     stream_->RemoveReadObserver(this);
    216     stream_ = NULL;
    217   }
    218 }
    219 
    220 }  // namespace content
    221