Completed
Push — master ( ed35ab...871ad1 )
by Michael
05:54
created

proxy.php ➔ http_request()   F

Complexity

Conditions 19
Paths 737

Size

Total Lines 105
Code Lines 63

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 19
eloc 63
nc 737
nop 2
dl 0
loc 105
rs 2.3386
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
0 ignored issues
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 92 and the first side effect is on line 22.

The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.

The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.

To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.

Loading history...
2
/**
3
 * Gentics Aloha Editor AJAX Gateway
4
 * Copyright (c) 2010 Gentics Software GmbH
5
 * Licensed unter the terms of http://www.aloha-editor.com/license.html
6
 * [email protected]
7
 * Author Haymo Meran [email protected]
8
 * Author Johannes Schüth [email protected]
9
 * Author Tobias Steiner [email protected]
10
 * 
11
 * Testing from the command line:
12
 * function getallheaders(){return array('X-Gentics' => 'X');};
13
 * https url example: https://google.com/adsense
14
 * 
15
 */
16
17
// for debugging
18
//$_SERVER['SERVER_PROTOCOL'] = 'HTTP/1.0';
19
//$_SERVER['REQUEST_METHOD'] = 'HEAD';
20
//error_reporting(E_ALL);
21
22
$request = array(
23
    'method'   => $_SERVER['REQUEST_METHOD'],
24
    'protocol' => $_SERVER['SERVER_PROTOCOL'],
25
    'headers'  => getallheaders(),
26
    //TODO: multipart/form-data is not handled by php://input. there
27
    //doesn't seem to be a generic way to get at the raw post data for
28
    //that content-type.
29
    'payload'  => file_get_contents('php://input'),
30
);
31
32
// read url parameter
33
if (array_key_exists('url', $_GET)) {
34
	$request['url'] = urldecode($_GET['url']);
35
} else {
36
	header("HTTP/1.0 400 Bad Request");
37
	echo "Aloha Editor AJAX Gateway failed because parameter url is missing.";
38
	exit();
39
}
40
41
// check if link exists
42
$response = http_request($request);
43
44
// Note HEAD does not always work even if specified...
45
// We use HEAD for Linkchecking so we do a 2nd request.
46
if (!array_key_exists('method', $response)) {
47
	$response['method'] = false;
48
}
49
50
if (strtoupper($response['method']) == 'HEAD' && (int) $response['status'] >= 400 ) {
51
52
	$request['method'] = 'GET';
53
	$response = http_request($request);
54
55
	//since we handle a HEAD, we don't need to proxy any contents
56
	fclose($response['socket']);
57
	$response['socket'] = null;
58
}
59
60
// forward each returned header...
61
foreach ($response['headers'] as $key => $value) {
62
	if (strtolower($key) == 'content-length') {
63
		//there is no need to specify a content length since we don't do keep
64
		//alive, and this can cause problems for integration (e.g. gzip output,
65
		//which would change the content length)
66
		//Note: overriding with header('Content-length:') will set
67
		//the content-length to zero for some reason
68
		continue;
69
	}
70
	header("$key: $value");
71
}
72
73
header('Connection: close');
74
75
// output the contents if any
76
if (null !== $response['socket']) {
77
	fpassthru($response['socket']);
78
	fclose($response['socket']);
79
}
80
81
exit;
82
83
/**
84
 * Query an HTTP(S) URL with the given request parameters and return the
85
 * response headers and status code. The socket is returned as well and
86
 * will point to the begining of the response payload (after all headers
87
 * have been read), and must be closed with fclose().
88
 * @param $url the request URL
89
 * @param $request the request method may optionally be overridden.
90
 * @param $timeout connection and read timeout in seconds
91
 */
