在IIS 7上使用PHP检索分块PUT数据挂起

在IIS 7上使用PHP检索分块PUT数据挂起,php,iis,Php,Iis,我使用PHP5.3.8Web服务运行IIS7。我已经为PUT请求创建了一个处理程序,当尝试PUT时,该处理程序被称为fine 为了访问客户端发送的PUT数据,我使用如下代码 if (($stream = fopen('php://input', "r")) !== FALSE) { fputs($logfile, "stream reading begin\r\n"); stream_set_timeout($stream, 5); $newcontent = "";

我使用PHP5.3.8Web服务运行IIS7。我已经为PUT请求创建了一个处理程序,当尝试PUT时,该处理程序被称为fine

为了访问客户端发送的PUT数据,我使用如下代码

if (($stream = fopen('php://input', "r")) !== FALSE) {
    fputs($logfile, "stream reading begin\r\n");
    stream_set_timeout($stream, 5);
    $newcontent = "";
    while ($chunk = fread($stream, 32)) {
        $newcontent .= $chunk;
        fputs($logfile, "stream read chunk >>$chunk<<\r\n");
    }
    // $newcontent = (stream_get_contents($stream));
    fputs($logfile, "stream read\r\n");
当我使用wireshark连接HTTP时,我看到它是空闲的,并且使用常规TCP保持活动

不用说,代码在Apache中运行良好

在我看来,PHP似乎无法识别传入数据的结尾,并等待更多数据。然而,即使如此,我也不明白为什么流超时没有生效

知道怎么解决吗

谢谢你,克里斯托夫

PS:我应该补充一点,客户机正在对PUT数据使用分块编码:

PUT /moodle-debug/.../....php?mac=IP111-28-00-75... HTTP/1.1
User-Agent: innovaphone-IP111/110792
Transfer-Encoding: chunked

23
vars check 09c4c4ec6778cb3eb4aa63

ac0
# 11r1 dvl IP111[11.0792], 
...
我在书里找到一张便条

在传输编码的情况下:分块IIS提供内容长度:-1 在请求数据中,该数据在sapi\U cgi\U read\U post中转换为uint。 使例程读取数据,直到读取完全部4G 2^32-1


这可以解释这种行为。接下来的问题是:有解决办法吗?

所以我花了两天时间才弄明白。以下是我的想法:

IIS7.x上带有FastCGI的PHP5.3.8无法处理具有分块编码数据的PUT请求 显然,IIS向PHP-1指示了一个负的内容大小,然后PHP-1尝试从HTTP请求流中读取大量字节-1到uint 由于客户端不发送那么多字节,因此读取不会成功 然而,由于客户端也没有关闭HTTP请求流,PHP会无休止地等待输入永远不会到来 在我的机器上运行了很长时间60分钟后,IIS终止了PHP请求的执行 为了解决这个我认为应该用PHP解决的问题,我为IIS创建了一个HTTP请求处理程序,用C编码。如下所示:

using System.Web;
using System.IO;
using System.Text;
using System;

namespace handler
{
    public class PutHandler : IHttpHandler
    {
        string path = "C:\\inetpub\\wwwroot\\put\\log\\mylog.txt";

        private void log(string line)
        {
            FileStream log;
            StreamWriter stream;
            log = File.Open(path, FileMode.Append);
            stream = new StreamWriter(log);
            stream.WriteLine(line);
            stream.Flush();
            stream.Close();
        }

        public static byte[] Combine(byte[] first, byte[] second)
        {
            byte[] ret = new byte[first.Length + second.Length];
            Buffer.BlockCopy(first, 0, ret, 0, first.Length);
            Buffer.BlockCopy(second, 0, ret, first.Length, second.Length);
            return ret;
        }

        public PutHandler()
        {
            log("PutHandler()");
        }
        ~PutHandler()
        {
            log("~PutHandler()");
        }

        // This handler is called whenever a file ending 
        // in .put is requested. A file with that extension
        // does not need to exist.
        public void ProcessRequest(HttpContext context)
        {
            log("ProcessRequest()");

            HttpRequest Request = context.Request;
            HttpResponse Response = context.Response;


            log("size " + context.Request.ContentLength);
            log("size " + context.Request.HttpMethod);
            byte[] allbytes = new byte[0];
            // context.Request.ContentLength is 0 for chunked encodings :-(
            while (true)
            {
                byte[] bytes = context.Request.BinaryRead(1024);
                if (bytes.Length <= 0) break;
                allbytes = PutHandler.Combine(allbytes, bytes);
            }
            log("Total " + allbytes.Length + " bytes in request");
            Response.StatusCode = 200;

            string uri = "http://" + Request.ServerVariables["SERVER_NAME"] + Request.RawUrl.Replace(".put", ".php");
            log("pushing back to " + uri);

            // now pushback to original target URL
            var client = new System.Net.WebClient();
            Response.BinaryWrite(client.UploadData(uri, "PUT", allbytes));

            log("RequestHandler done");
        }
        public bool IsReusable
        {
            // To enable pooling, return true here.
            // This keeps the handler in memory.
            get { return false; }
        }
    }
}
需要让IIS知道处理程序。为此,您需要在相应的应用程序目录中提供一个web.config,如下所示:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <system.webServer>
    <modules>
      <remove name="WebDAVModule" />
    </modules>
    <handlers>
      <remove name="WebDAV" />
      <add name="PutHandler" path="*.put" verb="PUT" type="handler.PutHandler, PutHandler"
          requireAccess="Script" />
    </handlers>
  </system.webServer>
</configuration>
这将删除标准WebDav模块/处理程序,因为我们不希望在这里使用它们。然后添加一个新的处理程序,其二进制文件需要安装在web应用程序的bin文件夹中。此处理程序用于所有带有.put后缀的URI,并且仅用于put请求。要使其工作,应用程序池必须处于集成模式

处理程序所做的是收集在C中运行良好的分块数据,然后将所有数据体和相对URI(包括所有查询参数)重新发送到同一个虚拟服务器,并将.put后缀更改为.php。由于这是在一个步骤中发送的,因此不使用分块编码,并且接收的PHP代码工作正常

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <system.webServer>
    <modules>
      <remove name="WebDAVModule" />
    </modules>
    <handlers>
      <remove name="WebDAV" />
      <add name="PutHandler" path="*.put" verb="PUT" type="handler.PutHandler, PutHandler"
          requireAccess="Script" />
    </handlers>
  </system.webServer>
</configuration>