server.php
来自「PHP 知识管理系统(基于树结构的知识管理系统), 英文原版的PHP源码。」· PHP 代码 · 共 1,998 行 · 第 1/5 页
PHP
1,998 行
function http_PROPPATCH()
{
if ($this->_check_lock_status($this->path)) {
$options = Array();
$options["path"] = $this->path;
$propinfo = new _parse_proppatch("php://input");
if (!$propinfo->success) {
$this->http_status("400 Error");
return;
}
$options['props'] = $propinfo->props;
$responsedescr = $this->PROPPATCH($options);
$this->http_status("207 Multi-Status");
header('Content-Type: text/xml; charset="utf-8"');
echo "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
echo "<D:multistatus xmlns:D=\"DAV:\">\n";
echo " <D:response>\n";
echo " <D:href>".$this->_urlencode($this->_mergePathes($this->_SERVER["SCRIPT_NAME"], $this->path))."</D:href>\n";
foreach ($options["props"] as $prop) {
echo " <D:propstat>\n";
echo " <D:prop><$prop[name] xmlns=\"$prop[ns]\"/></D:prop>\n";
echo " <D:status>HTTP/1.1 $prop[status]</D:status>\n";
echo " </D:propstat>\n";
}
if ($responsedescr) {
echo " <D:responsedescription>".
$this->_prop_encode(htmlspecialchars($responsedescr)).
"</D:responsedescription>\n";
}
echo " </D:response>\n";
echo "</D:multistatus>\n";
} else {
$this->http_status("423 Locked");
}
}
// }}}
// {{{ http_MKCOL()
/**
* MKCOL method handler
*
* @param void
* @return void
*/
function http_MKCOL()
{
$options = Array();
$options["path"] = $this->path;
$stat = $this->MKCOL($options);
$this->http_status($stat);
}
// }}}
// {{{ http_GET()
/**
* GET method handler
*
* @param void
* @returns void
*/
function http_GET()
{
// TODO check for invalid stream
$options = Array();
$options["path"] = $this->path;
$this->_get_ranges($options);
if (true === ($status = $this->GET($options))) {
if (!headers_sent()) {
$status = "200 OK";
if (!isset($options['mimetype'])) {
$options['mimetype'] = "application/octet-stream";
}
header("Content-type: $options[mimetype]");
if (isset($options['mtime'])) {
header("Last-modified:".gmdate("D, d M Y H:i:s ", $options['mtime'])."GMT");
}
if (isset($options['stream'])) {
// GET handler returned a stream
if (!empty($options['ranges']) && (0===fseek($options['stream'], 0, SEEK_SET))) {
// partial request and stream is seekable
if (count($options['ranges']) === 1) {
$range = $options['ranges'][0];
if (isset($range['start'])) {
fseek($options['stream'], $range['start'], SEEK_SET);
if (feof($options['stream'])) {
$this->http_status("416 Requested range not satisfiable");
return;
}
if (isset($range['end'])) {
$size = $range['end']-$range['start']+1;
$this->http_status("206 partial");
header("Content-length: $size");
header("Content-range: $range[start]-$range[end]/"
. (isset($options['size']) ? $options['size'] : "*"));
while ($size && !feof($options['stream'])) {
$buffer = fread($options['stream'], 4096);
$size -= strlen($buffer);
echo $buffer;
}
} else {
$this->http_status("206 partial");
if (isset($options['size'])) {
header("Content-length: ".($options['size'] - $range['start']));
header("Content-range: ".$range['start']."-".$range['end']."/"
. (isset($options['size']) ? $options['size'] : "*"));
}
fpassthru($options['stream']);
}
} else {
header("Content-length: ".$range['last']);
fseek($options['stream'], -$range['last'], SEEK_END);
fpassthru($options['stream']);
}
} else {
$this->_multipart_byterange_header(); // init multipart
foreach ($options['ranges'] as $range) {
// TODO what if size unknown? 500?
if (isset($range['start'])) {
$from = $range['start'];
$to = !empty($range['end']) ? $range['end'] : $options['size']-1;
} else {
$from = $options['size'] - $range['last']-1;
$to = $options['size'] -1;
}
$total = isset($options['size']) ? $options['size'] : "*";
$size = $to - $from + 1;
$this->_multipart_byterange_header($options['mimetype'], $from, $to, $total);
fseek($options['stream'], $from, SEEK_SET);
while ($size && !feof($options['stream'])) {
$buffer = fread($options['stream'], 4096);
$size -= strlen($buffer);
echo $buffer;
}
}
$this->_multipart_byterange_header(); // end multipart
}
} else {
// normal request or stream isn't seekable, return full content
if (isset($options['size'])) {
header("Content-length: ".$options['size']);
}
fpassthru($options['stream']);
return; // no more headers
}
} elseif (isset($options['data'])) {
if (is_array($options['data'])) {
// reply to partial request
} else {
header("Content-length: ".strlen($options['data']));
echo $options['data'];
}
}
}
}
if (!headers_sent()) {
if (false === $status) {
$this->http_status("404 not found");
} else {
// TODO: check setting of headers in various code pathes above
$this->http_status("$status");
}
}
}
/**
* parse HTTP Range: header
*
* @param array options array to store result in
* @return void
*/
function _get_ranges(&$options)
{
// process Range: header if present
if (isset($this->_SERVER['HTTP_RANGE'])) {
// we only support standard "bytes" range specifications for now
if (preg_match('/bytes\s*=\s*(.+)/', $this->_SERVER['HTTP_RANGE'], $matches)) {
$options["ranges"] = array();
// ranges are comma separated
foreach (explode(",", $matches[1]) as $range) {
// ranges are either from-to pairs or just end positions
list($start, $end) = explode("-", $range);
$options["ranges"][] = ($start==="")
? array("last"=>$end)
: array("start"=>$start, "end"=>$end);
}
}
}
}
/**
* generate separator headers for multipart response
*
* first and last call happen without parameters to generate
* the initial header and closing sequence, all calls inbetween
* require content mimetype, start and end byte position and
* optionaly the total byte length of the requested resource
*
* @param string mimetype
* @param int start byte position
* @param int end byte position
* @param int total resource byte size
*/
function _multipart_byterange_header($mimetype = false, $from = false, $to=false, $total=false)
{
if ($mimetype === false) {
if (!isset($this->multipart_separator)) {
// initial
// a little naive, this sequence *might* be part of the content
// but it's really not likely and rather expensive to check
$this->multipart_separator = "SEPARATOR_".md5(microtime());
// generate HTTP header
header("Content-type: multipart/byteranges; boundary=".$this->multipart_separator);
} else {
// final
// generate closing multipart sequence
echo "\n--{$this->multipart_separator}--";
}
} else {
// generate separator and header for next part
echo "\n--{$this->multipart_separator}\n";
echo "Content-type: $mimetype\n";
echo "Content-range: $from-$to/". ($total === false ? "*" : $total);
echo "\n\n";
}
}
// }}}
// {{{ http_HEAD()
/**
* HEAD method handler
*
* @param void
* @return void
*/
function http_HEAD()
{
$status = false;
$options = Array();
$options["path"] = $this->path;
if (method_exists($this, "HEAD")) {
$status = $this->head($options);
} else if (method_exists($this, "GET")) {
ob_start();
$status = $this->GET($options);
if (!isset($options['size'])) {
$options['size'] = ob_get_length();
}
ob_end_clean();
}
if (!isset($options['mimetype'])) {
$options['mimetype'] = "application/octet-stream";
}
header("Content-type: $options[mimetype]");
if (isset($options['mtime'])) {
header("Last-modified:".gmdate("D, d M Y H:i:s ", $options['mtime'])."GMT");
}
if (isset($options['size'])) {
header("Content-length: ".$options['size']);
}
if ($status === true) $status = "200 OK";
if ($status === false) $status = "404 Not found";
$this->http_status($status);
}
// }}}
// {{{ http_PUT()
/**
* PUT method handler
*
* @param void
* @return void
*/
function http_PUT()
{
if ($this->_check_lock_status($this->path)) {
$options = Array();
$options["path"] = $this->path;
$options["content_length"] = $this->_SERVER["CONTENT_LENGTH"];
// get the Content-type
if (isset($this->_SERVER["CONTENT_TYPE"])) {
// for now we do not support any sort of multipart requests
if (!strncmp($this->_SERVER["CONTENT_TYPE"], "multipart/", 10)) {
$this->http_status("501 not implemented");
echo "The service does not support mulipart PUT requests";
return;
}
$options["content_type"] = $this->_SERVER["CONTENT_TYPE"];
} else {
// default content type if none given
$options["content_type"] = "application/octet-stream";
}
/* RFC 2616 2.6 says: "The recipient of the entity MUST NOT
ignore any Content-* (e.g. Content-Range) headers that it
does not understand or implement and MUST return a 501
(Not Implemented) response in such cases."
*/
foreach ($this->_SERVER as $key => $val) {
if (strncmp($key, "HTTP_CONTENT", 11)) continue;
switch ($key) {
case 'HTTP_CONTENT_ENCODING': // RFC 2616 14.11
// TODO support this if ext/zlib filters are available
$this->http_status("501 not implemented");
echo "The service does not support '$val' content encoding";
return;
case 'HTTP_CONTENT_LANGUAGE': // RFC 2616 14.12
// we assume it is not critical if this one is ignored
// in the actual PUT implementation ...
$options["content_language"] = $val;
break;
case 'HTTP_CONTENT_LOCATION': // RFC 2616 14.14
/* The meaning of the Content-Location header in PUT
or POST requests is undefined; servers are free
to ignore it in those cases. */
break;
case 'HTTP_CONTENT_RANGE': // RFC 2616 14.16
// single byte range requests are supported
// the header format is also specified in RFC 2616 14.16
// TODO we have to ensure that implementations support this or send 501 instead
if (!preg_match('@bytes\s+(\d+)-(\d+)/((\d+)|\*)@', $val, $matches)) {
$this->http_status("400 bad request");
echo "The service does only support single byte ranges";
return;
}
$range = array("start"=>$matches[1], "end"=>$matches[2]);
if (is_numeric($matches[3])) {
$range["total_length"] = $matches[3];
}
$option["ranges"][] = $range;
// TODO make sure the implementation supports partial PUT
// this has to be done in advance to avoid data being overwritten
// on implementations that do not support this ...
break;
case 'HTTP_CONTENT_MD5': // RFC 2616 14.15
// TODO: maybe we can just pretend here?
$this->http_status("501 not implemented");
echo "The service does not support content MD5 checksum verification";
return;
default:
// any other unknown Content-* headers
$this->http_status("501 not implemented");
echo "The service does not support '$key'";
return;
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?