位置: IT常识 - 正文

Building a HTTP Proxy

编辑:rootadmin
Web ProxyBuilding a HTTP Proxy Frequently Asked Questions Download the testing script Download the s

推荐整理分享Building a HTTP Proxy,希望有所帮助,仅作参考,欢迎阅读内容。

文章相关热门搜索词:,内容如对您有帮助,希望把文章链接给更多的朋友!

Building a HTTP Proxy

Frequently Asked Questions Download the testing script Download the sample Makefile

Contents

1 Building a HTTP Proxy 1.1 Overview 1.2 Introduction: The Hypertext Transfer Protocol 1.2.1 HTTP Proxies 1.3 Assignment Details 1.3.1 The Basics 1.3.2 Listening 1.3.3 Parsing the URL 1.3.4 Getting Data from the Remote Server 1.3.5 Returning Data to the Client 1.3.6 Testing Your Proxy 1.4 Configuring a Web Browser to Use a Proxy 1.4.1 A Caveat 1.4.2 Firefox 1.4.2.1 Configuring Firefox to use HTTP/1.0 1.4.3 Internet Explorer 1.5 Socket Programming 1.6 Grading 1.6.1 A Note on Network Programming 1.6.2 Possible Extensions 1.6.2.1 Content Transformation 1.6.2.2 Caching 1.6.2.3 Link Prefetch 1.6.2.4 Other Possible Extensions

Overview

In this assignment, you will implement a simple web proxy that passes requests and data between a web client and a web server. This will give you a chance to get to know one of the most popular application protocols on the Internet- the Hypertext Transfer Protocol (HTTP)v. 1.0- and give you an introduction to the Berkeley sockets API. When you're done with the assignment, you should be able to configure your web browser to use your personal proxy server as a web proxy.Introduction: The Hypertext Transfer Protocol

The Hypertext Transfer Protocol or (HTTP) is the protocol used for communication on this web. That is, it is the protocol which defines how your web browser requests resources from a web server and how the server responds. For simplicity, in this assignment we will be dealing only with version 1.0 of the HTTP protocol, defined in detail in RFC 1945. You should read through this RFC and refer back to it when deciding on the behavior of your proxy.

HTTP communications happen in the form of transactions, a transaction consists of a client sending a request to a server and then reading the response. Request and response messages share a common basic format:

An initial line (a request or response line, as defined below) Zero or more header lines A blank line (CRLF) An optional message body.

For most common HTTP transactions, the protocol boils down to a relatively simple series of steps (important sections of RFC 1945 are in parenthesis):

A client creates a connection to the server. The client issues a request by sending a line of text to the server. This request line consists of a HTTP method (most often GET, but POST, PUT, and others are possible), a request URI (like a URL), and the protocol version that the client wants to use (HTTP/1.0). The message body of the initial request is typically empty. (5.1-5.2, 8.1-8.3, 10, D.1) The server sends a response message, with its initial line consisting of a status line, indicating if the request was successful. The status line consists of the HTTP version (HTTP/1.0), a response status code (a numerical value that indicates whether or not the request was completed successfully), and a reason phrase, an English-language message providing description of the status code. Just as with the the request message, there can be as many or as few header fields in the response as the server wants to return. Following the CRLF field separator, the message body contains the data requested by the client in the event of a successful request. (6.1-6.2, 9.1-9.5, 10) Once the server has returned the response to the client, it closes the connection.

It's fairly easy to see this process in action without using a web browser. From a Unix prompt, type:

telnet www.yahoo.com 80

This opens a TCP connection to the server at www.yahoo.com listening on port 80- the default HTTP port. You should see something like this:

Trying 209.131.36.158...Connected to www.yahoo.com (209.131.36.158).Escape character is '^]'.

type the following:

GET / HTTP/1.0

and hit enter twice. You should see something like the following:

HTTP/1.1 200 OKDate: Fri, 10 Nov 2006 20:31:19 GMTConnection: closeContent-Type: text/html; charset=utf-8

<html><head><title>Yahoo!</title>(More HTML follows)

There may be some additional pieces of header information as well- setting cookies, instructions to the browser or proxy on caching behavior, etc. What you are seeing is exactly what your web browser sees when it goes to the Yahoo home page: the HTTP status line, the header fields, and finally the HTTP message body- consisting of the HTML that your browser interprets to create a web page.

