Home | History | Annotate | Download | only in ocsp
      1 // Copyright (c) 2009 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 "net/ocsp/nss_ocsp.h"
      6 
      7 #include <certt.h>
      8 #include <certdb.h>
      9 #include <ocsp.h>
     10 #include <nspr.h>
     11 #include <nss.h>
     12 #include <secerr.h>
     13 
     14 #include <string>
     15 
     16 #include "base/compiler_specific.h"
     17 #include "base/condition_variable.h"
     18 #include "base/logging.h"
     19 #include "base/message_loop.h"
     20 #include "base/singleton.h"
     21 #include "base/thread.h"
     22 #include "base/time.h"
     23 #include "googleurl/src/gurl.h"
     24 #include "net/base/io_buffer.h"
     25 #include "net/base/load_flags.h"
     26 #include "net/http/http_response_headers.h"
     27 #include "net/url_request/url_request.h"
     28 #include "net/url_request/url_request_context.h"
     29 
     30 namespace {
     31 
     32 const int kRecvBufferSize = 4096;
     33 
     34 // All OCSP handlers should be called in the context of
     35 // CertVerifier's thread (i.e. worker pool, not on the I/O thread).
     36 // It supports blocking mode only.
     37 
     38 SECStatus OCSPCreateSession(const char* host, PRUint16 portnum,
     39                             SEC_HTTP_SERVER_SESSION* pSession);
     40 SECStatus OCSPKeepAliveSession(SEC_HTTP_SERVER_SESSION session,
     41                                PRPollDesc **pPollDesc);
     42 SECStatus OCSPFreeSession(SEC_HTTP_SERVER_SESSION session);
     43 
     44 SECStatus OCSPCreate(SEC_HTTP_SERVER_SESSION session,
     45                      const char* http_protocol_variant,
     46                      const char* path_and_query_string,
     47                      const char* http_request_method,
     48                      const PRIntervalTime timeout,
     49                      SEC_HTTP_REQUEST_SESSION* pRequest);
     50 SECStatus OCSPSetPostData(SEC_HTTP_REQUEST_SESSION request,
     51                           const char* http_data,
     52                           const PRUint32 http_data_len,
     53                           const char* http_content_type);
     54 SECStatus OCSPAddHeader(SEC_HTTP_REQUEST_SESSION request,
     55                         const char* http_header_name,
     56                         const char* http_header_value);
     57 SECStatus OCSPTrySendAndReceive(SEC_HTTP_REQUEST_SESSION request,
     58                                 PRPollDesc** pPollDesc,
     59                                 PRUint16* http_response_code,
     60                                 const char** http_response_content_type,
     61                                 const char** http_response_headers,
     62                                 const char** http_response_data,
     63                                 PRUint32* http_response_data_len);
     64 SECStatus OCSPFree(SEC_HTTP_REQUEST_SESSION request);
     65 
     66 class OCSPInitSingleton : public MessageLoop::DestructionObserver {
     67  public:
     68   // Called on IO thread.
     69   virtual void WillDestroyCurrentMessageLoop() {
     70     AutoLock autolock(lock_);
     71     DCHECK_EQ(MessageLoopForIO::current(), io_loop_);
     72     io_loop_ = NULL;
     73     request_context_ = NULL;
     74   };
     75 
     76   // Called from worker thread.
     77   void PostTaskToIOLoop(
     78       const tracked_objects::Location& from_here, Task* task) {
     79     AutoLock autolock(lock_);
     80     if (io_loop_)
     81       io_loop_->PostTask(from_here, task);
     82   }
     83 
     84   // This is static method because it is called before NSS initialization,
     85   // that is, before OCSPInitSingleton is initialized.
     86   static void set_url_request_context(URLRequestContext* request_context) {
     87     request_context_ = request_context;
     88   }
     89   static URLRequestContext* url_request_context() {
     90     return request_context_;
     91   }
     92 
     93  private:
     94   friend struct DefaultSingletonTraits<OCSPInitSingleton>;
     95 
     96   OCSPInitSingleton()
     97       : io_loop_(MessageLoopForIO::current()) {
     98     DCHECK(io_loop_);
     99     io_loop_->AddDestructionObserver(this);
    100     client_fcn_.version = 1;
    101     SEC_HttpClientFcnV1Struct *ft = &client_fcn_.fcnTable.ftable1;
    102     ft->createSessionFcn = OCSPCreateSession;
    103     ft->keepAliveSessionFcn = OCSPKeepAliveSession;
    104     ft->freeSessionFcn = OCSPFreeSession;
    105     ft->createFcn = OCSPCreate;
    106     ft->setPostDataFcn = OCSPSetPostData;
    107     ft->addHeaderFcn = OCSPAddHeader;
    108     ft->trySendAndReceiveFcn = OCSPTrySendAndReceive;
    109     ft->cancelFcn = NULL;
    110     ft->freeFcn = OCSPFree;
    111     SECStatus status = SEC_RegisterDefaultHttpClient(&client_fcn_);
    112     if (status != SECSuccess) {
    113       NOTREACHED() << "Error initializing OCSP: " << PR_GetError();
    114     }
    115   }
    116 
    117   virtual ~OCSPInitSingleton() {
    118     // IO thread was already deleted before the singleton is deleted
    119     // in AtExitManager.
    120     AutoLock autolock(lock_);
    121     DCHECK(!io_loop_);
    122     DCHECK(!request_context_);
    123   }
    124 
    125   SEC_HttpClientFcn client_fcn_;
    126 
    127   // |lock_| protects |io_loop_|.
    128   Lock lock_;
    129   // I/O thread.
    130   MessageLoop* io_loop_;  // I/O thread
    131   // URLRequestContext for OCSP handlers.
    132   static URLRequestContext* request_context_;
    133 
    134   DISALLOW_COPY_AND_ASSIGN(OCSPInitSingleton);
    135 };
    136 
    137 URLRequestContext* OCSPInitSingleton::request_context_ = NULL;
    138 
    139 // Concrete class for SEC_HTTP_REQUEST_SESSION.
    140 // Public methods except virtual methods of URLRequest::Delegate (On* methods)
    141 // run on certificate verifier thread (worker thread).
    142 // Virtual methods of URLRequest::Delegate and private methods run
    143 // on IO thread.
    144 class OCSPRequestSession
    145     : public base::RefCountedThreadSafe<OCSPRequestSession>,
    146       public URLRequest::Delegate,
    147       public MessageLoop::DestructionObserver {
    148  public:
    149   OCSPRequestSession(const GURL& url,
    150                      const char* http_request_method,
    151                      base::TimeDelta timeout)
    152       : url_(url),
    153         http_request_method_(http_request_method),
    154         timeout_(timeout),
    155         request_(NULL),
    156         buffer_(new net::IOBuffer(kRecvBufferSize)),
    157         response_code_(-1),
    158         cv_(&lock_),
    159         io_loop_(NULL),
    160         finished_(false) {}
    161 
    162   void SetPostData(const char* http_data, PRUint32 http_data_len,
    163                    const char* http_content_type) {
    164     upload_content_.assign(http_data, http_data_len);
    165     upload_content_type_.assign(http_content_type);
    166   }
    167 
    168   void AddHeader(const char* http_header_name, const char* http_header_value) {
    169     if (!extra_request_headers_.empty())
    170       extra_request_headers_ += "\r\n";
    171     StringAppendF(&extra_request_headers_,
    172                   "%s: %s", http_header_name, http_header_value);
    173   }
    174 
    175   void Start() {
    176     // At this point, it runs on worker thread.
    177     // |io_loop_| was initialized to be NULL in constructor, and
    178     // set only in StartURLRequest, so no need to lock |lock_| here.
    179     DCHECK(!io_loop_);
    180     Singleton<OCSPInitSingleton>()->PostTaskToIOLoop(
    181         FROM_HERE,
    182         NewRunnableMethod(this, &OCSPRequestSession::StartURLRequest));
    183   }
    184 
    185   bool Started() const {
    186     return request_ != NULL;
    187   }
    188 
    189   void Cancel() {
    190     // IO thread may set |io_loop_| to NULL, so protect by |lock_|.
    191     AutoLock autolock(lock_);
    192     CancelLocked();
    193   }
    194 
    195   bool Finished() const {
    196     AutoLock autolock(lock_);
    197     return finished_;
    198   }
    199 
    200   bool Wait() {
    201     base::TimeDelta timeout = timeout_;
    202     AutoLock autolock(lock_);
    203     while (!finished_) {
    204       base::TimeTicks last_time = base::TimeTicks::Now();
    205       cv_.TimedWait(timeout);
    206       // Check elapsed time
    207       base::TimeDelta elapsed_time = base::TimeTicks::Now() - last_time;
    208       timeout -= elapsed_time;
    209       if (timeout < base::TimeDelta()) {
    210         LOG(INFO) << "OCSP Timed out";
    211         if (!finished_)
    212           CancelLocked();
    213         break;
    214       }
    215     }
    216     return finished_;
    217   }
    218 
    219   const GURL& url() const {
    220     return url_;
    221   }
    222 
    223   const std::string& http_request_method() const {
    224     return http_request_method_;
    225   }
    226 
    227   base::TimeDelta timeout() const {
    228     return timeout_;
    229   }
    230 
    231   PRUint16 http_response_code() const {
    232     DCHECK(finished_);
    233     return response_code_;
    234   }
    235 
    236   const std::string& http_response_content_type() const {
    237     DCHECK(finished_);
    238     return response_content_type_;
    239   }
    240 
    241   const std::string& http_response_headers() const {
    242     DCHECK(finished_);
    243     return response_headers_->raw_headers();
    244   }
    245 
    246   const std::string& http_response_data() const {
    247     DCHECK(finished_);
    248     return data_;
    249   }
    250 
    251   virtual void OnResponseStarted(URLRequest* request) {
    252     DCHECK_EQ(request, request_);
    253     DCHECK_EQ(MessageLoopForIO::current(), io_loop_);
    254 
    255     int bytes_read = 0;
    256     if (request->status().is_success()) {
    257       response_code_ = request_->GetResponseCode();
    258       response_headers_ = request_->response_headers();
    259       response_headers_->GetMimeType(&response_content_type_);
    260       request_->Read(buffer_, kRecvBufferSize, &bytes_read);
    261     }
    262     OnReadCompleted(request_, bytes_read);
    263   }
    264 
    265   virtual void OnReadCompleted(URLRequest* request, int bytes_read) {
    266     DCHECK_EQ(request, request_);
    267     DCHECK_EQ(MessageLoopForIO::current(), io_loop_);
    268 
    269     do {
    270       if (!request_->status().is_success() || bytes_read <= 0)
    271         break;
    272       data_.append(buffer_->data(), bytes_read);
    273     } while (request_->Read(buffer_, kRecvBufferSize, &bytes_read));
    274 
    275     if (!request_->status().is_io_pending()) {
    276       delete request_;
    277       request_ = NULL;
    278       io_loop_->RemoveDestructionObserver(this);
    279       {
    280         AutoLock autolock(lock_);
    281         finished_ = true;
    282         io_loop_ = NULL;
    283       }
    284       cv_.Signal();
    285       Release();  // Balanced with StartURLRequest().
    286     }
    287   }
    288 
    289   virtual void WillDestroyCurrentMessageLoop() {
    290     DCHECK_EQ(MessageLoopForIO::current(), io_loop_);
    291     {
    292       AutoLock autolock(lock_);
    293       io_loop_ = NULL;
    294     }
    295     CancelURLRequest();
    296   }
    297 
    298  private:
    299   friend class base::RefCountedThreadSafe<OCSPRequestSession>;
    300 
    301   virtual ~OCSPRequestSession() {
    302     // When this destructor is called, there should be only one thread that has
    303     // a reference to this object, and so that thread doesn't need to lock
    304     // |lock_| here.
    305     DCHECK(!request_);
    306     DCHECK(!io_loop_);
    307   }
    308 
    309   // Must call this method while holding |lock_|.
    310   void CancelLocked() {
    311     lock_.AssertAcquired();
    312     if (io_loop_) {
    313       io_loop_->PostTask(
    314           FROM_HERE,
    315           NewRunnableMethod(this, &OCSPRequestSession::CancelURLRequest));
    316     }
    317   }
    318 
    319   void StartURLRequest() {
    320     DCHECK(!request_);
    321 
    322     URLRequestContext* url_request_context =
    323         OCSPInitSingleton::url_request_context();
    324     if (url_request_context == NULL)
    325       return;
    326 
    327     {
    328       AutoLock autolock(lock_);
    329       DCHECK(!io_loop_);
    330       io_loop_ = MessageLoopForIO::current();
    331       io_loop_->AddDestructionObserver(this);
    332     }
    333 
    334     request_ = new URLRequest(url_, this);
    335     request_->set_context(url_request_context);
    336     // To meet the privacy requirements of off-the-record mode.
    337     request_->set_load_flags(
    338         net::LOAD_DISABLE_CACHE | net::LOAD_DO_NOT_SAVE_COOKIES |
    339         net::LOAD_DO_NOT_SEND_COOKIES);
    340 
    341     if (http_request_method_ == "POST") {
    342       DCHECK(!upload_content_.empty());
    343       DCHECK(!upload_content_type_.empty());
    344 
    345       request_->set_method("POST");
    346       if (!extra_request_headers_.empty())
    347         extra_request_headers_ += "\r\n";
    348       StringAppendF(&extra_request_headers_,
    349                     "Content-Type: %s", upload_content_type_.c_str());
    350       request_->AppendBytesToUpload(upload_content_.data(),
    351                                     static_cast<int>(upload_content_.size()));
    352     }
    353     if (!extra_request_headers_.empty())
    354       request_->SetExtraRequestHeaders(extra_request_headers_);
    355 
    356     request_->Start();
    357     AddRef();  // Release after |request_| deleted.
    358   }
    359 
    360   void CancelURLRequest() {
    361 #ifndef NDEBUG
    362     {
    363       AutoLock autolock(lock_);
    364       if (io_loop_)
    365         DCHECK_EQ(MessageLoopForIO::current(), io_loop_);
    366     }
    367 #endif
    368     if (request_) {
    369       request_->Cancel();
    370       delete request_;
    371       request_ = NULL;
    372       // |io_loop_| may be NULL here if it called from
    373       // WillDestroyCurrentMessageLoop().
    374       if (io_loop_)
    375         io_loop_->RemoveDestructionObserver(this);
    376       {
    377         AutoLock autolock(lock_);
    378         finished_ = true;
    379         io_loop_ = NULL;
    380       }
    381       cv_.Signal();
    382       Release();  // Balanced with StartURLRequest().
    383     }
    384   }
    385 
    386   GURL url_;                      // The URL we eventually wound up at
    387   std::string http_request_method_;
    388   base::TimeDelta timeout_;       // The timeout for OCSP
    389   URLRequest* request_;           // The actual request this wraps
    390   scoped_refptr<net::IOBuffer> buffer_;  // Read buffer
    391   std::string extra_request_headers_;  // Extra headers for the request, if any
    392   std::string upload_content_;    // HTTP POST payload
    393   std::string upload_content_type_;  // MIME type of POST payload
    394 
    395   int response_code_;             // HTTP status code for the request
    396   std::string response_content_type_;
    397   scoped_refptr<net::HttpResponseHeaders> response_headers_;
    398   std::string data_;              // Results of the requst
    399 
    400   // |lock_| protects |finished_| and |io_loop_|.
    401   mutable Lock lock_;
    402   ConditionVariable cv_;
    403 
    404   MessageLoop* io_loop_;          // Message loop of the IO thread
    405   bool finished_;
    406 
    407   DISALLOW_COPY_AND_ASSIGN(OCSPRequestSession);
    408 };
    409 
    410 // Concrete class for SEC_HTTP_SERVER_SESSION.
    411 class OCSPServerSession {
    412  public:
    413   OCSPServerSession(const char* host, PRUint16 port)
    414       : host_(host), port_(port) {}
    415   ~OCSPServerSession() {}
    416 
    417   OCSPRequestSession* CreateRequest(const char* http_protocol_variant,
    418                                     const char* path_and_query_string,
    419                                     const char* http_request_method,
    420                                     const PRIntervalTime timeout) {
    421     // We dont' support "https" because we haven't thought about
    422     // whether it's safe to re-enter this code from talking to an OCSP
    423     // responder over SSL.
    424     if (strcmp(http_protocol_variant, "http") != 0)
    425       return NULL;
    426 
    427     // TODO(ukai): If |host| is an IPv6 literal, we need to quote it with
    428     //  square brackets [].
    429     std::string url_string(StringPrintf("%s://%s:%d%s",
    430                                         http_protocol_variant,
    431                                         host_.c_str(),
    432                                         port_,
    433                                         path_and_query_string));
    434     LOG(INFO) << "URL [" << url_string << "]";
    435     GURL url(url_string);
    436     return new OCSPRequestSession(
    437         url, http_request_method,
    438         base::TimeDelta::FromMilliseconds(PR_IntervalToMilliseconds(timeout)));
    439   }
    440 
    441 
    442  private:
    443   std::string host_;
    444   int port_;
    445 
    446   DISALLOW_COPY_AND_ASSIGN(OCSPServerSession);
    447 };
    448 
    449 
    450 // OCSP Http Client functions.
    451 // Our Http Client functions operate in blocking mode.
    452 SECStatus OCSPCreateSession(const char* host, PRUint16 portnum,
    453                             SEC_HTTP_SERVER_SESSION* pSession) {
    454   LOG(INFO) << "OCSP create session: host=" << host << " port=" << portnum;
    455   DCHECK(!MessageLoop::current());
    456   if (OCSPInitSingleton::url_request_context() == NULL) {
    457     LOG(ERROR) << "No URLRequestContext for OCSP handler.";
    458     return SECFailure;
    459   }
    460   *pSession = new OCSPServerSession(host, portnum);
    461   return SECSuccess;
    462 }
    463 
    464 SECStatus OCSPKeepAliveSession(SEC_HTTP_SERVER_SESSION session,
    465                                PRPollDesc **pPollDesc) {
    466   LOG(INFO) << "OCSP keep alive";
    467   DCHECK(!MessageLoop::current());
    468   if (pPollDesc)
    469     *pPollDesc = NULL;
    470   return SECSuccess;
    471 }
    472 
    473 SECStatus OCSPFreeSession(SEC_HTTP_SERVER_SESSION session) {
    474   LOG(INFO) << "OCSP free session";
    475   DCHECK(!MessageLoop::current());
    476   delete reinterpret_cast<OCSPServerSession*>(session);
    477   return SECSuccess;
    478 }
    479 
    480 SECStatus OCSPCreate(SEC_HTTP_SERVER_SESSION session,
    481                      const char* http_protocol_variant,
    482                      const char* path_and_query_string,
    483                      const char* http_request_method,
    484                      const PRIntervalTime timeout,
    485                      SEC_HTTP_REQUEST_SESSION* pRequest) {
    486   LOG(INFO) << "OCSP create protocol=" << http_protocol_variant
    487             << " path_and_query=" << path_and_query_string
    488             << " http_request_method=" << http_request_method
    489             << " timeout=" << timeout;
    490   DCHECK(!MessageLoop::current());
    491   OCSPServerSession* ocsp_session =
    492       reinterpret_cast<OCSPServerSession*>(session);
    493 
    494   OCSPRequestSession* req = ocsp_session->CreateRequest(http_protocol_variant,
    495                                                         path_and_query_string,
    496                                                         http_request_method,
    497                                                         timeout);
    498   SECStatus rv = SECFailure;
    499   if (req) {
    500     req->AddRef();  // Release in OCSPFree().
    501     rv = SECSuccess;
    502   }
    503   *pRequest = req;
    504   return rv;
    505 }
    506 
    507 SECStatus OCSPSetPostData(SEC_HTTP_REQUEST_SESSION request,
    508                           const char* http_data,
    509                           const PRUint32 http_data_len,
    510                           const char* http_content_type) {
    511   LOG(INFO) << "OCSP set post data len=" << http_data_len;
    512   DCHECK(!MessageLoop::current());
    513   OCSPRequestSession* req = reinterpret_cast<OCSPRequestSession*>(request);
    514 
    515   req->SetPostData(http_data, http_data_len, http_content_type);
    516   return SECSuccess;
    517 }
    518 
    519 SECStatus OCSPAddHeader(SEC_HTTP_REQUEST_SESSION request,
    520                         const char* http_header_name,
    521                         const char* http_header_value) {
    522   LOG(INFO) << "OCSP add header name=" << http_header_name
    523             << " value=" << http_header_value;
    524   DCHECK(!MessageLoop::current());
    525   OCSPRequestSession* req = reinterpret_cast<OCSPRequestSession*>(request);
    526 
    527   req->AddHeader(http_header_name, http_header_value);
    528   return SECSuccess;
    529 }
    530 
    531 // Sets response of |req| in the output parameters.
    532 // It is helper routine for OCSP trySendAndReceiveFcn.
    533 // |http_response_data_len| could be used as input parameter.  If it has
    534 // non-zero value, it is considered as maximum size of |http_response_data|.
    535 bool OCSPSetResponse(OCSPRequestSession* req,
    536                      PRUint16* http_response_code,
    537                      const char** http_response_content_type,
    538                      const char** http_response_headers,
    539                      const char** http_response_data,
    540                      PRUint32* http_response_data_len) {
    541   DCHECK(req->Finished());
    542   const std::string& data = req->http_response_data();
    543   if (http_response_data_len && *http_response_data_len) {
    544     if (*http_response_data_len < data.size()) {
    545       LOG(ERROR) << "data size too large: " << *http_response_data_len
    546                  << " < " << data.size();
    547       *http_response_data_len = data.size();
    548       return false;
    549     }
    550   }
    551   LOG(INFO) << "OCSP response "
    552             << " response_code=" << req->http_response_code()
    553             << " content_type=" << req->http_response_content_type()
    554             << " header=" << req->http_response_headers()
    555             << " data_len=" << data.size();
    556   if (http_response_code)
    557     *http_response_code = req->http_response_code();
    558   if (http_response_content_type)
    559     *http_response_content_type = req->http_response_content_type().c_str();
    560   if (http_response_headers)
    561     *http_response_headers = req->http_response_headers().c_str();
    562   if (http_response_data)
    563     *http_response_data = data.data();
    564   if (http_response_data_len)
    565     *http_response_data_len = data.size();
    566   return true;
    567 }
    568 
    569 SECStatus OCSPTrySendAndReceive(SEC_HTTP_REQUEST_SESSION request,
    570                                 PRPollDesc** pPollDesc,
    571                                 PRUint16* http_response_code,
    572                                 const char** http_response_content_type,
    573                                 const char** http_response_headers,
    574                                 const char** http_response_data,
    575                                 PRUint32* http_response_data_len) {
    576   LOG(INFO) << "OCSP try send and receive";
    577   DCHECK(!MessageLoop::current());
    578   OCSPRequestSession* req = reinterpret_cast<OCSPRequestSession*>(request);
    579   // We support blocking mode only.
    580   if (pPollDesc)
    581     *pPollDesc = NULL;
    582 
    583   if (req->Started() || req->Finished()) {
    584     // We support blocking mode only, so this function shouldn't be called
    585     // again when req has stareted or finished.
    586     NOTREACHED();
    587     goto failed;
    588   }
    589   req->Start();
    590   if (!req->Wait())
    591     goto failed;
    592 
    593   // If the response code is -1, the request failed and there is no response.
    594   if (req->http_response_code() == static_cast<PRUint16>(-1))
    595     goto failed;
    596 
    597   return OCSPSetResponse(
    598       req, http_response_code,
    599       http_response_content_type,
    600       http_response_headers,
    601       http_response_data,
    602       http_response_data_len) ? SECSuccess : SECFailure;
    603 
    604  failed:
    605   if (http_response_data_len) {
    606     // We must always set an output value, even on failure.  The output value 0
    607     // means the failure was unrelated to the acceptable response data length.
    608     *http_response_data_len = 0;
    609   }
    610   return SECFailure;
    611 }
    612 
    613 SECStatus OCSPFree(SEC_HTTP_REQUEST_SESSION request) {
    614   LOG(INFO) << "OCSP free";
    615   DCHECK(!MessageLoop::current());
    616   OCSPRequestSession* req = reinterpret_cast<OCSPRequestSession*>(request);
    617   req->Cancel();
    618   req->Release();
    619   return SECSuccess;
    620 }
    621 
    622 }  // anonymous namespace
    623 
    624 namespace net {
    625 
    626 void EnsureOCSPInit() {
    627   Singleton<OCSPInitSingleton>::get();
    628 }
    629 
    630 // This function would be called before NSS initialization.
    631 void SetURLRequestContextForOCSP(URLRequestContext* request_context) {
    632   OCSPInitSingleton::set_url_request_context(request_context);
    633 }
    634 
    635 URLRequestContext* GetURLRequestContextForOCSP() {
    636   return OCSPInitSingleton::url_request_context();
    637 }
    638 
    639 }  // namespace net
    640