content/media/plugins/MediaResourceServer.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
michael@0 3 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
michael@0 5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6 #include "mozilla/Assertions.h"
michael@0 7 #include "mozilla/Base64.h"
michael@0 8 #include "nsThreadUtils.h"
michael@0 9 #include "nsIServiceManager.h"
michael@0 10 #include "nsISocketTransport.h"
michael@0 11 #include "nsIOutputStream.h"
michael@0 12 #include "nsIInputStream.h"
michael@0 13 #include "nsIRandomGenerator.h"
michael@0 14 #include "nsReadLine.h"
michael@0 15 #include "nsNetCID.h"
michael@0 16 #include "VideoUtils.h"
michael@0 17 #include "MediaResource.h"
michael@0 18 #include "MediaResourceServer.h"
michael@0 19
michael@0 20 #if defined(_MSC_VER)
michael@0 21 #define strtoll _strtoi64
michael@0 22 #define snprintf _snprintf_s
michael@0 23 #endif
michael@0 24
michael@0 25 using namespace mozilla;
michael@0 26
michael@0 27 /*
michael@0 28 ReadCRLF is a variant of NS_ReadLine from nsReadLine.h that deals
michael@0 29 with the carriage return/line feed requirements of HTTP requests.
michael@0 30 */
michael@0 31 template<typename CharT, class StreamType, class StringType>
michael@0 32 nsresult
michael@0 33 ReadCRLF (StreamType* aStream, nsLineBuffer<CharT> * aBuffer,
michael@0 34 StringType & aLine, bool *aMore)
michael@0 35 {
michael@0 36 // eollast is true if the last character in the buffer is a '\r',
michael@0 37 // signaling a potential '\r\n' sequence split between reads.
michael@0 38 bool eollast = false;
michael@0 39
michael@0 40 aLine.Truncate();
michael@0 41
michael@0 42 while (1) { // will be returning out of this loop on eol or eof
michael@0 43 if (aBuffer->start == aBuffer->end) { // buffer is empty. Read into it.
michael@0 44 uint32_t bytesRead;
michael@0 45 nsresult rv = aStream->Read(aBuffer->buf, kLineBufferSize, &bytesRead);
michael@0 46 if (NS_FAILED(rv) || bytesRead == 0) {
michael@0 47 *aMore = false;
michael@0 48 return rv;
michael@0 49 }
michael@0 50 aBuffer->start = aBuffer->buf;
michael@0 51 aBuffer->end = aBuffer->buf + bytesRead;
michael@0 52 *(aBuffer->end) = '\0';
michael@0 53 }
michael@0 54
michael@0 55 /*
michael@0 56 * Walk the buffer looking for an end-of-line.
michael@0 57 * There are 4 cases to consider:
michael@0 58 * 1. the CR char is the last char in the buffer
michael@0 59 * 2. the CRLF sequence are the last characters in the buffer
michael@0 60 * 3. the CRLF sequence + one or more chars at the end of the buffer
michael@0 61 * we need at least one char after the first CRLF sequence to
michael@0 62 * set |aMore| correctly.
michael@0 63 * 4. The LF character is the first char in the buffer when eollast is
michael@0 64 * true.
michael@0 65 */
michael@0 66 CharT* current = aBuffer->start;
michael@0 67 if (eollast) { // Case 4
michael@0 68 if (*current == '\n') {
michael@0 69 aBuffer->start = ++current;
michael@0 70 *aMore = true;
michael@0 71 return NS_OK;
michael@0 72 }
michael@0 73 else {
michael@0 74 eollast = false;
michael@0 75 aLine.Append('\r');
michael@0 76 }
michael@0 77 }
michael@0 78 // Cases 2 and 3
michael@0 79 for ( ; current < aBuffer->end-1; ++current) {
michael@0 80 if (*current == '\r' && *(current+1) == '\n') {
michael@0 81 *current++ = '\0';
michael@0 82 *current++ = '\0';
michael@0 83 aLine.Append(aBuffer->start);
michael@0 84 aBuffer->start = current;
michael@0 85 *aMore = true;
michael@0 86 return NS_OK;
michael@0 87 }
michael@0 88 }
michael@0 89 // Case 1
michael@0 90 if (*current == '\r') {
michael@0 91 eollast = true;
michael@0 92 *current++ = '\0';
michael@0 93 }
michael@0 94
michael@0 95 aLine.Append(aBuffer->start);
michael@0 96 aBuffer->start = aBuffer->end; // mark the buffer empty
michael@0 97 }
michael@0 98 }
michael@0 99
michael@0 100 // Each client HTTP request results in a thread being spawned to process it.
michael@0 101 // That thread has a single event dispatched to it which handles the HTTP
michael@0 102 // protocol. It parses the headers and forwards data from the MediaResource
michael@0 103 // associated with the URL back to client. When the request is complete it will
michael@0 104 // shutdown the thread.
michael@0 105 class ServeResourceEvent : public nsRunnable {
michael@0 106 private:
michael@0 107 // Reading from this reads the data sent from the client.
michael@0 108 nsCOMPtr<nsIInputStream> mInput;
michael@0 109
michael@0 110 // Writing to this sends data to the client.
michael@0 111 nsCOMPtr<nsIOutputStream> mOutput;
michael@0 112
michael@0 113 // The MediaResourceServer that owns the MediaResource instances
michael@0 114 // served. This is used to lookup the MediaResource from the URL.
michael@0 115 nsRefPtr<MediaResourceServer> mServer;
michael@0 116
michael@0 117 // Write 'aBufferLength' bytes from 'aBuffer' to 'mOutput'. This
michael@0 118 // method ensures all the data is written by checking the number
michael@0 119 // of bytes returned from the output streams 'Write' method and
michael@0 120 // looping until done.
michael@0 121 nsresult WriteAll(char const* aBuffer, int32_t aBufferLength);
michael@0 122
michael@0 123 public:
michael@0 124 ServeResourceEvent(nsIInputStream* aInput, nsIOutputStream* aOutput,
michael@0 125 MediaResourceServer* aServer)
michael@0 126 : mInput(aInput), mOutput(aOutput), mServer(aServer) {}
michael@0 127
michael@0 128 // This method runs on the thread and exits when it has completed the
michael@0 129 // HTTP request.
michael@0 130 NS_IMETHOD Run();
michael@0 131
michael@0 132 // Given the first line of an HTTP request, parse the URL requested and
michael@0 133 // return the MediaResource for that URL.
michael@0 134 already_AddRefed<MediaResource> GetMediaResource(nsCString const& aHTTPRequest);
michael@0 135
michael@0 136 // Gracefully shutdown the thread and cleanup resources
michael@0 137 void Shutdown();
michael@0 138 };
michael@0 139
michael@0 140 nsresult
michael@0 141 ServeResourceEvent::WriteAll(char const* aBuffer, int32_t aBufferLength)
michael@0 142 {
michael@0 143 while (aBufferLength > 0) {
michael@0 144 uint32_t written = 0;
michael@0 145 nsresult rv = mOutput->Write(aBuffer, aBufferLength, &written);
michael@0 146 if (NS_FAILED (rv)) return rv;
michael@0 147
michael@0 148 aBufferLength -= written;
michael@0 149 aBuffer += written;
michael@0 150 }
michael@0 151
michael@0 152 return NS_OK;
michael@0 153 }
michael@0 154
michael@0 155 already_AddRefed<MediaResource>
michael@0 156 ServeResourceEvent::GetMediaResource(nsCString const& aHTTPRequest)
michael@0 157 {
michael@0 158 // Check that the HTTP method is GET
michael@0 159 const char* HTTP_METHOD = "GET ";
michael@0 160 if (strncmp(aHTTPRequest.get(), HTTP_METHOD, strlen(HTTP_METHOD)) != 0) {
michael@0 161 return nullptr;
michael@0 162 }
michael@0 163
michael@0 164 const char* url_start = strchr(aHTTPRequest.get(), ' ');
michael@0 165 if (!url_start) {
michael@0 166 return nullptr;
michael@0 167 }
michael@0 168
michael@0 169 const char* url_end = strrchr(++url_start, ' ');
michael@0 170 if (!url_end) {
michael@0 171 return nullptr;
michael@0 172 }
michael@0 173
michael@0 174 // The path extracted from the HTTP request is used as a key in hash
michael@0 175 // table. It is not related to retrieving data from the filesystem so
michael@0 176 // we don't need to do any sanity checking on ".." paths and similar
michael@0 177 // exploits.
michael@0 178 nsCString relative(url_start, url_end - url_start);
michael@0 179 nsRefPtr<MediaResource> resource =
michael@0 180 mServer->GetResource(mServer->GetURLPrefix() + relative);
michael@0 181 return resource.forget();
michael@0 182 }
michael@0 183
michael@0 184 NS_IMETHODIMP
michael@0 185 ServeResourceEvent::Run() {
michael@0 186 bool more = false; // Are there HTTP headers to read after the first line
michael@0 187 nsCString line; // Contains the current line read from input stream
michael@0 188 nsLineBuffer<char>* buffer = new nsLineBuffer<char>();
michael@0 189 nsresult rv = ReadCRLF(mInput.get(), buffer, line, &more);
michael@0 190 if (NS_FAILED(rv)) { Shutdown(); return rv; }
michael@0 191
michael@0 192 // First line contains the HTTP GET request. Extract the URL and obtain
michael@0 193 // the MediaResource for it.
michael@0 194 nsRefPtr<MediaResource> resource = GetMediaResource(line);
michael@0 195 if (!resource) {
michael@0 196 const char* response_404 = "HTTP/1.1 404 Not Found\r\n"
michael@0 197 "Content-Length: 0\r\n\r\n";
michael@0 198 rv = WriteAll(response_404, strlen(response_404));
michael@0 199 Shutdown();
michael@0 200 return rv;
michael@0 201 }
michael@0 202
michael@0 203 // Offset in bytes to start reading from resource.
michael@0 204 // This is zero by default but can be set to another starting value if
michael@0 205 // this HTTP request includes a byte range request header.
michael@0 206 int64_t start = 0;
michael@0 207
michael@0 208 // Keep reading lines until we get a zero length line, which is the HTTP
michael@0 209 // protocol's way of signifying the end of headers and start of body, or
michael@0 210 // until we have no more data to read.
michael@0 211 while (more && line.Length() > 0) {
michael@0 212 rv = ReadCRLF(mInput.get(), buffer, line, &more);
michael@0 213 if (NS_FAILED(rv)) { Shutdown(); return rv; }
michael@0 214
michael@0 215 // Look for a byte range request header. If there is one, set the
michael@0 216 // media resource offset to start from to that requested. Here we
michael@0 217 // only check for the range request format used by Android rather
michael@0 218 // than implementing all possibilities in the HTTP specification.
michael@0 219 // That is, the range request is of the form:
michael@0 220 // Range: bytes=nnnn-
michael@0 221 // Were 'nnnn' is an integer number.
michael@0 222 // The end of the range is not checked, instead we return up to
michael@0 223 // the end of the resource and the client is informed of this via
michael@0 224 // the content-range header.
michael@0 225 NS_NAMED_LITERAL_CSTRING(byteRange, "Range: bytes=");
michael@0 226 const char* s = strstr(line.get(), byteRange.get());
michael@0 227 if (s) {
michael@0 228 start = strtoll(s+byteRange.Length(), nullptr, 10);
michael@0 229
michael@0 230 // Clamp 'start' to be between 0 and the resource length.
michael@0 231 start = std::max(0ll, std::min(resource->GetLength(), start));
michael@0 232 }
michael@0 233 }
michael@0 234
michael@0 235 // HTTP response to use if this is a non byte range request
michael@0 236 const char* response_normal = "HTTP/1.1 200 OK\r\n";
michael@0 237
michael@0 238 // HTTP response to use if this is a byte range request
michael@0 239 const char* response_range = "HTTP/1.1 206 Partial Content\r\n";
michael@0 240
michael@0 241 // End of HTTP reponse headers is indicated by an empty line.
michael@0 242 const char* response_end = "\r\n";
michael@0 243
michael@0 244 // If the request was a byte range request, we need to read from the
michael@0 245 // requested offset. If the resource is non-seekable, or the seek
michael@0 246 // fails, then the start offset is set back to zero. This results in all
michael@0 247 // HTTP response data being as if the byte range request was not made.
michael@0 248 if (start > 0 && !resource->IsTransportSeekable()) {
michael@0 249 start = 0;
michael@0 250 }
michael@0 251
michael@0 252 const char* response_line = start > 0 ?
michael@0 253 response_range :
michael@0 254 response_normal;
michael@0 255 rv = WriteAll(response_line, strlen(response_line));
michael@0 256 if (NS_FAILED(rv)) { Shutdown(); return NS_OK; }
michael@0 257
michael@0 258 // Buffer used for reading from the input stream and writing to
michael@0 259 // the output stream. The buffer size should be big enough for the
michael@0 260 // HTTP response headers sent below. A static_assert ensures
michael@0 261 // this where the buffer is used.
michael@0 262 const int buffer_size = 32768;
michael@0 263 nsAutoArrayPtr<char> b(new char[buffer_size]);
michael@0 264
michael@0 265 // If we know the length of the resource, send a Content-Length header.
michael@0 266 int64_t contentlength = resource->GetLength() - start;
michael@0 267 if (contentlength > 0) {
michael@0 268 static_assert (buffer_size > 1024,
michael@0 269 "buffer_size must be large enough "
michael@0 270 "to hold response headers");
michael@0 271 snprintf(b, buffer_size, "Content-Length: %lld\r\n", contentlength);
michael@0 272 rv = WriteAll(b, strlen(b));
michael@0 273 if (NS_FAILED(rv)) { Shutdown(); return NS_OK; }
michael@0 274 }
michael@0 275
michael@0 276 // If the request was a byte range request, respond with a Content-Range
michael@0 277 // header which details the extent of the data returned.
michael@0 278 if (start > 0) {
michael@0 279 static_assert (buffer_size > 1024,
michael@0 280 "buffer_size must be large enough "
michael@0 281 "to hold response headers");
michael@0 282 snprintf(b, buffer_size, "Content-Range: bytes %lld-%lld/%lld\r\n",
michael@0 283 start, resource->GetLength() - 1, resource->GetLength());
michael@0 284 rv = WriteAll(b, strlen(b));
michael@0 285 if (NS_FAILED(rv)) { Shutdown(); return NS_OK; }
michael@0 286 }
michael@0 287
michael@0 288 rv = WriteAll(response_end, strlen(response_end));
michael@0 289 if (NS_FAILED(rv)) { Shutdown(); return NS_OK; }
michael@0 290
michael@0 291 rv = mOutput->Flush();
michael@0 292 if (NS_FAILED(rv)) { Shutdown(); return NS_OK; }
michael@0 293
michael@0 294 // Read data from media resource
michael@0 295 uint32_t bytesRead = 0; // Number of bytes read/written to streams
michael@0 296 rv = resource->ReadAt(start, b, buffer_size, &bytesRead);
michael@0 297 while (NS_SUCCEEDED(rv) && bytesRead != 0) {
michael@0 298 // Keep track of what we think the starting position for the next read
michael@0 299 // is. This is used in subsequent ReadAt calls to ensure we are reading
michael@0 300 // from the correct offset in the case where another thread is reading
michael@0 301 // from th same MediaResource.
michael@0 302 start += bytesRead;
michael@0 303
michael@0 304 // Write data obtained from media resource to output stream
michael@0 305 rv = WriteAll(b, bytesRead);
michael@0 306 if (NS_FAILED (rv)) break;
michael@0 307
michael@0 308 rv = resource->ReadAt(start, b, 32768, &bytesRead);
michael@0 309 }
michael@0 310
michael@0 311 Shutdown();
michael@0 312 return NS_OK;
michael@0 313 }
michael@0 314
michael@0 315 void
michael@0 316 ServeResourceEvent::Shutdown()
michael@0 317 {
michael@0 318 // Cleanup resources and exit.
michael@0 319 mInput->Close();
michael@0 320 mOutput->Close();
michael@0 321
michael@0 322 // To shutdown the current thread we need to first exit this event.
michael@0 323 // The Shutdown event below is posted to the main thread to do this.
michael@0 324 nsCOMPtr<nsIRunnable> event = new ShutdownThreadEvent(NS_GetCurrentThread());
michael@0 325 NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
michael@0 326 }
michael@0 327
michael@0 328 /*
michael@0 329 This is the listener attached to the server socket. When an HTTP
michael@0 330 request is made by the client the OnSocketAccepted method is
michael@0 331 called. This method will spawn a thread to process the request.
michael@0 332 The thread receives a single event which does the parsing of
michael@0 333 the HTTP request and forwarding the data from the MediaResource
michael@0 334 to the output stream of the request.
michael@0 335
michael@0 336 The MediaResource used for providing the request data is obtained
michael@0 337 from the MediaResourceServer that created this listener, using the
michael@0 338 URL the client requested.
michael@0 339 */
michael@0 340 class ResourceSocketListener : public nsIServerSocketListener
michael@0 341 {
michael@0 342 public:
michael@0 343 // The MediaResourceServer used to look up the MediaResource
michael@0 344 // on requests.
michael@0 345 nsRefPtr<MediaResourceServer> mServer;
michael@0 346
michael@0 347 public:
michael@0 348 NS_DECL_THREADSAFE_ISUPPORTS
michael@0 349 NS_DECL_NSISERVERSOCKETLISTENER
michael@0 350
michael@0 351 ResourceSocketListener(MediaResourceServer* aServer) :
michael@0 352 mServer(aServer)
michael@0 353 {
michael@0 354 }
michael@0 355
michael@0 356 virtual ~ResourceSocketListener() { }
michael@0 357 };
michael@0 358
michael@0 359 NS_IMPL_ISUPPORTS(ResourceSocketListener, nsIServerSocketListener)
michael@0 360
michael@0 361 NS_IMETHODIMP
michael@0 362 ResourceSocketListener::OnSocketAccepted(nsIServerSocket* aServ,
michael@0 363 nsISocketTransport* aTrans)
michael@0 364 {
michael@0 365 nsCOMPtr<nsIInputStream> input;
michael@0 366 nsCOMPtr<nsIOutputStream> output;
michael@0 367 nsresult rv;
michael@0 368
michael@0 369 rv = aTrans->OpenInputStream(nsITransport::OPEN_BLOCKING, 0, 0, getter_AddRefs(input));
michael@0 370 if (NS_FAILED(rv)) return rv;
michael@0 371
michael@0 372 rv = aTrans->OpenOutputStream(nsITransport::OPEN_BLOCKING, 0, 0, getter_AddRefs(output));
michael@0 373 if (NS_FAILED(rv)) return rv;
michael@0 374
michael@0 375 nsCOMPtr<nsIThread> thread;
michael@0 376 rv = NS_NewThread(getter_AddRefs(thread));
michael@0 377 if (NS_FAILED(rv)) return rv;
michael@0 378
michael@0 379 nsCOMPtr<nsIRunnable> event = new ServeResourceEvent(input.get(), output.get(), mServer);
michael@0 380 return thread->Dispatch(event, NS_DISPATCH_NORMAL);
michael@0 381 }
michael@0 382
michael@0 383 NS_IMETHODIMP
michael@0 384 ResourceSocketListener::OnStopListening(nsIServerSocket* aServ, nsresult aStatus)
michael@0 385 {
michael@0 386 return NS_OK;
michael@0 387 }
michael@0 388
michael@0 389 MediaResourceServer::MediaResourceServer() :
michael@0 390 mMutex("MediaResourceServer")
michael@0 391 {
michael@0 392 }
michael@0 393
michael@0 394 NS_IMETHODIMP
michael@0 395 MediaResourceServer::Run()
michael@0 396 {
michael@0 397 MutexAutoLock lock(mMutex);
michael@0 398
michael@0 399 nsresult rv;
michael@0 400 mSocket = do_CreateInstance(NS_SERVERSOCKET_CONTRACTID, &rv);
michael@0 401 if (NS_FAILED(rv)) return rv;
michael@0 402
michael@0 403 rv = mSocket->InitSpecialConnection(-1,
michael@0 404 nsIServerSocket::LoopbackOnly
michael@0 405 | nsIServerSocket::KeepWhenOffline,
michael@0 406 -1);
michael@0 407 if (NS_FAILED(rv)) return rv;
michael@0 408
michael@0 409 rv = mSocket->AsyncListen(new ResourceSocketListener(this));
michael@0 410 if (NS_FAILED(rv)) return rv;
michael@0 411
michael@0 412 return NS_OK;
michael@0 413 }
michael@0 414
michael@0 415 /* static */
michael@0 416 already_AddRefed<MediaResourceServer>
michael@0 417 MediaResourceServer::Start()
michael@0 418 {
michael@0 419 nsRefPtr<MediaResourceServer> server = new MediaResourceServer();
michael@0 420 NS_DispatchToMainThread(server, NS_DISPATCH_SYNC);
michael@0 421 return server.forget();
michael@0 422 }
michael@0 423
michael@0 424 void
michael@0 425 MediaResourceServer::Stop()
michael@0 426 {
michael@0 427 MutexAutoLock lock(mMutex);
michael@0 428 mSocket->Close();
michael@0 429 mSocket = nullptr;
michael@0 430 }
michael@0 431
michael@0 432 nsresult
michael@0 433 MediaResourceServer::AppendRandomPath(nsCString& aUrl)
michael@0 434 {
michael@0 435 // Use a cryptographic quality PRNG to generate raw random bytes
michael@0 436 // and convert that to a base64 string for use as an URL path. This
michael@0 437 // is based on code from nsExternalAppHandler::SetUpTempFile.
michael@0 438 nsresult rv;
michael@0 439 nsCOMPtr<nsIRandomGenerator> rg =
michael@0 440 do_GetService("@mozilla.org/security/random-generator;1", &rv);
michael@0 441 if (NS_FAILED(rv)) return rv;
michael@0 442
michael@0 443 // For each three bytes of random data we will get four bytes of
michael@0 444 // ASCII. Request a bit more to be safe and truncate to the length
michael@0 445 // we want at the end.
michael@0 446 const uint32_t wantedFileNameLength = 16;
michael@0 447 const uint32_t requiredBytesLength =
michael@0 448 static_cast<uint32_t>((wantedFileNameLength + 1) / 4 * 3);
michael@0 449
michael@0 450 uint8_t* buffer;
michael@0 451 rv = rg->GenerateRandomBytes(requiredBytesLength, &buffer);
michael@0 452 if (NS_FAILED(rv)) return rv;
michael@0 453
michael@0 454 nsAutoCString tempLeafName;
michael@0 455 nsDependentCSubstring randomData(reinterpret_cast<const char*>(buffer),
michael@0 456 requiredBytesLength);
michael@0 457 rv = Base64Encode(randomData, tempLeafName);
michael@0 458 NS_Free(buffer);
michael@0 459 buffer = nullptr;
michael@0 460 if (NS_FAILED (rv)) return rv;
michael@0 461
michael@0 462 tempLeafName.Truncate(wantedFileNameLength);
michael@0 463
michael@0 464 // Base64 characters are alphanumeric (a-zA-Z0-9) and '+' and '/', so we need
michael@0 465 // to replace illegal characters -- notably '/'
michael@0 466 tempLeafName.ReplaceChar(FILE_PATH_SEPARATOR FILE_ILLEGAL_CHARACTERS, '_');
michael@0 467
michael@0 468 aUrl += "/";
michael@0 469 aUrl += tempLeafName;
michael@0 470
michael@0 471 return NS_OK;
michael@0 472 }
michael@0 473
michael@0 474 nsresult
michael@0 475 MediaResourceServer::AddResource(mozilla::MediaResource* aResource, nsCString& aUrl)
michael@0 476 {
michael@0 477 nsCString url = GetURLPrefix();
michael@0 478 nsresult rv = AppendRandomPath(url);
michael@0 479 if (NS_FAILED (rv)) return rv;
michael@0 480
michael@0 481 {
michael@0 482 MutexAutoLock lock(mMutex);
michael@0 483
michael@0 484 // Adding a resource URL that already exists is considered an error.
michael@0 485 if (mResources.find(aUrl) != mResources.end()) return NS_ERROR_FAILURE;
michael@0 486 mResources[url] = aResource;
michael@0 487 }
michael@0 488
michael@0 489 aUrl = url;
michael@0 490
michael@0 491 return NS_OK;
michael@0 492 }
michael@0 493
michael@0 494 void
michael@0 495 MediaResourceServer::RemoveResource(nsCString const& aUrl)
michael@0 496 {
michael@0 497 MutexAutoLock lock(mMutex);
michael@0 498 mResources.erase(aUrl);
michael@0 499 }
michael@0 500
michael@0 501 nsCString
michael@0 502 MediaResourceServer::GetURLPrefix()
michael@0 503 {
michael@0 504 MutexAutoLock lock(mMutex);
michael@0 505
michael@0 506 int32_t port = 0;
michael@0 507 nsresult rv = mSocket->GetPort(&port);
michael@0 508 if (NS_FAILED (rv) || port < 0) {
michael@0 509 return nsCString("");
michael@0 510 }
michael@0 511
michael@0 512 char buffer[256];
michael@0 513 snprintf(buffer, sizeof(buffer), "http://127.0.0.1:%d", port >= 0 ? port : 0);
michael@0 514 return nsCString(buffer);
michael@0 515 }
michael@0 516
michael@0 517 already_AddRefed<MediaResource>
michael@0 518 MediaResourceServer::GetResource(nsCString const& aUrl)
michael@0 519 {
michael@0 520 MutexAutoLock lock(mMutex);
michael@0 521 ResourceMap::const_iterator it = mResources.find(aUrl);
michael@0 522 if (it == mResources.end()) return nullptr;
michael@0 523
michael@0 524 nsRefPtr<MediaResource> resource = it->second;
michael@0 525 return resource.forget();
michael@0 526 }

mercurial