HTTP Proxies

Ordinarily, HTTP is a client-server protocol. The client (usually your web browser) communicates directly with the server (the web server software). However, in some circumstances it may be useful to introduce an intermediate entity called a proxy. Conceptually, the proxy sits between the client and the server. In the simplest case, instead of sending requests directly to the server the client sends all its requests to the proxy. The proxy then opens a connection to the server, and passes on the client's request. The proxy receives the reply from the server, and then sends that reply back to the client. Notice that the proxy is essentially acting like both a HTTP client (to the remote server) and a HTTP server (to the initial client).

Why use a proxy? There are a few possible reasons:

Performance: By saving a copy of the pages that it fetches, a proxy can reduce the need to create connections to remote servers. This can reduce the overall delay involved in retrieving a page, particularly if a server is remote or under heavy load. Content Filtering and Transformation: While in the simplest case the proxy merely fetches a resource without inspecting it, there is nothing that says that a proxy is limited to blindly fetching and serving files. The proxy can inspect the requested URL and selectively block access to certain domains, reformat web pages (for instances, by stripping out images to make a page easier to display on a handheld or other limited-resource client), or perform other transformations and filtering. Privacy: Normally, web servers log all incoming requests for resources. This information typically includes at least the IP address of the client, the browser or other client program that they are using (called the User-Agent), the date and time, and the requested file. If a client does not wish to have this personally identifiable information recorded, routing HTTP requests through a proxy is one solution. All requests coming from clients using the same proxy appear to come from the IP address and User-Agent of the proxy itself, rather than the individual clients. If a number of clients use the same proxy (say, an entire business or university), it becomes much harder to link a particular HTTP transaction to a single computer or individual.

Links:

RFC 1945 The Hypertext Transfer Protocol, version 1.0

Assignment DetailsThe Basics

Your first task is to build a basic web proxy capable of accepting HTTP requests, making requests from remote servers, and returning data to a client.

This assignment can be completed in either ANSI C or C++. It should compile and run without errors from the FC 010 cluster, producing a binary called proxy that takes as its first argument a port to listen from. Don't use a hard-coded port number.

You shouldn't assume that your server will be running on a particular IP address, or that clients will be coming from a pre-determined IP.Listening

When your proxy starts, the first thing that it will need to do is establish a socket connection that it can use to listen for incoming connections. Your proxy should listen on the port specified from the command line, and wait for incoming client connections.

Once a client has connected, the proxy should read data from the client and then check for a properly-formatted HTTP request. An invalid request from the client should be answered with an appropriate error code.Parsing the URL

Once the proxy sees a valid HTTP request, it will need to parse the requested URL. The proxy needs at most three pieces of information: the requested host and port, and the requested path. See the URL (7) manual page for more info.Getting Data from the Remote Server

Once the proxy has parsed the URL, it can make a connection to the requested host (using the appropriate remote port, or the default of 80 if none is specified) and send a HTTP request for the appropriate file. The proxy then sends the HTTP request that it received from the client to the remote server.Returning Data to the Client

After the response from the remote server is received, the proxy should send the response message to the client via the appropriate socket. Once the transaction is complete, the proxy should close the connection.Testing Your Proxy

Run your client with the following command:

./proxy <port>, where port is the port number that the proxy should listen on. As a basic test of functionality, try requesting a page using telnet:

telnet localhost <port>Trying 127.0.0.1...Connected to localhost.localdomain (127.0.0.1).Escape character is '^]'.GET http://www.google.com HTTP/1.0

If your proxy is working correctly, the headers and HTML of the Google homepage should be displayed on your terminal screen.

For a slightly more complex test, you can configure your web browser to use your proxy server as its web proxy. See the section beflow for details.Configuring a Web Browser to Use a ProxyA Caveat

If you write a single-threaded proxy server, you will probably see some problems when you use your proxy with a standard web browser. Because a web browser like Firefox or IE issues multiple HTTP requests for each URL you request (for instance, to download images and other embedded content), a single-threaded proxy will likely miss some requests, resulting in missing images or other minor errors. That's OK. You are not required to use threading in this assignment. As long as your proxy works correctly for a simple HTML document (like, for instance, this assignment page) and follows the RFC, you can still receive all the points for this assignment.Firefox

Version 2.0:

