From c8c4f937cba7add5cdd38d8dcd1822820615be2f Mon Sep 17 00:00:00 2001 From: PiBa-NL Date: Sat, 13 Dec 2014 16:42:52 +0100 Subject: haproxy-devel, add options to set the 'errorfile' option to replace haproxy errors like 'no server available' --- config/haproxy-devel/haproxy.inc | 50 ++++++- config/haproxy-devel/haproxy.xml | 5 + config/haproxy-devel/haproxy_files.php | 176 ++++++++++++++++++++++++ config/haproxy-devel/haproxy_htmllist.inc | 50 ++++++- config/haproxy-devel/haproxy_listeners_edit.php | 5 +- config/haproxy-devel/haproxy_pool_edit.php | 40 ++++++ config/haproxy-devel/pkg_haproxy_tabs.inc | 2 + 7 files changed, 319 insertions(+), 9 deletions(-) create mode 100644 config/haproxy-devel/haproxy_files.php (limited to 'config') diff --git a/config/haproxy-devel/haproxy.inc b/config/haproxy-devel/haproxy.inc index 5b56595a..c839cd4c 100644 --- a/config/haproxy-devel/haproxy.inc +++ b/config/haproxy-devel/haproxy.inc @@ -196,6 +196,19 @@ $a_sticky_type['stick_rdp_cookie'] = array('name' => 'Stick on RDP-cookie', 'descr' => "Uses a RDP-Cookie send by the mstsc client, note that not all clients send this.", 'cookiedescr' => 'EXAMPLE: msts or mstshash'); + +global $a_error; +$a_error = array(); +$a_error['200'] = array('descr' => "stats or monitoring requests"); +$a_error['400'] = array('descr' => "request invalid or too large"); +$a_error['401'] = array('descr' => "authentication is required to perform the action"); +$a_error['403'] = array('descr' => "request is forbidden"); +$a_error['408'] = array('descr' => "timeout before the request is complete"); +$a_error['500'] = array('descr' => "internal error"); +$a_error['502'] = array('descr' => "server response invalid or blocked"); +$a_error['503'] = array('descr' => "no server was available to handle the request"); +$a_error['504'] = array('descr' => "timeout before the server responds"); + if(!function_exists('group_ports')){ // function group_ports() is present in pfSense 2.2 in util.inc /* create ranges of sequential port numbers (200:215) and remove duplicates */ @@ -308,6 +321,20 @@ function haproxy_hostoralias_to_list($host_or_alias) { return $result; } +function haproxy_get_fileslist() { + // returns the files array with 'keys'. + $result = array(); + global $config; + // create a copy to not modify the original 'keyless' array + $a_files = $config['installedpackages']['haproxy']['files']['item']; + if (!is_array($a_files)) $a_files = array(); + foreach($a_files as $file) { + $key = $file['name']; + $result[$key] = $file; + } + return $result; +} + function haproxy_custom_php_deinstall_command() { global $static_output; $static_output .= "HAProxy, running haproxy_custom_php_deinstall_command()\n"; @@ -603,7 +630,7 @@ function haproxy_find_acl($name) { function write_backend($configpath, $fd, $name, $pool, $frontend) { if(!is_array($pool['ha_servers']['item']) && !$pool['stats_enabled']=='yes') return; - global $a_checktypes, $a_cookiemode; + global $a_checktypes, $a_cookiemode, $a_files, $a_error; $a_servers = &$pool['ha_servers']['item']; $frontendtype = $frontend['type']; @@ -667,6 +694,23 @@ function write_backend($configpath, $fd, $name, $pool, $frontend) { fwrite ($fd, "\tstats\t\t\tscope " . $scope_item . "\n"); } } + + if (is_arrayset($pool,'errorfiles','item')) { + foreach($pool['errorfiles']['item'] as $errorfile) { + if (!is_array($a_files))// load only once + $a_files = haproxy_get_fileslist(); + $file = $errorfile['errorfile']; + $errorcodes = explode(",",$errorfile['errorcode']); + foreach($errorcodes as $errorcode) { + $filename = "$configpath/errorfile_{$name}_{$errorcode}_{$file}"; + $content = base64_decode($a_files[$file]['content']); + $content = str_replace('{errormsg}', $a_error[$errorcode]['descr'], $content); + $content = str_replace('{errorcode}', $errorcode, $content); + file_put_contents($filename, $content); + fwrite ($fd, "\terrorfile\t\t\t" . $errorcode ." " . $filename . "\n"); + } + } + } } switch($pool["persist_sticky_type"]) { @@ -1668,7 +1712,7 @@ function get_frontend_ipport($frontend, $userfriendly=false) { $mainfrontend = get_primaryfrontend($frontend); $newline = ""; $result = array(); - if (!isset($mainfrontend)) + if (!is_arrayset($mainfrontend,"a_extaddr","item")) return $result; foreach($mainfrontend['a_extaddr']['item'] as $extaddr) { if ($extaddr['extaddr'] == 'custom'){ @@ -1725,6 +1769,8 @@ function get_haproxy_frontends($excludeitem="") { global $config; $a_frontend = &$config['installedpackages']['haproxy']['ha_backends']['item']; $result = array(); + if(!is_array($a_frontend)) + return $result; foreach($a_frontend as &$frontend) { if ($frontend['secondary']) diff --git a/config/haproxy-devel/haproxy.xml b/config/haproxy-devel/haproxy.xml index acd934a7..6156c174 100644 --- a/config/haproxy-devel/haproxy.xml +++ b/config/haproxy-devel/haproxy.xml @@ -87,6 +87,11 @@ 077 https://packages.pfsense.org/packages/config/haproxy-devel/haproxy_global.php + + /usr/local/www/ + 077 + https://packages.pfsense.org/packages/config/haproxy-devel/haproxy_files.php + /usr/local/www/ 077 diff --git a/config/haproxy-devel/haproxy_files.php b/config/haproxy-devel/haproxy_files.php new file mode 100644 index 00000000..4946a7be --- /dev/null +++ b/config/haproxy-devel/haproxy_files.php @@ -0,0 +1,176 @@ +keyfield = "name"; + +if ($_POST) { + $pconfig = $_POST; + + if ($_POST['apply']) { + $result = haproxy_check_and_run($savemsg, true); + if ($result) + unlink_if_exists($d_haproxyconfdirty_path); + } else { + $a_files = $fileslist->haproxy_htmllist_get_values($fields_files); + $filedupcheck = array(); + + foreach($a_files as $key => $file) { + $name = $file['name']; + if (!preg_match("/^[a-zA-Z][a-zA-Z0-9\.\-_]*$/", $file['name'])) + $input_errors[] = "The field 'Name' (".htmlspecialchars($file['name']).") contains invalid characters. Use only: a-zA-Z0-9.-_ and start with a letter"; + if (isset($filedupcheck[$name])) + $input_errors[] = "Duplicate names are not allowed: " . htmlspecialchars($name); + $filedupcheck[$name] = true; + } + + // replace references in backends to renamed 'files' + foreach($a_pools as &$backend) { + if (is_arrayset($backend,'errorfiles','item')) + foreach($backend['errorfiles']['item'] as &$errorfile) { + $found = false; + foreach($a_files as $key => $file) { + if ($errorfile['errorfile'] == $key) { + $errorfile['errorfile'] = $file['name']; + $found = true; + } + } + if (!$found) + $input_errors[] = "Errorfile marked for deletion: " . $errorfile['errorfile'] . " which is used in backend " . $backend['name']; + } + } + if (!$input_errors) { + // save config when no errors found + touch($d_haproxyconfdirty_path); + write_config($changedesc); + header("Location: haproxy_files.php"); + exit; + } + } +} + +$pf_version=substr(trim(file_get_contents("/etc/version")),0,3); + +$pgtitle = "Services: HAProxy: Files"; +include("head.inc"); + +?> + + +
+ + + +You must apply the changes in order for them to take effect.");?>
+ + + + +
+ +
+
+ + + + + + + + + + + + + + + + +
+ Files can be used for errorfiles, that can return custom error pages in + case haproxy reports a error (like no available backend). The content needs + to be less than the buffer size which is typically 8kb. + There are 2 possible variables to use inside the template: + Put these variables in the content of the errorfile templates and they will be replaced by the actual errorcode / message. (include the curly braces around the text)
+ {errorcode} this represents the errorcode
+ {errormsg} this represents the human readable error
+
+   +
+ Draw($a_files); + ?> +
+   +
+ +
+
+
+
+ + + + + diff --git a/config/haproxy-devel/haproxy_htmllist.inc b/config/haproxy-devel/haproxy_htmllist.inc index 98f7ab94..f873028e 100644 --- a/config/haproxy-devel/haproxy_htmllist.inc +++ b/config/haproxy-devel/haproxy_htmllist.inc @@ -46,6 +46,7 @@ class HaproxyHtmlList private $fields = array(); public $editmode = false; public $fields_details = null; + public $keyfield = ""; public function HaproxyHtmlList($tablename, $fields){ $this->tablename = $tablename; @@ -64,10 +65,25 @@ class HaproxyHtmlList foreach($this->fields as $item){ $itemname = $item['name']; $value[$itemname] = $_POST[$itemname.$x]; + if ($item['type'] == 'textarea') + $value[$itemname] = base64_encode($value[$itemname]); $add_item |= isset($_POST[$itemname.$x]); } - if ($add_item) - $values[] = $value; + if ($add_item) { + if ($this->keyfield != "") { + if (isset($_POST[$this->tablename."_key".$x])) + $key = $_POST[$this->tablename."_key".$x]; + else + $key = $_POST[$this->keyfield.$x]; + + } else + $key = ""; + + if (isset($values[$key])) + $values[] = $value; + else + $values[$key] = $value; + } } return $values; } @@ -78,12 +94,16 @@ class HaproxyHtmlList if ($editable) { $itemtype = $item['type']; if ($itemtype == "select"){ - //updatevisibility() echo_html_select($itemnamenr, $item['items'], $itemvalue,"","html_listitem_change(\"{$this->tablename}\",\"{$itemname}\",\"{$counter}\",this);", "width:{$item['size']}"); } else if ($itemtype == "checkbox"){ $checked = $itemvalue=='yes' ? " checked" : ""; echo ""; + } else + if ($itemtype == "textarea"){ + echo ""; } else echo ""; } else { @@ -93,7 +113,10 @@ class HaproxyHtmlList if ($itemtype == "checkbox"){ echo $itemvalue=='yes' ? gettext('yes') : gettext('no'); } else - echo $itemvalue; + if ($itemtype == "textarea"){ + echo htmlspecialchars(base64_decode($itemvalue)); + } else + echo htmlspecialchars($itemvalue); } } @@ -108,7 +131,16 @@ class HaproxyHtmlList echo " "; if (is_array($rowvalues)){ - foreach($rowvalues as $value){ + foreach($rowvalues as $keyid => $value){ + if ($this->keyfield != "") { + if (preg_match("/[^0-9]/", $keyid)) + $itemvalue = $keyid; + else + $itemvalue = $value[$this->keyfield]; + $key = ""; + } else + $key = ""; + if (!$editstate) { echo ""; $leftitem = true; @@ -123,6 +155,7 @@ class HaproxyHtmlList $this->haproxy_htmllist_drawcell($item, $itemvalue, false, $itemname, $counter); echo ""; $leftitem = false; + } echo " @@ -144,12 +177,13 @@ class HaproxyHtmlList foreach($items as $item){ $itemname = $item['name']; $itemvalue = $value[$itemname]; - echo ""; + echo "".$key; if (isset($item['customdrawcell'])) { $item['customdrawcell']($item, $itemvalue, true, $item['name'].$counter); } else $this->haproxy_htmllist_drawcell($item, $itemvalue, true, $itemname, $counter); echo ""; + $key = ""; } echo " @@ -288,6 +322,10 @@ function haproxy_htmllist_js(){ td.innerHTML="<\/input> "; + } else if(items[i]['type'] == 'textarea') { + td.innerHTML="