content/media/plugins/MediaResourceServer.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/content/media/plugins/MediaResourceServer.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,526 @@
     1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +/* vim:set ts=2 sw=2 sts=2 et cindent: */
     1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this file,
     1.8 + * You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +#include "mozilla/Assertions.h"
    1.10 +#include "mozilla/Base64.h"
    1.11 +#include "nsThreadUtils.h"
    1.12 +#include "nsIServiceManager.h"
    1.13 +#include "nsISocketTransport.h"
    1.14 +#include "nsIOutputStream.h"
    1.15 +#include "nsIInputStream.h"
    1.16 +#include "nsIRandomGenerator.h"
    1.17 +#include "nsReadLine.h"
    1.18 +#include "nsNetCID.h"
    1.19 +#include "VideoUtils.h"
    1.20 +#include "MediaResource.h"
    1.21 +#include "MediaResourceServer.h"
    1.22 +
    1.23 +#if defined(_MSC_VER)
    1.24 +#define strtoll _strtoi64
    1.25 +#define snprintf _snprintf_s
    1.26 +#endif
    1.27 +
    1.28 +using namespace mozilla;
    1.29 +
    1.30 +/*
    1.31 +  ReadCRLF is a variant of NS_ReadLine from nsReadLine.h that deals
    1.32 +  with the carriage return/line feed requirements of HTTP requests.
    1.33 +*/
    1.34 +template<typename CharT, class StreamType, class StringType>
    1.35 +nsresult
    1.36 +ReadCRLF (StreamType* aStream, nsLineBuffer<CharT> * aBuffer,
    1.37 +          StringType & aLine, bool *aMore)
    1.38 +{
    1.39 +  // eollast is true if the last character in the buffer is a '\r',
    1.40 +  // signaling a potential '\r\n' sequence split between reads.
    1.41 +  bool eollast = false;
    1.42 +
    1.43 +  aLine.Truncate();
    1.44 +
    1.45 +  while (1) { // will be returning out of this loop on eol or eof
    1.46 +    if (aBuffer->start == aBuffer->end) { // buffer is empty.  Read into it.
    1.47 +      uint32_t bytesRead;
    1.48 +      nsresult rv = aStream->Read(aBuffer->buf, kLineBufferSize, &bytesRead);
    1.49 +      if (NS_FAILED(rv) || bytesRead == 0) {
    1.50 +        *aMore = false;
    1.51 +        return rv;
    1.52 +      }
    1.53 +      aBuffer->start = aBuffer->buf;
    1.54 +      aBuffer->end = aBuffer->buf + bytesRead;
    1.55 +      *(aBuffer->end) = '\0';
    1.56 +    }
    1.57 +
    1.58 +    /*
    1.59 +     * Walk the buffer looking for an end-of-line.
    1.60 +     * There are 4 cases to consider:
    1.61 +     *  1. the CR char is the last char in the buffer
    1.62 +     *  2. the CRLF sequence are the last characters in the buffer
    1.63 +     *  3. the CRLF sequence + one or more chars at the end of the buffer
    1.64 +     *      we need at least one char after the first CRLF sequence to
    1.65 +     *      set |aMore| correctly.
    1.66 +     *  4. The LF character is the first char in the buffer when eollast is
    1.67 +     *      true.
    1.68 +     */
    1.69 +    CharT* current = aBuffer->start;
    1.70 +    if (eollast) { // Case 4
    1.71 +      if (*current == '\n') {
    1.72 +        aBuffer->start = ++current;
    1.73 +        *aMore = true;
    1.74 +        return NS_OK;
    1.75 +      }
    1.76 +      else {
    1.77 +        eollast = false;
    1.78 +        aLine.Append('\r');
    1.79 +      }
    1.80 +    }
    1.81 +    // Cases 2 and 3
    1.82 +    for ( ; current < aBuffer->end-1; ++current) {
    1.83 +      if (*current == '\r' && *(current+1) == '\n') {
    1.84 +        *current++ = '\0';
    1.85 +        *current++ = '\0';
    1.86 +        aLine.Append(aBuffer->start);
    1.87 +        aBuffer->start = current;
    1.88 +        *aMore = true;
    1.89 +        return NS_OK;
    1.90 +      }
    1.91 +    }
    1.92 +    // Case 1
    1.93 +    if (*current == '\r') {
    1.94 +      eollast = true;
    1.95 +      *current++ = '\0';
    1.96 +    }
    1.97 +
    1.98 +    aLine.Append(aBuffer->start);
    1.99 +    aBuffer->start = aBuffer->end; // mark the buffer empty
   1.100 +  }
   1.101 +}
   1.102 +
   1.103 +// Each client HTTP request results in a thread being spawned to process it.
   1.104 +// That thread has a single event dispatched to it which handles the HTTP
   1.105 +// protocol. It parses the headers and forwards data from the MediaResource
   1.106 +// associated with the URL back to client. When the request is complete it will
   1.107 +// shutdown the thread.
   1.108 +class ServeResourceEvent : public nsRunnable {
   1.109 +private:
   1.110 +  // Reading from this reads the data sent from the client.
   1.111 +  nsCOMPtr<nsIInputStream> mInput;
   1.112 +
   1.113 +  // Writing to this sends data to the client.
   1.114 +  nsCOMPtr<nsIOutputStream> mOutput;
   1.115 +
   1.116 +  // The MediaResourceServer that owns the MediaResource instances
   1.117 +  // served. This is used to lookup the MediaResource from the URL.
   1.118 +  nsRefPtr<MediaResourceServer> mServer;
   1.119 +
   1.120 +  // Write 'aBufferLength' bytes from 'aBuffer' to 'mOutput'. This
   1.121 +  // method ensures all the data is written by checking the number
   1.122 +  // of bytes returned from the output streams 'Write' method and
   1.123 +  // looping until done.
   1.124 +  nsresult WriteAll(char const* aBuffer, int32_t aBufferLength);
   1.125 +
   1.126 +public:
   1.127 +  ServeResourceEvent(nsIInputStream* aInput, nsIOutputStream* aOutput,
   1.128 +                     MediaResourceServer* aServer)
   1.129 +    : mInput(aInput), mOutput(aOutput), mServer(aServer) {}
   1.130 +
   1.131 +  // This method runs on the thread and exits when it has completed the
   1.132 +  // HTTP request.
   1.133 +  NS_IMETHOD Run();
   1.134 +
   1.135 +  // Given the first line of an HTTP request, parse the URL requested and
   1.136 +  // return the MediaResource for that URL.
   1.137 +  already_AddRefed<MediaResource> GetMediaResource(nsCString const& aHTTPRequest);
   1.138 +
   1.139 +  // Gracefully shutdown the thread and cleanup resources
   1.140 +  void Shutdown();
   1.141 +};
   1.142 +
   1.143 +nsresult
   1.144 +ServeResourceEvent::WriteAll(char const* aBuffer, int32_t aBufferLength)
   1.145 +{
   1.146 +  while (aBufferLength > 0) {
   1.147 +    uint32_t written = 0;
   1.148 +    nsresult rv = mOutput->Write(aBuffer, aBufferLength, &written);
   1.149 +    if (NS_FAILED (rv)) return rv;
   1.150 +
   1.151 +    aBufferLength -= written;
   1.152 +    aBuffer += written;
   1.153 +  }
   1.154 +
   1.155 +  return NS_OK;
   1.156 +}
   1.157 +
   1.158 +already_AddRefed<MediaResource>
   1.159 +ServeResourceEvent::GetMediaResource(nsCString const& aHTTPRequest)
   1.160 +{
   1.161 +  // Check that the HTTP method is GET
   1.162 +  const char* HTTP_METHOD = "GET ";
   1.163 +  if (strncmp(aHTTPRequest.get(), HTTP_METHOD, strlen(HTTP_METHOD)) != 0) {
   1.164 +    return nullptr;
   1.165 +  }
   1.166 +
   1.167 +  const char* url_start = strchr(aHTTPRequest.get(), ' ');
   1.168 +  if (!url_start) {
   1.169 +    return nullptr;
   1.170 +  }
   1.171 +
   1.172 +  const char* url_end = strrchr(++url_start, ' ');
   1.173 +  if (!url_end) {
   1.174 +    return nullptr;
   1.175 +  }
   1.176 +
   1.177 +  // The path extracted from the HTTP request is used as a key in hash
   1.178 +  // table. It is not related to retrieving data from the filesystem so
   1.179 +  // we don't need to do any sanity checking on ".." paths and similar
   1.180 +  // exploits.
   1.181 +  nsCString relative(url_start, url_end - url_start);
   1.182 +  nsRefPtr<MediaResource> resource =
   1.183 +    mServer->GetResource(mServer->GetURLPrefix() + relative);
   1.184 +  return resource.forget();
   1.185 +}
   1.186 +
   1.187 +NS_IMETHODIMP
   1.188 +ServeResourceEvent::Run() {
   1.189 +  bool more = false; // Are there HTTP headers to read after the first line
   1.190 +  nsCString line;    // Contains the current line read from input stream
   1.191 +  nsLineBuffer<char>* buffer = new nsLineBuffer<char>();
   1.192 +  nsresult rv = ReadCRLF(mInput.get(), buffer, line, &more);
   1.193 +  if (NS_FAILED(rv)) { Shutdown(); return rv; }
   1.194 +
   1.195 +  // First line contains the HTTP GET request. Extract the URL and obtain
   1.196 +  // the MediaResource for it.
   1.197 +  nsRefPtr<MediaResource> resource = GetMediaResource(line);
   1.198 +  if (!resource) {
   1.199 +    const char* response_404 = "HTTP/1.1 404 Not Found\r\n"
   1.200 +                               "Content-Length: 0\r\n\r\n";
   1.201 +    rv = WriteAll(response_404, strlen(response_404));
   1.202 +    Shutdown();
   1.203 +    return rv;
   1.204 +  }
   1.205 +
   1.206 +  // Offset in bytes to start reading from resource.
   1.207 +  // This is zero by default but can be set to another starting value if
   1.208 +  // this HTTP request includes a byte range request header.
   1.209 +  int64_t start = 0;
   1.210 +
   1.211 +  // Keep reading lines until we get a zero length line, which is the HTTP
   1.212 +  // protocol's way of signifying the end of headers and start of body, or
   1.213 +  // until we have no more data to read.
   1.214 +  while (more && line.Length() > 0) {
   1.215 +    rv = ReadCRLF(mInput.get(), buffer, line, &more);
   1.216 +    if (NS_FAILED(rv)) { Shutdown(); return rv; }
   1.217 +
   1.218 +    // Look for a byte range request header. If there is one, set the
   1.219 +    // media resource offset to start from to that requested. Here we
   1.220 +    // only check for the range request format used by Android rather
   1.221 +    // than implementing all possibilities in the HTTP specification.
   1.222 +    // That is, the range request is of the form:
   1.223 +    //   Range: bytes=nnnn-
   1.224 +    // Were 'nnnn' is an integer number.
   1.225 +    // The end of the range is not checked, instead we return up to
   1.226 +    // the end of the resource and the client is informed of this via
   1.227 +    // the content-range header.
   1.228 +    NS_NAMED_LITERAL_CSTRING(byteRange, "Range: bytes=");
   1.229 +    const char* s = strstr(line.get(), byteRange.get());
   1.230 +    if (s) {
   1.231 +      start = strtoll(s+byteRange.Length(), nullptr, 10);
   1.232 +
   1.233 +      // Clamp 'start' to be between 0 and the resource length.
   1.234 +      start = std::max(0ll, std::min(resource->GetLength(), start));
   1.235 +    }
   1.236 +  }
   1.237 +
   1.238 +  // HTTP response to use if this is a non byte range request
   1.239 +  const char* response_normal = "HTTP/1.1 200 OK\r\n";
   1.240 +
   1.241 +  // HTTP response to use if this is a byte range request
   1.242 +  const char* response_range = "HTTP/1.1 206 Partial Content\r\n";
   1.243 +
   1.244 +  // End of HTTP reponse headers is indicated by an empty line.
   1.245 +  const char* response_end = "\r\n";
   1.246 +
   1.247 +  // If the request was a byte range request, we need to read from the
   1.248 +  // requested offset. If the resource is non-seekable, or the seek
   1.249 +  // fails, then the start offset is set back to zero. This results in all
   1.250 +  // HTTP response data being as if the byte range request was not made.
   1.251 +  if (start > 0 && !resource->IsTransportSeekable()) {
   1.252 +    start = 0;
   1.253 +  }
   1.254 +
   1.255 +  const char* response_line = start > 0 ?
   1.256 +                                response_range :
   1.257 +                                response_normal;
   1.258 +  rv = WriteAll(response_line, strlen(response_line));
   1.259 +  if (NS_FAILED(rv)) { Shutdown(); return NS_OK; }
   1.260 +
   1.261 +  // Buffer used for reading from the input stream and writing to
   1.262 +  // the output stream. The buffer size should be big enough for the
   1.263 +  // HTTP response headers sent below. A static_assert ensures
   1.264 +  // this where the buffer is used.
   1.265 +  const int buffer_size = 32768;
   1.266 +  nsAutoArrayPtr<char> b(new char[buffer_size]);
   1.267 +
   1.268 +  // If we know the length of the resource, send a Content-Length header.
   1.269 +  int64_t contentlength = resource->GetLength() - start;
   1.270 +  if (contentlength > 0) {
   1.271 +    static_assert (buffer_size > 1024,
   1.272 +                   "buffer_size must be large enough "
   1.273 +                   "to hold response headers");
   1.274 +    snprintf(b, buffer_size, "Content-Length: %lld\r\n", contentlength);
   1.275 +    rv = WriteAll(b, strlen(b));
   1.276 +    if (NS_FAILED(rv)) { Shutdown(); return NS_OK; }
   1.277 +  }
   1.278 +
   1.279 +  // If the request was a byte range request, respond with a Content-Range
   1.280 +  // header which details the extent of the data returned.
   1.281 +  if (start > 0) {
   1.282 +    static_assert (buffer_size > 1024,
   1.283 +                   "buffer_size must be large enough "
   1.284 +                   "to hold response headers");
   1.285 +    snprintf(b, buffer_size, "Content-Range: bytes %lld-%lld/%lld\r\n",
   1.286 +             start, resource->GetLength() - 1, resource->GetLength());
   1.287 +    rv = WriteAll(b, strlen(b));
   1.288 +    if (NS_FAILED(rv)) { Shutdown(); return NS_OK; }
   1.289 +  }
   1.290 +
   1.291 +  rv = WriteAll(response_end, strlen(response_end));
   1.292 +  if (NS_FAILED(rv)) { Shutdown(); return NS_OK; }
   1.293 +
   1.294 +  rv = mOutput->Flush();
   1.295 +  if (NS_FAILED(rv)) { Shutdown(); return NS_OK; }
   1.296 +
   1.297 +  // Read data from media resource
   1.298 +  uint32_t bytesRead = 0; // Number of bytes read/written to streams
   1.299 +  rv = resource->ReadAt(start, b, buffer_size, &bytesRead);
   1.300 +  while (NS_SUCCEEDED(rv) && bytesRead != 0) {
   1.301 +    // Keep track of what we think the starting position for the next read
   1.302 +    // is. This is used in subsequent ReadAt calls to ensure we are reading
   1.303 +    // from the correct offset in the case where another thread is reading
   1.304 +    // from th same MediaResource.
   1.305 +    start += bytesRead;
   1.306 +
   1.307 +    // Write data obtained from media resource to output stream
   1.308 +    rv = WriteAll(b, bytesRead);
   1.309 +    if (NS_FAILED (rv)) break;
   1.310 +
   1.311 +    rv = resource->ReadAt(start, b, 32768, &bytesRead);
   1.312 +  }
   1.313 +
   1.314 +  Shutdown();
   1.315 +  return NS_OK;
   1.316 +}
   1.317 +
   1.318 +void
   1.319 +ServeResourceEvent::Shutdown()
   1.320 +{
   1.321 +  // Cleanup resources and exit.
   1.322 +  mInput->Close();
   1.323 +  mOutput->Close();
   1.324 +
   1.325 +  // To shutdown the current thread we need to first exit this event.
   1.326 +  // The Shutdown event below is posted to the main thread to do this.
   1.327 +  nsCOMPtr<nsIRunnable> event = new ShutdownThreadEvent(NS_GetCurrentThread());
   1.328 +  NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
   1.329 +}
   1.330 +
   1.331 +/*
   1.332 +  This is the listener attached to the server socket. When an HTTP
   1.333 +  request is made by the client the OnSocketAccepted method is
   1.334 +  called. This method will spawn a thread to process the request.
   1.335 +  The thread receives a single event which does the parsing of
   1.336 +  the HTTP request and forwarding the data from the MediaResource
   1.337 +  to the output stream of the request.
   1.338 +
   1.339 +  The MediaResource used for providing the request data is obtained
   1.340 +  from the MediaResourceServer that created this listener, using the
   1.341 +  URL the client requested.
   1.342 +*/
   1.343 +class ResourceSocketListener : public nsIServerSocketListener
   1.344 +{
   1.345 +public:
   1.346 +  // The MediaResourceServer used to look up the MediaResource
   1.347 +  // on requests.
   1.348 +  nsRefPtr<MediaResourceServer> mServer;
   1.349 +
   1.350 +public:
   1.351 +  NS_DECL_THREADSAFE_ISUPPORTS
   1.352 +  NS_DECL_NSISERVERSOCKETLISTENER
   1.353 +
   1.354 +  ResourceSocketListener(MediaResourceServer* aServer) :
   1.355 +    mServer(aServer)
   1.356 +  {
   1.357 +  }
   1.358 +
   1.359 +  virtual ~ResourceSocketListener() { }
   1.360 +};
   1.361 +
   1.362 +NS_IMPL_ISUPPORTS(ResourceSocketListener, nsIServerSocketListener)
   1.363 +
   1.364 +NS_IMETHODIMP
   1.365 +ResourceSocketListener::OnSocketAccepted(nsIServerSocket* aServ,
   1.366 +                                         nsISocketTransport* aTrans)
   1.367 +{
   1.368 +  nsCOMPtr<nsIInputStream> input;
   1.369 +  nsCOMPtr<nsIOutputStream> output;
   1.370 +  nsresult rv;
   1.371 +
   1.372 +  rv = aTrans->OpenInputStream(nsITransport::OPEN_BLOCKING, 0, 0, getter_AddRefs(input));
   1.373 +  if (NS_FAILED(rv)) return rv;
   1.374 +
   1.375 +  rv = aTrans->OpenOutputStream(nsITransport::OPEN_BLOCKING, 0, 0, getter_AddRefs(output));
   1.376 +  if (NS_FAILED(rv)) return rv;
   1.377 +
   1.378 +  nsCOMPtr<nsIThread> thread;
   1.379 +  rv = NS_NewThread(getter_AddRefs(thread));
   1.380 +  if (NS_FAILED(rv)) return rv;
   1.381 +
   1.382 +  nsCOMPtr<nsIRunnable> event = new ServeResourceEvent(input.get(), output.get(), mServer);
   1.383 +  return thread->Dispatch(event, NS_DISPATCH_NORMAL);
   1.384 +}
   1.385 +
   1.386 +NS_IMETHODIMP
   1.387 +ResourceSocketListener::OnStopListening(nsIServerSocket* aServ, nsresult aStatus)
   1.388 +{
   1.389 +  return NS_OK;
   1.390 +}
   1.391 +
   1.392 +MediaResourceServer::MediaResourceServer() :
   1.393 +  mMutex("MediaResourceServer")
   1.394 +{
   1.395 +}
   1.396 +
   1.397 +NS_IMETHODIMP
   1.398 +MediaResourceServer::Run()
   1.399 +{
   1.400 +  MutexAutoLock lock(mMutex);
   1.401 +
   1.402 +  nsresult rv;
   1.403 +  mSocket = do_CreateInstance(NS_SERVERSOCKET_CONTRACTID, &rv);
   1.404 +  if (NS_FAILED(rv)) return rv;
   1.405 +
   1.406 +  rv = mSocket->InitSpecialConnection(-1,
   1.407 +                                      nsIServerSocket::LoopbackOnly
   1.408 +                                      | nsIServerSocket::KeepWhenOffline,
   1.409 +                                      -1);
   1.410 +  if (NS_FAILED(rv)) return rv;
   1.411 +
   1.412 +  rv = mSocket->AsyncListen(new ResourceSocketListener(this));
   1.413 +  if (NS_FAILED(rv)) return rv;
   1.414 +
   1.415 +  return NS_OK;
   1.416 +}
   1.417 +
   1.418 +/* static */
   1.419 +already_AddRefed<MediaResourceServer>
   1.420 +MediaResourceServer::Start()
   1.421 +{
   1.422 +  nsRefPtr<MediaResourceServer> server = new MediaResourceServer();
   1.423 +  NS_DispatchToMainThread(server, NS_DISPATCH_SYNC);
   1.424 +  return server.forget();
   1.425 +}
   1.426 +
   1.427 +void
   1.428 +MediaResourceServer::Stop()
   1.429 +{
   1.430 +  MutexAutoLock lock(mMutex);
   1.431 +  mSocket->Close();
   1.432 +  mSocket = nullptr;
   1.433 +}
   1.434 +
   1.435 +nsresult
   1.436 +MediaResourceServer::AppendRandomPath(nsCString& aUrl)
   1.437 +{
   1.438 +  // Use a cryptographic quality PRNG to generate raw random bytes
   1.439 +  // and convert that to a base64 string for use as an URL path. This
   1.440 +  // is based on code from nsExternalAppHandler::SetUpTempFile.
   1.441 +  nsresult rv;
   1.442 +  nsCOMPtr<nsIRandomGenerator> rg =
   1.443 +    do_GetService("@mozilla.org/security/random-generator;1", &rv);
   1.444 +  if (NS_FAILED(rv)) return rv;
   1.445 +
   1.446 +  // For each three bytes of random data we will get four bytes of
   1.447 +  // ASCII. Request a bit more to be safe and truncate to the length
   1.448 +  // we want at the end.
   1.449 +  const uint32_t wantedFileNameLength = 16;
   1.450 +  const uint32_t requiredBytesLength =
   1.451 +    static_cast<uint32_t>((wantedFileNameLength + 1) / 4 * 3);
   1.452 +
   1.453 +  uint8_t* buffer;
   1.454 +  rv = rg->GenerateRandomBytes(requiredBytesLength, &buffer);
   1.455 +  if (NS_FAILED(rv)) return rv;
   1.456 +
   1.457 +  nsAutoCString tempLeafName;
   1.458 +  nsDependentCSubstring randomData(reinterpret_cast<const char*>(buffer),
   1.459 +                                   requiredBytesLength);
   1.460 +  rv = Base64Encode(randomData, tempLeafName);
   1.461 +  NS_Free(buffer);
   1.462 +  buffer = nullptr;
   1.463 +  if (NS_FAILED (rv)) return rv;
   1.464 +
   1.465 +  tempLeafName.Truncate(wantedFileNameLength);
   1.466 +
   1.467 +  // Base64 characters are alphanumeric (a-zA-Z0-9) and '+' and '/', so we need
   1.468 +  // to replace illegal characters -- notably '/'
   1.469 +  tempLeafName.ReplaceChar(FILE_PATH_SEPARATOR FILE_ILLEGAL_CHARACTERS, '_');
   1.470 +
   1.471 +  aUrl += "/";
   1.472 +  aUrl += tempLeafName;
   1.473 +
   1.474 +  return NS_OK;
   1.475 +}
   1.476 +
   1.477 +nsresult
   1.478 +MediaResourceServer::AddResource(mozilla::MediaResource* aResource, nsCString& aUrl)
   1.479 +{
   1.480 +  nsCString url = GetURLPrefix();
   1.481 +  nsresult rv = AppendRandomPath(url);
   1.482 +  if (NS_FAILED (rv)) return rv;
   1.483 +
   1.484 +  {
   1.485 +    MutexAutoLock lock(mMutex);
   1.486 +
   1.487 +    // Adding a resource URL that already exists is considered an error.
   1.488 +    if (mResources.find(aUrl) != mResources.end()) return NS_ERROR_FAILURE;
   1.489 +    mResources[url] = aResource;
   1.490 +  }
   1.491 +
   1.492 +  aUrl = url;
   1.493 +
   1.494 +  return NS_OK;
   1.495 +}
   1.496 +
   1.497 +void
   1.498 +MediaResourceServer::RemoveResource(nsCString const& aUrl)
   1.499 +{
   1.500 +  MutexAutoLock lock(mMutex);
   1.501 +  mResources.erase(aUrl);
   1.502 +}
   1.503 +
   1.504 +nsCString
   1.505 +MediaResourceServer::GetURLPrefix()
   1.506 +{
   1.507 +  MutexAutoLock lock(mMutex);
   1.508 +
   1.509 +  int32_t port = 0;
   1.510 +  nsresult rv = mSocket->GetPort(&port);
   1.511 +  if (NS_FAILED (rv) || port < 0) {
   1.512 +    return nsCString("");
   1.513 +  }
   1.514 +
   1.515 +  char buffer[256];
   1.516 +  snprintf(buffer, sizeof(buffer), "http://127.0.0.1:%d", port >= 0 ? port : 0);
   1.517 +  return nsCString(buffer);
   1.518 +}
   1.519 +
   1.520 +already_AddRefed<MediaResource>
   1.521 +MediaResourceServer::GetResource(nsCString const& aUrl)
   1.522 +{
   1.523 +  MutexAutoLock lock(mMutex);
   1.524 +  ResourceMap::const_iterator it = mResources.find(aUrl);
   1.525 +  if (it == mResources.end()) return nullptr;
   1.526 +
   1.527 +  nsRefPtr<MediaResource> resource = it->second;
   1.528 +  return resource.forget();
   1.529 +}

mercurial