Select Tools->Options from the menu. Click on the 'Advanced' icon in the Options dialog. Select the 'Network' tab, and click on 'Settings' in the 'Connections' area. Select 'Manual Proxy Configuration' from the options available. In the boxes, enter

Building a HTTP Proxy

the hostname and port where proxy program is running.

Earlier Versions:

Select Edit->Preferences from the menu. On the 'General' tab, click 'Connection Settings'. Select 'Manual Proxy Configuration' and enter the hostname and port where your proxy is running.

To stop using the proxy server, select 'Direct connection to the Internet' in the connection settings dialog.

Configuring Firefox to use HTTP/1.0

Because Firefox defaults to using HTTP/1.1 and your proxy speaks HTTP/1.0, there are a couple of minor changes that need to be made to Firefox's configuration. Fortunately, Firefox is smart enough to know when it is connecting through a proxy, and has a few special configuration keys that can be used to tweak the browser's behavior.

Type 'about:config' in the title bar. In the search/filter bar, type 'network.http.proxy' You should see three keys: network.http.proxy.keepalive, network.http.proxy.pipelining, and network.http.proxy.version. Set keepalive to false. Set version to 1.0. Make sure that pipelining is set to false.

Internet Explorer

Take a look at this page for complete instructions on enabling a proxy for various versions if Internet Explorer.

You should also do the following to make Internet Explorer work in a HTTP 1.0 compatible mode with your proxy:

Under Internet Options, select the 'Advanced' tab. Scroll down to HTTP 1.1 Settings. Uncheck 'Use HTTP 1.1 through proxy connections'.

Socket Programming

In order to build your proxy you will need to learn and become comfortable programming sockets. The Berkeley sockets library is the standard method of creating network systems on Unix. There are a number of functions that you will need to use for this assignment:

Parsing addresses:

inet_addr Convert a dotted quad IP address (such as 36.56.0.150) into a 32-bit address. gethostbyname Convert a hostname (such as argus.stanford.edu) into a 32-bit address. getservbyname Find the port number associated with a particular service, such as FTP.

Setting up a connection:

socket Get a descriptor to a socket of the given type connect Connect to a peer on a given socket getsockname Get the local address of a socket

Creating a server socket:

bind Assign an address to a socket listen Tell a socket to listen for incoming connections accept Accept an incoming connection

Communicating over the connection:

read/write Read and write data to a socket descriptor htons, htonl / ntohs , ntohl Convert between host and network byte orders (and vice versa) for 16 and 32-bit values

You can find the details of these functions in the Unix man pages (most of them are in section 2) and in the Stevens Unix Network Programming book, particularly chapters 3 and 4. Other sections you may want to browse include the client-server example system in chapter 5 (you will need to write both client and server code for this assignment) and the name and address conversion functions in chapter 9.

Links:

Guide to Network Programming Using Sockets An Introduction to Sockets Programming HTTP Made Really Easy- A Practical Guide to Writing Clients and Servers

Grading

You should submit your completed proxy by the date posted on the course website to iavramop at princeton dot edu. You will need to submit a tarball file containing the following:

All of the source code for your proxy A Makefile that builds your proxy A README file describing your code and the design decisions that you made, as described in the project guidelines page.

If you don't know how to create a tarball (tar archive), take a look at the sample Makefile at the top of the page, or man tar

Your proxy will be graded out of ten points, with the following criteria:

Your assignment should create a binary name proxy that will compile and run on the FC 010 cluster. The first command line argument should be the port that the proxy will listen from. Your proxy should run silently- any status messages or diagnostic output should be off by default. You can complete the assignment in either ANSI C or C++. Your proxy should work with both Firefox 2.0 and Internet Explorer 6. We'll first check that your proxy works correctly with a small number of major web pages, using the same script that we've given you to test your proxy. If your proxy passes all of these 'public' tests, you will get 7 of the possible points. We'll then check a number of additional URLs and transactions that you will not know in advance. If your proxy passes all of these tests, you get two additional points. These tests will check the overall robustness of your proxy, and how you handle certain edge cases. This may include sending your proxy incorrectly formed HTTP requests, large transfers, less common HTTP methods, etc. Well written (good abstraction, error checking, readability) and well commented code will get one additional point, for a total of 10. The first student to submit a proxy that scores a perfect 10 will win a prize!