92
function http_request($request, $timeout = 5) {
93
94
	$url = $request['url'];
95
	// Extract the hostname from url
96
	$parts = parse_url($url);
97
	if (array_key_exists('host', $parts)) {
98
		$remote = $parts['host'];
99
	} else {
100
		return myErrorHandler("url ($url) has no host. Is it relative?");
101
	}
102
	if (array_key_exists('port', $parts)) {
103
		$port = $parts['port'];
104
	} else {
105
		$port = 0;
106
	}
107
108
	// Beware that RFC2616 (HTTP/1.1) defines header fields as case-insensitive entities.
109
	$request_headers = "";
110
	foreach ($request['headers'] as $name => $value) {
111
		switch (strtolower($name)) {
112
		//omit some headers
113
		case "keep-alive":
114
		case "connection":
115
		case "cookie":
116
		//TODO: we don't handle any compression encodings. compression
117
		//can cause a problem if client communication is already being
118
		//compressed by the server/app that integrates this script
119
		//(which would double compress the content, once from the remote
120
		//server to us, and once from us to the client, but the client
121
		//would de-compress only once).
122
		case "accept-encoding":
123
			break;
124
		// correct the host parameter
125
		case "host":
126
			$host_info = $remote;
127
			if ($port) {
128
				$host_info .= ':' . $port;
129
			}
130
			$request_headers .= "$name: $host_info\r\n";
131
			break;
132
		// forward all other headers
133
		default:
134
			$request_headers .= "$name: $value\r\n";
135
			break;
136
		}
137
	}
138
139
	//set fsockopen transport scheme, and the default port
140
	switch (strtolower($parts['scheme'])) {
141
	case 'https':
142
		$scheme = 'ssl://';
143
		if ( ! $port ) $port = 443;
144
		break;
145
	case 'http':
146
		$scheme = '';
147
		if ( ! $port ) $port = 80;
148
		break;
149
	default:
150
		//some other transports are available but not really supported
151
		//by this script: http://php.net/manual/en/transports.inet.php
152
		$scheme = $parts['scheme'] . '://';
153
		if ( ! $port ) {
154
			return myErrorHandler("Unknown scheme ($scheme) and no port.");
155
		}
156
		break;
157
	}
158
159
	//we make the request with socket operations since we don't want to
160
	//depend on the curl extension, and the higher level wrappers don't
161
	//give us usable error information.
162
	$sock = @fsockopen("$scheme$remote", $port, $errno, $errstr, $timeout);
163
	if ( ! $sock ) {
164
		return myErrorHandler("Unable to open URL ($url): $errstr");
165
	}
166
167
	//the timeout in fsockopen is only for the connection, the following
168
	//is for reading the content
169
	stream_set_timeout($sock, $timeout);
170
171
	//an absolute url should only be specified for proxy requests
172
	if (array_key_exists('path', $parts)) {
173
		$path_info  = $parts['path'];
174
	} else {
175
		$path_info  = '/';
176
	}
177
178
	if (array_key_exists('query',    $parts)) $path_info .= '?' . $parts['query'];
179
	if (array_key_exists('fragment', $parts)) $path_info .= '#' . $parts['fragment'];
180
181
	$out = $request["method"]." ".$path_info." ".$request["protocol"]."\r\n"
182
		 . $request_headers
183
		 . "Connection: close\r\n\r\n";
184
	fwrite($sock, $out);
185
	fwrite($sock, $request['payload']);
186
187
	$header_str = stream_get_line($sock, 1024*16, "\r\n\r\n");
188
	$headers = http_parse_headers($header_str);
189
	$status_line = array_shift($headers);
190
191
	// get http status
192
	preg_match('|HTTP/\d+\.\d+\s+(\d+)\s+.*|i',$status_line,$match);
193
	$status = $match[1];
194
195
	return array('headers' => $headers, 'socket' => $sock, 'status' => $status);
196
}
197
198
/**
199
 * Parses a string containing multiple HTTP header lines into an array
200
 * of key => values.
201
 * Inspired by HTTP::Daemon (CPAN).
202
 */
203
function http_parse_headers($header_str) {
204
	$headers = array();
205
206
	//ignore leading blank lines
207
	$header_str = preg_replace("/^(?:\x0D?\x0A)+/", '', $header_str);
208
209
	while (preg_match("/^([^\x0A]*?)\x0D?(?:\x0A|\$)/", $header_str, $matches)) {
210
		$header_str = substr($header_str, strlen($matches[0]));
211
		$status_line = $matches[1];
212
213
		if (empty($headers)) {
214
			// the status line
215
			$headers[] = $status_line;
216
		}
217
		elseif (preg_match('/^([^:\s]+)\s*:\s*(.*)/', $status_line, $matches)) {
218
			if (isset($key)) {
219
				//previous header is finished (was potentially multi-line)
220
				$headers[$key] = $val;
221
			}
222
			list(,$key,$val) = $matches;
223
		}
224
		elseif (preg_match('/^\s+(.*)/', $status_line, $matches)) {
225
			//continue a multi-line header
226
			$val .= " ".$matches[1];
227
		}
228
		else {
229
			//empty (possibly malformed) header signals the end of all headers
230
			break;
231
		}
232
	}
233
	if (isset($key)) {
234
		$headers[$key] = $val;
235
	}
236
	return $headers;
237
}
238
239
function myErrorHandler($msg)
240
{
241
	// 500 could be misleading... 
242
	// Should we return a special Error when a proxy error occurs?
243
	header("HTTP/1.0 500 Internal Error");
244
	echo "Gentics Aloha Editor AJAX Gateway Error: $msg";
245
	exit();
0 ignored issues
show
Coding Style Compatibility introduced by
The function myErrorHandler() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
246
}
247
248
//EOF
249