mirror of
https://we.phorge.it/source/phorge.git
synced 2024-12-01 03:02:43 +01:00
c7f23f522a
Summary: Mostly ripped from D7391, with some changes: - Serve repositories at `/diffusion/X/`, with no special `/git/` or `/serve/` URI component. - This requires a little bit of magic, but I got the magic working for Git, Mercurial and SVN, and it seems reasonable. - I think having one URI for everything will make it easier for users to understand. - One downside is that git will clone into `X` by default, but I think that's not a big deal, and we can work around that in the future easily enough. - Accept HTTP requests for Git, SVN and Mercurial repositories. - Auth logic is a little different in order to be more consistent with how other things work. - Instead of AphrontBasicAuthResponse, added "VCSResponse". Mercurial can print strings we send it on the CLI if we're careful, so support that. I did a fair amount of digging and didn't have any luck with git or svn. - Commands we don't know about are assumed to require "Push" capability by default. No actual VCS data going over the wire yet. Test Plan: Ran a bunch of stuff like this: $ hg clone http://local.aphront.com:8080/diffusion/P/ abort: HTTP Error 403: This repository is not available over HTTP. ...and got pretty reasonable-seeming errors in all cases. All this can do is produce errors for now. Reviewers: hach-que, btrahan Reviewed By: hach-que CC: aran Maniphest Tasks: T2230 Differential Revision: https://secure.phabricator.com/D7417
151 lines
4 KiB
PHP
151 lines
4 KiB
PHP
<?php
|
|
|
|
/**
|
|
* @group aphront
|
|
*/
|
|
abstract class AphrontResponse {
|
|
|
|
private $request;
|
|
private $cacheable = false;
|
|
private $responseCode = 200;
|
|
private $lastModified = null;
|
|
|
|
protected $frameable;
|
|
|
|
public function setRequest($request) {
|
|
$this->request = $request;
|
|
return $this;
|
|
}
|
|
|
|
public function getRequest() {
|
|
return $this->request;
|
|
}
|
|
|
|
public function getHeaders() {
|
|
$headers = array();
|
|
if (!$this->frameable) {
|
|
$headers[] = array('X-Frame-Options', 'Deny');
|
|
}
|
|
|
|
return $headers;
|
|
}
|
|
|
|
public function setCacheDurationInSeconds($duration) {
|
|
$this->cacheable = $duration;
|
|
return $this;
|
|
}
|
|
|
|
public function setLastModified($epoch_timestamp) {
|
|
$this->lastModified = $epoch_timestamp;
|
|
return $this;
|
|
}
|
|
|
|
public function setHTTPResponseCode($code) {
|
|
$this->responseCode = $code;
|
|
return $this;
|
|
}
|
|
|
|
public function getHTTPResponseCode() {
|
|
return $this->responseCode;
|
|
}
|
|
|
|
public function getHTTPResponseMessage() {
|
|
return '';
|
|
}
|
|
|
|
public function setFrameable($frameable) {
|
|
$this->frameable = $frameable;
|
|
return $this;
|
|
}
|
|
|
|
public static function processValueForJSONEncoding(&$value, $key) {
|
|
if ($value instanceof PhutilSafeHTMLProducerInterface) {
|
|
// This renders the producer down to PhutilSafeHTML, which will then
|
|
// be simplified into a string below.
|
|
$value = hsprintf('%s', $value);
|
|
}
|
|
|
|
if ($value instanceof PhutilSafeHTML) {
|
|
// TODO: Javelin supports implicity conversion of '__html' objects to
|
|
// JX.HTML, but only for Ajax responses, not behaviors. Just leave things
|
|
// as they are for now (where behaviors treat responses as HTML or plain
|
|
// text at their discretion).
|
|
$value = $value->getHTMLContent();
|
|
}
|
|
}
|
|
|
|
public static function encodeJSONForHTTPResponse(array $object) {
|
|
|
|
array_walk_recursive(
|
|
$object,
|
|
array('AphrontResponse', 'processValueForJSONEncoding'));
|
|
|
|
$response = json_encode($object);
|
|
|
|
// Prevent content sniffing attacks by encoding "<" and ">", so browsers
|
|
// won't try to execute the document as HTML even if they ignore
|
|
// Content-Type and X-Content-Type-Options. See T865.
|
|
$response = str_replace(
|
|
array('<', '>'),
|
|
array('\u003c', '\u003e'),
|
|
$response);
|
|
|
|
return $response;
|
|
}
|
|
|
|
protected function addJSONShield($json_response) {
|
|
|
|
// Add a shield to prevent "JSON Hijacking" attacks where an attacker
|
|
// requests a JSON response using a normal <script /> tag and then uses
|
|
// Object.prototype.__defineSetter__() or similar to read response data.
|
|
// This header causes the browser to loop infinitely instead of handing over
|
|
// sensitive data.
|
|
|
|
$shield = 'for (;;);';
|
|
|
|
$response = $shield.$json_response;
|
|
|
|
return $response;
|
|
}
|
|
|
|
public function getCacheHeaders() {
|
|
$headers = array();
|
|
if ($this->cacheable) {
|
|
$headers[] = array(
|
|
'Expires',
|
|
$this->formatEpochTimestampForHTTPHeader(time() + $this->cacheable));
|
|
} else {
|
|
$headers[] = array(
|
|
'Cache-Control',
|
|
'private, no-cache, no-store, must-revalidate');
|
|
$headers[] = array(
|
|
'Pragma',
|
|
'no-cache');
|
|
$headers[] = array(
|
|
'Expires',
|
|
'Sat, 01 Jan 2000 00:00:00 GMT');
|
|
}
|
|
|
|
if ($this->lastModified) {
|
|
$headers[] = array(
|
|
'Last-Modified',
|
|
$this->formatEpochTimestampForHTTPHeader($this->lastModified));
|
|
}
|
|
|
|
// IE has a feature where it may override an explicit Content-Type
|
|
// declaration by inferring a content type. This can be a security risk
|
|
// and we always explicitly transmit the correct Content-Type header, so
|
|
// prevent IE from using inferred content types. This only offers protection
|
|
// on recent versions of IE; IE6/7 and Opera currently ignore this header.
|
|
$headers[] = array('X-Content-Type-Options', 'nosniff');
|
|
|
|
return $headers;
|
|
}
|
|
|
|
private function formatEpochTimestampForHTTPHeader($epoch_timestamp) {
|
|
return gmdate('D, d M Y H:i:s', $epoch_timestamp).' GMT';
|
|
}
|
|
|
|
abstract public function buildResponseString();
|
|
|
|
}
|