There will also be some sort of prize for the best extension to the proxy. Adding an extension will not change your grade. Take a look below for some hints about possible extensions that you can add to the proxy.

As mentioned above you are not required to implement a multi-threaded proxy for this assignment. If you write a single-threaded client, you may see errors when using your proxy with a standard web browser, but that's OK. As long as your proxy works correctly for single HTTP transactions (for instance, try telnetting to to the port the proxy is running from and requesting a single HTML document) you can still receive all the possible points for this assignment.A Note on Network Programming

Writing code that will interact with other programs on the Internet is a little different than just writing something for your own use. The general guideline often given for network programs is: be lenient about what you accept, but strict about what you send. That is, even if a client doesn't do exactly the right thing, you should make a best effort to process their request if it is possible to easily figure out their intent. On the other hand, you should ensure that anything that you send out conforms to the published protocols as closely as possible. If an incoming request has a single field out of whack (such as sending you a request using HTTP 0.9 or 1.1), uses non-standard line terminators (some clients only send \r instead of the standard \r\n), or does something you don't quite expect with HTTP headers, you should still handle the request rather than dropping the request. Pay attention to parts of the RFC that specify areas where not all clients may conform exactly to what you expect. We'll be looking for this kind of interoperability in both the second round of tests that we run and in the style portion of your grade.

When in doubt, try to follow the behavior specified in RFC 1945. Also, check the FAQ for more specific guidelines.Possible Extensions

While it may not be obvious at first, proxies are very flexible tools that can serve a number of different purposes on the web. Common uses for proxies include improving giving performance boosts to dial-up users (through caching and pre-fetching), privacy protection (through anonymous proxies), content filtering and blocking (used in many "NetNanny"-type applications), and content transformation.

Sample Proxy Applications:

Anonymizer - A privacy protection/anonymous browsing service. Foxy - A filtering web proxy. Google Web Accelerator - The latest of a number of 'accelerators'.

