What happens when you type a URL in browser

本文详细介绍了当我们在浏览器中输入URL后,从DNS查找、TCP连接建立到HTTP请求与响应等整个过程。揭示了浏览器如何解析域名、获取网页内容并最终呈现给用户的全过程。

原文地址见 http://edusagar.com/articles/view/70/What-happens-when-you-type-a-URL-in-browser

Apart from being a very common interview question, this is one of the very first query which lingers around in our mind every time we type a URL in a browser. Here is an attempt to satiate this quest while we delve into the details of what happens in the background when we type a URL in our browsers.
 

Step 1. URL is typed in the browser.

 

Step 2. If requested object is in browser cache and is fresh, move on to Step  8.

 

Step 3. DNS lookup to find the ip address of the server

when we want to connect to  google.com, we actually want to reach out to a server where google web services are hosted. One such server is having an ip address of  74.125.236.65. Now, if you type " http://74.125.236.65" in your browser, this will take you to google home page itself. Which means, " http://google.com" and " http://74.125.236.65" are nothing but same stuff. But, it is not so. Google has multiple servers in multiple locations to cater to the huge volume of requests they receive per second. Thus we should let Google decide which server is best suited to our needs. Using "google.com" does the job for us. When we type "google.com",  DNS(Domain Name System) services comes into play and resolves the URL to a proper ip address. 

Following is a summary of steps happening while DNS service is at work:
  • Check browser cache: browsers maintain cache of DNS records for some fixed duration. So, this is the first place to resolve DNS queries.
  • Check OS cache: if browser doesn't contain the record in its cache, it makes a system call to underlying Operating System to fetch the record as OS also maintains a cache of recent DNS queries.
  •  Router Cache: if above steps fail to get a DNS record, the search continues to your router which has its own cache.
  •  ISP cache: if everything fails, the search moves on to your ISP. First, it tries in its cache, if not found - ISP's DNS recursive search comes into picture. DNS lookup is again a complex process which finds the appropriate ip address from a list of many options available for websites like Google. You can read more about this here.

Step 4. Browser initiates a TCP connection with the server.

 

Step 5. Browser sends a HTTP request to the server.

Browser sends a  GET request to the server according to the specification of HTTP(Hyper Text Transfer Protocol) protocol. 
        

GET http://google.com/ HTTP/1.1
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:29.0) Gecko/20100101 Firefox/29.0
Accept-Encoding: gzip, deflate
Connection: Keep-Alive
Host: google.com
Cookie: datr=1265876274-[...]; locale=en_US; lsd=WW[...]; c_user=2101[...]

        
Here, browser passes some meta information in the form of headers to the server along with the URL - "http://google.com".  User-Agent header specifies the browser properties,  Accept-Encoding headers specify the type of responses it will accept.  Connection header tells the server to keep open the TCP connection established here. The request also contains  Cookies, which are meta information stored at the client end and contain previous browsing session information for the same website in the form of key-value pairs e.g. the login name of the user for Google.

A quick guide to HTTP specification can be found  here.

Step 6. Server handles the incoming request

HTTP request made from browsers are handled by a special software running on server - commonly known as  web servers e.g.  ApacheIIS etc. Web server passes on the request to the proper request handler - a program written to handle web services e.g.  PHP, ASP.NET, Ruby, Servlets etc.
        
For example URL-  http://edusagar.com/index.php is handled by a program written in PHP file -  index.php. As soon as GET request for index.php is received, Apache(our webserver at edusagar.com) prepares the environment to execute index.php file. Now, this php program will generate a response - in our case a HTML response. This response is then sent back to the browser according to HTTP guidelines.

Step 7. Browser receives the HTTP response

HTTP/1.1 200 OK
Cache-Control: private, no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Pragma: no-cache
Content-Encoding: gzip
Content-Type: text/html; charset=utf-8
Connection: Keep-Alive
Content-length: 1215
Date: Fri, 30 May 2014 08:10:15 GMT

.........<some blob> ................


HTTP response starts with the returned status code from the server. Following is a very brief summary of what a status code denotes:        
  •         1xx indicates an informational message only
  •         2xx indicates success of some kind
  •         3xx redirects the client to another URL
  •         4xx indicates an error on the client's part
  •         5xx indicates an error on the server's part
