HTTP服务器 - 提供favicon.ico

时间:2017-07-16 04:48:10

标签: java http web server

我正在设置我自己的java http服务器,以便更好地了解http服务器以及网络内容。我开发了一个非常简单的服务器,并且能够以JSON形式提供html页面和数据。然后我看到了浏览器(我使用的是Chrome,但假设它与其他人一样)正在发送一个favicon.ico请求。我能够在我的服务器上识别出该请求,因此我尝试提供我下载的随机图标并将其调整为png格式的16x16像素,因为互联网所说的大小需要是。这是我的代码,请注意它不应该是专业的,只是适合我的基本教育目的:

[set up ServerSocket and listen]
public static String err_header = "HTTP/1.1 500 ERR\nAccess-Control-Allow-Origin: *";
public static String success_header = "HTTP/1.1 200 OK\nAccess-Control-Allow-Origin: *";
public static String end_header = "\r\n\r\n";
while(true){
        try{
            System.out.println("Listening for new connections");
            clientSocket = server.accept();
            System.out.println("Connection established");
            InputStreamReader isr = new InputStreamReader(clientSocket.getInputStream());
            BufferedReader reader = new BufferedReader(isr);
            String getLine = reader.readLine();//first line of HTTP request
            handleRequest(getLine,clientSocket);
        }//end of try
        catch(Exception e){
            [error stuff]
        }//end of catch
    }//end of while

HandleRequest方法:

public static void handleRequest(String getLine,Socket clientSocket) throws Exception{
    if(getLine.substring(5,16).equals("favicon.ico")){
        List<String> iconTag = new ArrayList<String>();
        iconTag.add("\nContent-Type: image/png");
        handleFileRequest("[file]",iconTag,clientSocket);
    }//end of if
    else{
        handleFileRequest("[file]",clientSocket);
    }//end of else
}//end of handleRequest

图像的handleFileRequest:

public static void handleFileRequest(String fileName,List<String> headerTags,Socket clientSocket) throws Exception{
    OutputStream out = clientSocket.getOutputStream();
    BufferedReader read = new BufferedReader(new FileReader(fileName));
    out.write(success_header.getBytes("UTF-8"));
    Iterator<String> itr = headerTags.iterator();
    while(itr.hasNext()){
        out.write(itr.next().getBytes("UTF-8"));
    }//end of while
    out.write(end_header.getBytes("UTF-8"));
    String readLine = "";
    while((readLine = read.readLine())!=null){
        out.write(readLine.getBytes("UTF-8"));
    }//end of while
    out.flush();
    out.close();
}//end of handleFileRequest

它似乎工作,因为服务器发送文件,浏览器显示200 OK响应,但没有favicon,当我过滤网络请求只是图像时,页面请求一个图像正在服务但是favicon请求没有在那里列出(favicon请求在&#34;其他&#34;部分)。类似地,当点击另一图像时,图像显示在预览上,而对于favicon请求则不是这种情况。截图:

favicon

与此同时,这是另一张图片的样子,它在页面中显示得很好:

other image

我也尝试过包含Content-Length标题,但这似乎没有什么区别。我错过了一些明显的东西吗?

另外,为了澄清,我知道我可以在实际的html页面中包含favicon,目标不是这样做,而是要了解它是如何工作的。

1 个答案:

答案 0 :(得分:3)

读取二进制文件

似乎没有正确提供图标的内容。

我怀疑这很可能是因为您阅读其内容的方式:

while((readLine = read.readLine())!=null){
    out.write(readLine.getBytes("UTF-8"));
}

逐行阅读二进制内容是不合适的, 因为行的概念,还有UTF-8编码, 在二进制文件的上下文中没有意义。 并且你无法通过这种方式逐行正确读取二进制内容, 因为readLine的{​​{1}}方法不会返回整行,因为它会从最后删除换行符。 您无法手动添加换行符,因为您无法确切知道它是什么。

这是一种更简单,更正确的方法来读取二进制文件的内容:

BufferedReader

完成此操作后,可以使用byte[] bytes = Files.readAllBytes(Paths.get("/path/to/file")); 的值轻松生成包含内容长度的正确文件标题。

在浏览器中访问页面时会发生什么

如果我们澄清一些事情,似乎对你有好处。

在浏览器中打开网址时 浏览器向Web服务器发送GET请求以下载您指定的原始URL的内容。

一旦有了页面内容,它就会发送更多的GET请求:

  • 如果尚未拥有图标,请获取图标。可以在HTML文档中指定此位置,否则浏览器将尝试默认提取bytes.length
  • 获取文档中任意(有效)SERVERNAME/favicon.ico标记的src属性中指定的图像
  • 获取文档中任意(有效)<img/>标记的href属性中指定的样式表
  • ...同样适用于<style/>代码,依此类推......

favicon纯粹是装饰性的,在浏览器标签标题中显示, 其他资源对于呈现页面至关重要。 它们在像lynx这样的基于文本的浏览器中不是必不可少的, 这样的浏览器显然不会获取这些资源。

这是为什么要求favicon的解释,以及如何。

Web服务器如何提供文件?

在最基本的情况下,提供文件有两个重要组成部分:

  1. 生成适当的HTTP标头:标头中的每一行都采用<script/>格式,每行必须以name: value结尾。 必须至少有一个\n标题。 标题必须以空行终止。

  2. 在终止标题的空行之后, 内容可以是任何东西,甚至二进制。 举例说明, 考虑Content-type命令,该命令将URL的内容转储到标准输出。 如果您运行curl, 你会看到html文件的内容。 如果您运行curl url-to-some-html-file, 你会看到图像文件的内容。 这将是不可读的,你的终端可能会发出有趣的声音。 您可以将输出重定向到curl url-to-some-image-file的文件, 这会给你一个图像文件, 二进制内容, 您可以在任何图像查看器工具中打开。

  3. 简而言之,提供文件实际上只是在curl url-to-some-image-file > image.png上打印标题, 然后打印一个空行以终止标题, 然后在stdout上打印内容。

    调试图像的提供

    调试正确提供图像的简便方法是使用stdout将URL保存到文件中, 然后验证保存的文件和原始文件是否相同, 例如,使用curl命令:

    cmp

    curl -o file url-to-favicon cmp file /path/to/original 的输出应为空。 如果在两个文件中发现差异,则此命令仅生成输出。

    实施简单的HTTP服务器

    而不是使用cmp, 这是实现HTTP服务器的一种非常简单的方法:

    ServerSocket