You can impliment any of the following extensions (or some other extension that you've created yourself) as part of the contest we'll be running along with this assignment.

Content Transformation

Content transformation is the process of a proxy inserting, removing, or changing the contents of a resource requested from a remote server. After the resource has been retrieved from the server, the proxy is free to do whatever it would like to the content. Since the data returned from a web server is usually just text, this means that we can change the page almost any way we want- add or remove dirty words, change the text to Pig-Latin, rotate the images on the page 90 degrees, etc.Caching

Caching is one of the most common performance enhancements that web proxies implement. Caching takes advantage of the fact that most pages on the web don't change that often, and that any page that you visit once you (or someone else using the same proxy) are likely to visit again. A caching proxy server saves a copy of the files that it retrieves from remote servers. When another request comes in for the same resource, it returns the saved (or cached) copy instead of creating a new connection to a remote server. This saves a modest amount of time and CPU if the remote server is nearby and lightly trafficked, but can create more significant savings in the case of a more distant server or a remote server that is overloaded (it can also help reduce the load on heavily trafficked servers).

Caching introduces a few new complexities as well. First of all, a great deal of web content is dynamically generated, and as such shouldn't really be cached. Second, we need to decide how long to keep pages around in our cache. If the timeout is set too short, we negate most of the advantages of having a caching proxy. If the timeout is set too long, the client may end up looking at pages that are outdated or irrelevant.

There are a few steps to implementing caching behavior for your web proxy:

First, alter your proxy so that you can specify a timeout value (probably in seconds) on the command line. Second, you'll need to alter how your proxy retrieves pages. It should now check to see if a page exists in the proxy before retrieving a page from a remote server. If there is a valid cached copy of the page, that should be presented to the client instead of creating a new server connection. Finally, you will need to somehow implement cache expiration. The timing does not need to be exact (i.e. it's okay if a page is still in your cache after the timeout has expired, but it's not okay to serve a cached page after its timeout has expired), but you want to ensure that pages that are older than the user-set timeout are not served from the cache.

Link Prefetch

Building on top of your caching and content transformation code, the last piece of functionality that you will implement is called link prefetching. The idea behind link prefetching is simple: if a user asks for a particular page, the odds are that he or she will next request a page linked from that page. Link prefetching uses this information to attempt to speed up browsing by parsing requested pages for links, and then fetching the linked pages in the background. The pages fetched from the links are stored in the cache, ready to be served to the client when they are requested without the client having to wait around for the remote server to be contacted.

Parsing and fetching links can take an appreciable amount of time, especially for a page with a lot of links. For this reason, if you haven't already, at this stage you should make your proxy into a multi-threaded application. One thread should remain dedicated to the tasks that you have already implemented: reading requests from the client and serving pages from either the cache or a remote server. In a separate thread, the proxy will parse a page and extract the HTTP links, request those links from the remote server, and add them to the cache.Other Possible Extensions

HTTP 1.1 Support HTTP Connection Keep-Alive

本文链接地址:https://www.jiuchutong.com/zhishi/313342.html 转载请保留说明!

上一篇:织梦dedecms动态获取会员总数方法(织梦怎么改网站主页)

下一篇:syslogng配置(syslog ng)

  • 小米小爱智能音箱怎么使用(小米小爱智能音箱怎么联网)

    小米小爱智能音箱怎么使用(小米小爱智能音箱怎么联网)

  • 微视收款方没有实名认证(微视付完款买东西看不到订单)

    微视收款方没有实名认证(微视付完款买东西看不到订单)

  • 怎么通过微信号找到一个人(怎么通过微信号查出电话号码)

    怎么通过微信号找到一个人(怎么通过微信号查出电话号码)

  • 尺寸和像素是一样的吗(尺寸和像素什么关系)

    尺寸和像素是一样的吗(尺寸和像素什么关系)

  • 蓝牙耳机底噪是通病吗(蓝牙耳机底噪是什么声音)

    蓝牙耳机底噪是通病吗(蓝牙耳机底噪是什么声音)

  • 怎样取消拼多多订阅物流动态(怎样取消拼多多订单)

    怎样取消拼多多订阅物流动态(怎样取消拼多多订单)

  • 苹果日期和时间怎么修改不了(苹果日期和时间字体太大了在哪里设置)

    苹果日期和时间怎么修改不了(苹果日期和时间字体太大了在哪里设置)

  • iphone尾插坏了症状(iphone尾插坏了会导致黑屏重启吗)

    iphone尾插坏了症状(iphone尾插坏了会导致黑屏重启吗)

  • 7p能用18w快充吗(苹果7p能用18w充电器吗)

    7p能用18w快充吗(苹果7p能用18w充电器吗)

  • 退货怎么查看物流信息(退货怎么查询)

    退货怎么查看物流信息(退货怎么查询)

  • 抖音60秒以上视频怎么发(抖音60秒以上视频怎么做)

    抖音60秒以上视频怎么发(抖音60秒以上视频怎么做)

  • 一个优酷会员可以登几个手机(一个优酷会员可以登录几台手机)

    一个优酷会员可以登几个手机(一个优酷会员可以登录几台手机)

  • 华为p20有没有面部识别(华为p20有没有面容支付)

    华为p20有没有面部识别(华为p20有没有面容支付)

  • qq可不可以定时发消息给好友(qq可不可以定时发消息给群)

    qq可不可以定时发消息给好友(qq可不可以定时发消息给群)

  • cd-rom驱动器是计算机的基本部分吗(cd rom驱动器是外部设备吗)

    cd-rom驱动器是计算机的基本部分吗(cd rom驱动器是外部设备吗)

  • 宽带能看电视不能上网是怎么回事(宽带看电视不卡手机卡怎么回事)

    宽带能看电视不能上网是怎么回事(宽带看电视不卡手机卡怎么回事)

  • qq扩列匹配失败怎么办(qq扩列匹配失败匹配失败)

    qq扩列匹配失败怎么办(qq扩列匹配失败匹配失败)

  • bn43是小米什么型号电池(bn36是小米什么手机)

    bn43是小米什么型号电池(bn36是小米什么手机)

  • 苹果直营店装系统要钱吗(苹果直营店装系统要多久)

    苹果直营店装系统要钱吗(苹果直营店装系统要多久)

  • 怎么删除京东已评价(怎么删除京东已买过的订单)

    怎么删除京东已评价(怎么删除京东已买过的订单)

  • 为什么七天网络未授权(为什么七天网络查不到成绩)

    为什么七天网络未授权(为什么七天网络查不到成绩)

  • vivo云服务账号怎么退出(vivo云服务账号忘记了怎么办)

    vivo云服务账号怎么退出(vivo云服务账号忘记了怎么办)

  • web安全主要分几个方面(web安全包括)

    web安全主要分几个方面(web安全包括)

  • 苹果11可以插几张卡(苹果11可以插几张)

    苹果11可以插几张卡(苹果11可以插几张)

  • 苹果app自动扣费怎么取消(苹果App自动扣费是怎么回事)

    苹果app自动扣费怎么取消(苹果App自动扣费是怎么回事)

  • 剪映怎么调整背景音乐(剪映怎么调整背景颜色)

    剪映怎么调整背景音乐(剪映怎么调整背景颜色)

  • 什么是瀑布屏幕(啥是瀑布屏)

    什么是瀑布屏幕(啥是瀑布屏)

  • 荣耀9x有方舟编译器吗(荣耀9x方舟编译器)

    荣耀9x有方舟编译器吗(荣耀9x方舟编译器)

  • 响2声后显示通话中代表(响了两下然后通话中)

    响2声后显示通话中代表(响了两下然后通话中)

  • vivo手机测距仪在哪里(华为自带的ar测距仪)

    vivo手机测距仪在哪里(华为自带的ar测距仪)

  • 个税应纳税所得额是要上交的钱吗
  • 注册资本印花税减半征收政策
  • 留抵税额下月抵扣的分录
  • 财务台账包含什么
  • 公司开办期间的装修费怎么会计处理
  • 月中入职新公司社保谁交
  • 其他应收款账龄怎么算
  • 有限责任公司相关规定
  • 税盘锁了还能报税吗
  • 单张发票金额有多少
  • 金税盘坏了更换需要几天
  • 个人非税收入包括哪些
  • 应交税费和所得税费用会计分录
  • 如何核销财政票据的发票
  • 房地产公司转让土地
  • 摊余成本加还是减
  • 学校捐赠收入需上交吗
  • 一个月计提2个月工资
  • 预付账款金额过大的原因
  • 增值税税控盘抵减
  • 建筑企业员工培训
  • 我们公司春节发工资英文
  • 如何判断发票是否重复
  • 进货价加多少卖不亏增值税
  • 纸质承兑汇票到期怎么兑现,多久能到帐
  • 小型纳税人个人所得税
  • 电脑维修中常用的软件
  • 无形资产入账包括增值税吗
  • 资产评估报告包括
  • 华为p50新款
  • 无法使用内置管理员账户打开Edge
  • 产品外包装的作用
  • 已计提折旧怎么计算
  • 个税手续费会计分录
  • 财政部土地出让收支管理办法
  • 进程process.acore已停止怎么办
  • msmpeng.exe 是什么
  • 前端 php
  • 无形资产摊销是按原值吗
  • 会计中持有至到期投资是什么意思
  • 收到的国家电网电话
  • 运输费计税吗
  • error出错
  • php环境怎么搭
  • laravel 自定义guard
  • 垃圾清运费进入会计什么科目
  • 退回材料的会计分录
  • 存根联是自己留着吗
  • 织梦怎么用
  • scala实例
  • 支付给退休人员的退休费计入
  • 资产减值损失属于什么科目
  • sql2017附加数据库
  • 为什么一般纳税人税率高
  • 主播工资不发应该到哪里投诉
  • 处理报废的固定资产账务处理
  • 电脑折旧多少钱
  • 继续教育专项附加扣除可以扣几年
  • 本年利润是什么科目?
  • 工程费用科目
  • sql server 数据库介绍
  • win10正式版激活码
  • Vista 优化预读文件设置,提速开关机速度
  • freebsd操作命令
  • 苹果mac系统中英文切换
  • linux系统中的链接文件主要分为两种
  • wind移动版
  • extjs form textfield的隐藏方法
  • linux开发android好处
  • cocos2dx camera
  • cocos引擎教程
  • c语言node定义
  • 深入探讨英文
  • javascript的
  • js验证正整数
  • js判断浏览器内核和版本
  • css实现3d效果
  • bootstrap入门
  • 江西自考招考办
  • 免责声明:网站部分图片文字素材来源于网络,如有侵权,请及时告知,我们会第一时间删除,谢谢! 邮箱:opceo@qq.com

    鄂ICP备2023003026号

    网站地图: 企业信息 工商信息 财税知识 网络常识 编程技术

    友情链接: 武汉网站建设