Server sets various other headers to help browser render the proper content. Content-Type tells the type of the content the browser has to show,  Content-length tells the number of bytes of the response. Using the  Content-Encodingheader's value, browsers can decode the blob data present at the end of the response.

Step 8. Browsers displays the html content

Rendering of html content is also done in phases. The browser first renders the bare bone html structure, and then it sends multiple GET requests to fetch other hyper linked stuff e.g. If the html response contains an image in the form of img tags such as  <img scr="/assets/img/logo.png" />, browser will send a HTTP GET request to the server to fetch the image following the complete set of steps which we have seen till now. But this isn't that bad as it looks. Static files like images, javascript, css files are all cached by the browser so that in future it doesn't have to fetch them again.      

Step 9. Client interaction with server

Once a html page is loaded, there are several ways a user can interact with the server. For example, he call fill out a login form to sign in to the website. This also follows all the steps listed above, the only difference is that the HTTP request this time would be a  POST instead of GET and along with that request, browser will send the form data to the server for processing (username and password in this case).
        
Once server authenticates the user, it will send the proper HTML content(may be user's profile) back to the browser and thus user will see that new webpage after his login request is processed.

Step 10. AJAX queries

Another form of client interaction with server is through  AJAX(Asynchronous JavaScript And XML) requests. This is an asynchronous GET/POST request to which server can send a response back in a variety of ways -  json, xml, html etc. AJAX requests doesn't hinder the current view of the webpage and work in the background. Because of this, one can dynamically modify the content of a webpage by calling an AJAX request and updating the web elements using Javascript.

        
Hopefully, that gives you an idea of what happens in the background when we do a really simple operation of typing an URL in a browser.

 

 

 

chromium源码中下面的函数具体功能,详细解释一下: base::WeakPtr<content::NavigationHandle> Navigate(NavigateParams* params) { TRACE_EVENT1("navigation", "chrome::Navigate", "disposition", params->disposition); Browser* source_browser = params->browser; if (source_browser) { params->initiating_profile = source_browser->profile(); } DCHECK(params->initiating_profile); // If the created window is a partitioned popin, a valid source exists, and // the disposition is NEW_POPUP then the resulting popup should be tab-modal. // See: https://explainers-by-googlers.github.io/partitioned-popins/ params->is_tab_modal_popup |= params->window_features.is_partitioned_popin && params->source_contents && params->disposition == WindowOpenDisposition::NEW_POPUP; #if BUILDFLAG(IS_CHROMEOS) if (params->initiating_profile->IsOffTheRecord() && params->initiating_profile->GetOTRProfileID().IsCaptivePortal() && params->disposition != WindowOpenDisposition::NEW_POPUP && params->disposition != WindowOpenDisposition::CURRENT_TAB && !IncognitoModeForced(params->initiating_profile)) { // Navigation outside of the current tab or the initial popup window from a // captive portal signin window should be prevented. params->disposition = WindowOpenDisposition::CURRENT_TAB; } #endif if (params->initiating_profile->ShutdownStarted()) { // Don't navigate when the profile is shutting down. return nullptr; } // Block navigation requests when in locked fullscreen mode. We allow // navigation requests in the webapp when locked for OnTask (only relevant for // non-web browser scenarios). // TODO(b/365146870): Remove once we consolidate locked fullscreen with // OnTask. if (source_browser) { bool should_block_navigation = platform_util::IsBrowserLockedFullscreen(source_browser); #if BUILDFLAG(IS_CHROMEOS) if (source_browser->IsLockedForOnTask()) { should_block_navigation = false; } #endif // BUILDFLAG(IS_CHROMEOS) if (should_block_navigation) { return nullptr; } } // Open System Apps in their standalone window if necessary. // TODO(crbug.com/40136163): Remove this code after we integrate with intent // handling. #if BUILDFLAG(IS_CHROMEOS) const std::optional<ash::SystemWebAppType> capturing_system_app_type = ash::GetCapturingSystemAppForURL(params->initiating_profile, params->url); if (capturing_system_app_type && (!params->browser || !ash::IsBrowserForSystemWebApp(params->browser, capturing_system_app_type.value()))) { ash::SystemAppLaunchParams swa_params; swa_params.url = params->url; ash::LaunchSystemWebAppAsync(params->initiating_profile, capturing_system_app_type.value(), swa_params); // It's okay to early return here, because LaunchSystemWebAppAsync uses a // different logic to choose (and create if necessary) a browser window for // system apps. // // It's okay to skip the checks and cleanups below. The link captured system // app will either open in its own browser window, or navigate an existing // browser window exclusively used by this app. For the initiating browser, // the navigation should appear to be cancelled. return nullptr; } #endif // BUILDFLAG(IS_CHROMEOS) #if !BUILDFLAG(IS_ANDROID) // Force isolated PWAs to open in an app window. params->force_open_pwa_window = content::SiteIsolationPolicy::ShouldUrlUseApplicationIsolationLevel( params->initiating_profile, params->url); params->open_pwa_window_if_possible |= params->force_open_pwa_window; #endif if (!AdjustNavigateParamsForURL(params)) { return nullptr; } // Picture-in-picture browser windows must have a source contents in order for // the window to function correctly. If we have no source contents to work // with (e.g. if an extension popup attempts to open a PiP window), we should // cancel the navigation. The source URL must also be of a type that's // allowed to open document PiP. See `PictureInPictureWindowManager` for // details on what's allowed. if (params->disposition == WindowOpenDisposition::NEW_PICTURE_IN_PICTURE) { const GURL& url = params->source_contents ? params->source_contents->GetLastCommittedURL() : GURL(); if (!PictureInPictureWindowManager::IsSupportedForDocumentPictureInPicture( url)) { return nullptr; } } // If no source WebContents was specified, we use the selected one from the // target browser. This must happen before GetBrowserAndTabForDisposition() // has a chance to replace |params->browser| with another one, but after the // above check that relies on the original source_contents value. if (!params->source_contents && params->browser) { params->source_contents = params->browser->tab_strip_model()->GetActiveWebContents(); } WebContents* contents_to_navigate_or_insert = params->contents_to_insert.get(); if (params->switch_to_singleton_tab) { DCHECK_EQ(params->disposition, WindowOpenDisposition::SINGLETON_TAB); contents_to_navigate_or_insert = params->switch_to_singleton_tab; } #if !BUILDFLAG(IS_ANDROID) // If this is a Picture in Picture window, then notify the pip manager about // it. This enables the opener and pip window to stay connected, so that (for // example), the pip window does not outlive the opener. // // We do this before creating the browser window, so that the browser can talk // to the PictureInPictureWindowManager. Otherwise, the manager has no idea // that there's a pip window. if (params->disposition == WindowOpenDisposition::NEW_PICTURE_IN_PICTURE) { // Picture in picture windows may not be opened by other picture in // picture windows, or without an opener. if (!params->browser || params->browser->is_type_picture_in_picture()) { params->browser = nullptr; return nullptr; } PictureInPictureWindowManager::GetInstance()->EnterDocumentPictureInPicture( params->source_contents, contents_to_navigate_or_insert); } #endif // !BUILDFLAG(IS_ANDROID) // TODO(crbug.com/364657540): Revisit integration with web_application system // later if needed. int singleton_index; #if !BUILDFLAG(IS_ANDROID) std::unique_ptr<web_app::NavigationCapturingProcess> app_navigation = web_app::NavigationCapturingProcess::MaybeHandleAppNavigation(*params); std::optional<std::tuple<Browser*, int>> app_browser_tab_override; if (app_navigation) { app_browser_tab_override = app_navigation->GetInitialBrowserAndTabOverrideForNavigation(*params); } std::tie(params->browser, singleton_index) = app_browser_tab_override.has_value() ? *app_browser_tab_override : GetBrowserAndTabForDisposition(*params); #else // !BUILDFLAG(IS_ANDROID) std::tie(params->browser, singleton_index) = GetBrowserAndTabForDisposition(*params); #endif if (!params->browser) { return nullptr; } // Trying to open a background tab when in a non-tabbed app browser results in // focusing a regular browser window and opening a tab in the background // of that window. Change the disposition to NEW_FOREGROUND_TAB so that // the new tab is focused. if (source_browser && source_browser->is_type_app() && params->disposition == WindowOpenDisposition::NEW_BACKGROUND_TAB && !(source_browser->app_controller() && source_browser->app_controller()->has_tab_strip())) { params->disposition = WindowOpenDisposition::NEW_FOREGROUND_TAB; } if (singleton_index != -1) { contents_to_navigate_or_insert = params->browser->tab_strip_model()->GetWebContentsAt(singleton_index); } else if (params->disposition == WindowOpenDisposition::SWITCH_TO_TAB) { // The user is trying to open a tab that no longer exists. If we open a new // tab, it could leave orphaned NTPs around, but always overwriting the // current tab could could clobber state that the user was trying to // preserve. Fallback to the behavior used for singletons: overwrite the // current tab if it's the NTP, otherwise open a new tab. params->disposition = WindowOpenDisposition::SINGLETON_TAB; ShowSingletonTabOverwritingNTP(params); return nullptr; } if (params->force_open_pwa_window) { CHECK(web_app::AppBrowserController::IsWebApp(params->browser)); } #if BUILDFLAG(IS_CHROMEOS) if (source_browser && source_browser != params->browser) { // When the newly created browser was spawned by a browser which visits // another user's desktop, it should be shown on the same desktop as the // originating one. (This is part of the desktop separation per profile). auto* window_manager = MultiUserWindowManagerHelper::GetWindowManager(); // Some unit tests have no client instantiated. if (window_manager) { aura::Window* src_window = source_browser->window()->GetNativeWindow(); aura::Window* new_window = params->browser->window()->GetNativeWindow(); const AccountId& src_account_id = window_manager->GetUserPresentingWindow(src_window); if (src_account_id != window_manager->GetUserPresentingWindow(new_window)) { // Once the window gets presented, it should be shown on the same // desktop as the desktop of the creating browser. Note that this // command will not show the window if it wasn't shown yet by the // browser creation. window_manager->ShowWindowForUser(new_window, src_account_id); } } } #endif // Navigate() must not return early after this point. if (GetSourceProfile(params) != params->browser->profile()) { // A tab is being opened from a link from a different profile, we must reset // source information that may cause state to be shared. params->opener = nullptr; params->source_contents = nullptr; params->source_site_instance = nullptr; params->referrer = content::Referrer(); } // Make sure the Browser is shown if params call for it. ScopedBrowserShower shower(params, &contents_to_navigate_or_insert); if (params->is_tab_modal_popup) { shower.set_source_contents(params->source_contents); } // Some dispositions need coercion to base types. NormalizeDisposition(params); // If a new window has been created, it needs to be shown. if (params->window_action == NavigateParams::NO_ACTION && source_browser != params->browser && params->browser->tab_strip_model()->empty()) { params->window_action = NavigateParams::SHOW_WINDOW; } // If we create a popup window from a non user-gesture, don't activate it. if (params->window_action == NavigateParams::SHOW_WINDOW && params->disposition == WindowOpenDisposition::NEW_POPUP && params->user_gesture == false) { params->window_action = NavigateParams::SHOW_WINDOW_INACTIVE; } // Determine if the navigation was user initiated. If it was, we need to // inform the target WebContents, and we may need to update the UI. bool user_initiated = params->transition & ui::PAGE_TRANSITION_FROM_ADDRESS_BAR || !ui::PageTransitionIsWebTriggerable(params->transition); base::WeakPtr<content::NavigationHandle> navigation_handle; std::unique_ptr<tabs::TabModel> tab_to_insert; if (params->contents_to_insert) { tab_to_insert = std::make_unique<tabs::TabModel>(std::move(params->contents_to_insert), params->browser->tab_strip_model()); } // If no target WebContents was specified (and we didn't seek and find a // singleton), we need to construct one if we are supposed to target a new // tab. if (!contents_to_navigate_or_insert) { DCHECK(!params->url.is_empty()); if (params->disposition != WindowOpenDisposition::CURRENT_TAB) { tab_to_insert = std::make_unique<tabs::TabModel>( CreateTargetContents(*params, params->url), params->browser->tab_strip_model()); contents_to_navigate_or_insert = tab_to_insert->GetContents(); apps::SetAppIdForWebContents(params->browser->profile(), contents_to_navigate_or_insert, params->app_id); #if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION) captive_portal::CaptivePortalTabHelper::FromWebContents( contents_to_navigate_or_insert) ->set_window_type(params->captive_portal_window_type); #endif } else { // ... otherwise if we're loading in the current tab, the target is the // same as the source. DCHECK(params->source_contents); contents_to_navigate_or_insert = params->source_contents; } // Try to handle non-navigational URLs that popup dialogs and such, these // should not actually navigate. if (!HandleNonNavigationAboutURL(params->url)) { // Perform the actual navigation, tracking whether it came from the // renderer. navigation_handle = LoadURLInContents(contents_to_navigate_or_insert, params->url, params); } } else { // |contents_to_navigate_or_insert| was specified non-NULL, and so we assume // it has already been navigated appropriately. We need to do nothing more // other than add it to the appropriate tabstrip. } // If the user navigated from the omnibox, and the selected tab is going to // lose focus, then make sure the focus for the source tab goes away from the // omnibox. if (params->source_contents && (params->disposition == WindowOpenDisposition::NEW_FOREGROUND_TAB || params->disposition == WindowOpenDisposition::NEW_WINDOW) && (params->tabstrip_add_types & AddTabTypes::ADD_INHERIT_OPENER)) { params->source_contents->Focus(); } if (tab_to_insert) { // Save data needed for link capturing into apps that cannot otherwise be // inferred later in the navigation. These are only needed when the // navigation happens in a different tab to the link click. apps::SetLinkCapturingSourceDisposition(tab_to_insert->GetContents(), params->disposition); } if (params->source_contents == contents_to_navigate_or_insert) { // The navigation occurred in the source tab. params->browser->UpdateUIForNavigationInTab( contents_to_navigate_or_insert, params->transition, params->window_action, user_initiated); } else if (singleton_index == -1) { if (source_browser != params->browser) { params->tabstrip_index = params->browser->tab_strip_model()->count(); } // If some non-default value is set for the index, we should tell the // TabStripModel to respect it. if (params->tabstrip_index != -1) { params->tabstrip_add_types |= AddTabTypes::ADD_FORCE_INDEX; } // Maybe notify that an open operation has been done from a gesture. // TODO(crbug.com/40719979): preferably pipe this information through the // TabStripModel instead. See bug for deeper discussion. if (params->user_gesture && source_browser == params->browser) { params->browser->window()->LinkOpeningFromGesture(params->disposition); } DCHECK(tab_to_insert); // The navigation should insert a new tab into the target Browser. params->browser->tab_strip_model()->AddTab( std::move(tab_to_insert), params->tabstrip_index, params->transition, params->tabstrip_add_types, params->group); } if (singleton_index >= 0) { // If switching browsers, make sure it is shown. if (params->disposition == WindowOpenDisposition::SWITCH_TO_TAB && params->browser != source_browser) { params->window_action = NavigateParams::SHOW_WINDOW; } if (contents_to_navigate_or_insert->IsCrashed()) { contents_to_navigate_or_insert->GetController().Reload( content::ReloadType::NORMAL, true); } else if (params->path_behavior == NavigateParams::IGNORE_AND_NAVIGATE && contents_to_navigate_or_insert->GetURL() != params->url) { navigation_handle = LoadURLInContents(contents_to_navigate_or_insert, params->url, params); } // If the singleton tab isn't already selected, select it. if (params->source_contents != contents_to_navigate_or_insert) { // Use the index before the potential close below, because it could // make the index refer to a different tab. auto gesture_type = user_initiated ? TabStripUserGestureDetails::GestureType::kOther : TabStripUserGestureDetails::GestureType::kNone; bool should_close_this_tab = false; if (params->disposition == WindowOpenDisposition::SWITCH_TO_TAB) { // Close orphaned NTP (and the like) with no history when the user // switches away from them. if (params->source_contents) { if (params->source_contents->GetController().CanGoBack() || (params->source_contents->GetLastCommittedURL().spec() != chrome::kChromeUINewTabURL && params->source_contents->GetLastCommittedURL().spec() != url::kAboutBlankURL)) { // Blur location bar before state save in ActivateTabAt() below. params->source_contents->Focus(); } else { should_close_this_tab = true; } } } params->browser->tab_strip_model()->ActivateTabAt( singleton_index, TabStripUserGestureDetails(gesture_type)); // Close tab after switch so index remains correct. if (should_close_this_tab) { params->source_contents->Close(); } } } params->navigated_or_inserted_contents = contents_to_navigate_or_insert; // At this point, the `params->navigated_or_inserted_contents` is guaranteed to // be non null, so perform tasks if the navigation has been captured by a web // app, like enqueueing launch params. #if !BUILDFLAG(IS_ANDROID) if (app_navigation) { web_app::NavigationCapturingProcess::AfterWebContentsCreation( std::move(app_navigation), *params->navigated_or_inserted_contents, navigation_handle.get()); } #endif // !BUILDFLAG(IS_ANDROID) return navigation_handle; }
08-01
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值