Change swagger host and port serving it statically - json

Inspired by this sample repository, I'm generating a swagger output in json with protoc and serving it. However, for certain reasons I'm hosting the swagger content on a different port(:10000) than my REST api service(:8000).
I'm using the Go library statik to bundle up the swagger assets and serve them. It works, and a webpage is served when going to localhost:10000.
However, every cURL request swagger makes seems to be confined to just that - localhost:10000. The REST API lives on localhost:8081.
Serving swagger-ui with static content, how do I change the host/port for the REST api server?
I've tried going into the index.html of the swagger-ui content to add basePath as here, but with no luck. Every request is still made to :10000
window.onload = function() {
// Begin Swagger UI call region
const ui = SwaggerUIBundle({
url: "./service.swagger.json",
dom_id: '#swagger-ui',
deepLinking: true,
presets: [
SwaggerUIBundle.presets.apis,
SwaggerUIStandalonePreset
],
plugins: [
SwaggerUIBundle.plugins.DownloadUrl
],
layout: "StandaloneLayout",
// I added this, but it did not change anything.
basePath: "localhost:8081"
})
// End Swagger UI call region
window.ui = ui}
</script>

Add host with value localhost:8081
Remove basePath, basePath is used to change the relative path after host i.e if your web server is hosted at /v1/ etc, then you can use basepath to change that
i am still trying to find out how to pass host value dynamically for production, stage, dev env

Related

Serve index file instead of download prompt

I have my website hosted on S3 with CloudFront as a CDN, and I need these two URLs to behave the same and to serve the index.html file within the directory:
example.com/directory
example.com/directory/
The one with the / at the end incorrectly prompts the browser to download a zero byte file with a random hash for the name of the file. Without the slash it returns my 404 page.
How can I get both paths to deliver the index.html file within the directory?
If there's a way I'm "supposed" to do this, great! That's what I'm hoping for, but if not I'll probably try to use Lambda#Edge to do a redirect. I need that for some other situations anyway, so some instructions on how to do a 301 or 302 redirect from Lambda#Edge would be helpful too : )
Update (as per John Hanley's Comment)
curl -i https://www.example.com/directory/
HTTP/2 200
content-type: application/x-directory
content-length: 0
date: Sat, 12 Jan 2019 22:07:47 GMT
last-modified: Wed, 31 Jan 2018 00:44:16 GMT
etag: "[id]"
accept-ranges: bytes
server: AmazonS3
x-cache: Miss from cloudfront
via: 1.1 [id].cloudfront.net (CloudFront)
x-amz-cf-id: [id]
Update
CloudFront has one behavior set, forwarding http to https and sending the requests to S3. It also has a 404 error route under the errors tab.
S3 only offers automatic index documents when you've enabled and are using the web site hosting features of the bucket, by pointing to the bucket's website hosting endpoint, ${bucket}.s3-website.${region}.amazonaws.com rather than the generic REST endpoint of the bucket, ${bucket}.s3.amazonaws.com.
Web site endpoints and REST endpoints have numerous differences, including this one.
The reason you're seeing these 0-byte files for object keys ending in / is because you are creating folder objects in the bucket using the S3 console or another utility that actually creates the 0-byte objects. They aren't needed, once the folders have objects "in" them -- but they're the only way to display an empty folder in the S3 console, which displays an object named foo/ as a folder named foo, even if there are no other objects with a key prefix of foo/. It's part of the visual emulation of a folder hierarchy in the console, even though objects in S3 are never really "in" folders.
If for some reason you need to use the REST endpoint -- such as you don't want to make the bucket public -- then you need two Lambda#Edge triggers in CloudFront, to emulate this functionality fairly closely.
An Origin Request trigger can inspect and modify requests after the CloudFront cache is checked, before the request is sent to the origin. We use this to check for a path ending in / and append index.html if we find that.
An Origin Response trigger can inspect and potentially modify responses, before they are written into the CloudFront cache. The Origin Response trigger can also inspect the original request that preceded the request that generated the response. We use this to check whether the response is an error. If it is, and the original request does not appear to be for an index document or a file (specifically, after the final slash in the path, a "file" has at least one character, followed by a dot, followed by at least one more character -- and if so, that's probably a "file"). If it's neither one of those things, we redirect to the original path plus a final / that we append.
Origin Request and Origin Response triggers fire only on cache misses. When there is a cache hit, neither trigger fires, because they are on the origin side of CloudFront -- the back side of the cache. Requests that can be served from the cache are served from the cache, so the triggers are not invoked.
The following is a Lambda#Edge function written in Node.js 8.10. This one Lambda function modifies its behavior so that it it behaves as either origin request or origin response, depending on context. After publishing a version in Lambda, associate that version's ARN with the CloudFront Cache Behavior settings as both an Origin Request and an Origin Response trigger.
'use strict';
// combination origin-request, origin-response trigger to emulate the S3
// website hosting index document functionality, while using the REST
// endpoint for the bucket
// https://stackoverflow.com/a/54263794/1695906
const INDEX_DOCUMENT = 'index.html'; // do not prepend a slash to this value
const HTTP_REDIRECT_CODE = '302'; // or use 301 or another code if desired
const HTTP_REDIRECT_MESSAGE = 'Found';
exports.handler = (event, context, callback) => {
const cf = event.Records[0].cf;
if(cf.config.eventType === 'origin-request')
{
// if path ends with '/' then append INDEX_DOCUMENT before sending to S3
if(cf.request.uri.endsWith('/'))
{
cf.request.uri = cf.request.uri + INDEX_DOCUMENT;
}
// return control to CloudFront, to send request to S3, whether or not
// we modified it; if we did, the modified URI will be requested.
return callback(null, cf.request);
}
else if(cf.config.eventType === 'origin-response')
{
// is the response 403 or 404? If not, we will return it unchanged.
if(cf.response.status.match(/^40[34]$/))
{
// it's an error.
// we're handling a response, but Lambda#Edge can still see the attributes of the request that generated this response; so, we
// check whether this is a page that should be redirected with a trailing slash appended. If it doesn't look like an index
// document request, already, and it doesn't end in a slash, and doesn't look like a filename with an extension... we'll try that.
// This is essentially what the S3 web site endpoint does if you hit a nonexistent key, so that the browser requests
// the index with the correct relative path, except that S3 checks whether it will actually work. We are using heuristics,
// rather than checking the bucket, but checking is an alternative.
if(!cf.request.uri.endsWith('/' + INDEX_DOCUMENT) && // not a failed request for an index document
!cf.request.uri.endsWith('/') && // unlikely, unless this code is modified to pass other things through on the request side
!cf.request.uri.match(/[^\/]+\.[^\/]+$/)) // doesn't look like a filename with an extension
{
// add the original error to the response headers, for reference/troubleshooting
cf.response.headers['x-redirect-reason'] = [{ key: 'X-Redirect-Reason', value: cf.response.status + ' ' + cf.response.statusDescription }];
// set the redirect code
cf.response.status = HTTP_REDIRECT_CODE;
cf.response.statusDescription = HTTP_REDIRECT_MESSAGE;
// set the Location header with the modified URI
// just append the '/', not the "index.html" -- the next request will trigger
// this function again, and it will be added without appearing in the
// browser's address bar.
cf.response.headers['location'] = [{ key: 'Location', value: cf.request.uri + '/' }];
// not strictly necessary, since browsers don't display it, but remove the response body with the S3 error XML in it
cf.response.body = '';
}
}
// return control to CloudFront, with either the original response, or
// the modified response, if we modified it.
return callback(null, cf.response);
}
else // this is not intended as a viewer-side trigger. Throw an exception, visible only in the Lambda CloudWatch logs and a 502 to the browser.
{
return callback(`Lambda function is incorrectly configured; triggered on '${cf.config.eventType}' but expected 'origin-request' or 'origin-response'`);
}
};
The answers given are wrong. Cloudfront has its own configuration to have www.yourdomain.com/ serve up a document. It's called "default root object" and its config is found under the "general" tab of your cloudfront distribution. Here are the full steps for getting an SSL/https-enabled custom domain + cloudfront + s3 bucket.
Create a brand new S3 bucket with default (closed-off) permissions or remove all public access from the target bucket.
Disable static website hosting. You don't need it.
If you haven't already, get your SSL cert into Amazon so you can attach it to the cloudfront distribution which will be pointing to your S3 bucket.
Create a cloudfront distribution pointing to the target S3 bucket, utilizing the cert.
For the origin configuration, use the www.yourdomain.com.s3.amazonaws.com form for the origin, NOT the static website hosting URL (which should be disabled anyway).
Let the cloudfront config automatically change the S3 bucket access ("restrict bucket access"). You want access to the bucket restricted to this cloudfront distribution ONLY (via a specific identity). No one should be hitting your S3 bucket directly, especially since it can serve via http (no "s").
Under the cloudfront "general" tab (or during setup) set your default root object to "index.html" or whatever. Otherwise, requests to https://www.yourdomain.com/ will show permission denied.
Recently AWS has recently launched CloudFront Functions which can be used for this use case.
CloudFront Functions are cheaper, faster and easier to implement and test compared to Lambda#Edge.
Below is a sample function to attach index.html to the request if it is not provided while accessing the path.
function handler(event) {
var request = event.request;
var uri = request.uri;
// Check whether the URI is missing a file name.
if (uri.endsWith('/')) {
request.uri += 'index.html';
}
// Check whether the URI is missing a file extension.
else if (!uri.includes('.')) {
request.uri += '/index.html';
}
return request;
}
This will not append index.html in the web browser address bar, which gives a cleaner URL while browsing. In your case https://www.example.com/directory/ will remain as such while browsing, but will render the content of https://www.example.com/directory/index.html.
More samples can be found in https://github.com/aws-samples/amazon-cloudfront-functions/blob/main/url-rewrite-single-page-apps/index.js
This type of behavior is usually controlled/caused by your HTTP(s) header data, specifically, the Content-Type that your client receives.
Inspect the header and try tweaking what gets returned from your server. That should lead to your solution.
In Chrome, visit a URL, right click, select Inspect to open the developer tools.
Select Network tab.
Reload the page, select any HTTP request on the left panel, and the HTTP headers will be displayed on the right panel.

Fatfree routing with PHP built-in web server

I'm learning fatfree's route and found it behaves unexpected.
Here is my code in index.php:
$f3 = require_once(dirname(dirname(__FILE__)). '/lib/base.php');
$f3 = \Base::instance();
echo 'received uri: '.$_SERVER['REQUEST_URI'].'<br>';
$f3->route('GET /brew/#count',
function($f3,$params) {
echo $params['count'].' bottles of beer on the wall.';
}
);
$f3->run();
and here is the URL which I access: http://xx.xx.xx.xx:8090/brew/12
I get a 404 error:
received uri: /brew/12
Not Found
HTTP 404 (GET /12)
the strange thing is that the URI in F3 is now "/12" instead of "/brew/12" and I guess this is the issue.
When I check the base.php (3.6.5), $this->hive['BASE'] = "/brew" and $this->hive['PATH'] = "/12".
But if F3 only uses $this->hive['PATH'] to match the predefined route, it won't be able to match them.
If I change the route to:
$f3->route('GET /brew',
and use the URL: http://xx.xx.xx.xx:8090/brew, then the route matches without issue.
In this case, $this->hive['BASE'] = "" and $this->hive['PATH'] = "/brew". If F3 compares the $this->hive['PATH'] with predefined route, they match each other.
BTW, I'm using PHP's built-in web server and since $_SERVER['REQUEST_URI'] (which is used by base.php) returns the correct URI, I don't think there is anything wrong with the URL rewrite in my .htrouter.php.
Any idea? What did I miss here?
add the content of .htrouter.php here
<?php
#get the relative URL
$uri = urldecode(parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH));
#if request to a real file (such as a html, image, js, css) then leave it as it is
if ($uri !== '/' && file_exists(__DIR__ . $uri)) {
return false;
}
#if request virtual URL then pass it to the bootstrap file - index.php
$_GET['_url'] = $_SERVER['REQUEST_URI'];
require_once __DIR__ . './public/index.php';
Your issue is directly related to the way you're using the PHP built-in web server.
As stated in the PHP docs, here's how the server handles requests:
URI requests are served from the current working directory where PHP was started, unless the -t option is used to specify an explicit document root. If a URI request does not specify a file, then either index.php or index.html in the given directory are returned. If neither file exists, the lookup for index.php and index.html will be continued in the parent directory and so on until one is found or the document root has been reached. If an index.php or index.html is found, it is returned and $_SERVER['PATH_INFO'] is set to the trailing part of the URI. Otherwise a 404 response code is returned.
If a PHP file is given on the command line when the web server is started it is treated as a "router" script. The script is run at the start of each HTTP request. If this script returns FALSE, then the requested resource is returned as-is. Otherwise the script's output is returned to the browser.
That means that, by default (without a router script), the web server is doing a pretty good job for routing unexisting URIs to your document root index.php file.
In other words, provided your file structure is like:
lib/
base.php
template.php
etc.
public/
index.php
The following command is enough to start your server and dispatch the requests properly to the framework:
php -S 0.0.0.0:8090 -t public/
Or if you're running the command directly from the public/ folder:
cd public
php -S 0.0.0.0:8090
Beware that the working directory of your application depends on the folder from which you call the command. In order to leverage this value, I strongly advise you to add chdir(__DIR__); at the top of your public/index.php file. This way, all subsequent require calls will be relative to your public/ folder. For ex: $f3 = require('../lib/base.php');
Routing file-style URIs
The built-in server, by default, won't pass unexisting file URIs to your index.php, as stated in:
If a URI request does not specify a file, then either index.php or index.html in the given directory are returned
So if you plan to define some routes with dots, such as:
$f3->route('GET /brew.json','Brew->json');
$f3->route('GET /brew.html','Brew->html');
Then it won't work because PHP won't pass the request to index.php.
In that case, you need to call a custom router, such as the .htrouter.php you were trying to use. The only thing is that your .htrouter.php has obviously been designed for a different framework (F3 doesn't care about $_GET['url'] but cares about $_SERVER['SCRIPT_NAME'].
Here's an exemple of .htrouter.php that should work with F3:
// public directory definition
$public_dir=__DIR__.'/public';
// serve existing files as-is
if (file_exists($public_dir.$_SERVER['REQUEST_URI']))
return FALSE;
// patch SCRIPT_NAME and pass the request to index.php
$_SERVER['SCRIPT_NAME']='index.php';
require($public_dir.'/index.php');
NB: the $public_dir variable should be set accordingly to the location of the .htrouter.php file.
For example if you call:
php -S 0.0.0.0:8090 -t public/ .htrouter.php
it should be $public_dir=__DIR__.'/public'.
But if you call:
cd public
php -S 0.0.0.0:8090 .htrouter.php
it should be $public_dir=__DIR__.
OK, I checked the base.php and found out when f3 calculates the base URI, it uses $_SERVER['SCRIPT_NAME'].
$base='';
if (!$cli)
$base=rtrim($this->fixslashes(
dirname($_SERVER['SCRIPT_NAME'])),'/');
if we have web server directly forward all requests to index.php, then
_SERVER['SCRIPT_NAME'] = /index.php, and in this this case, base is ''.
if we use URL rewriting via .htrouter.php to index.php, then
_SERVER['SCRIPT_NAME'] = /brew/12, and in this this case, base is '/brew' which causes the issue.
Since I'm going to use the URL rewrite, I have to comment out the if statement and make sure base =''.
Thanks xfra35 for providing the clue.
Apache like php router here:
It can url rewrite.
https://github.com/kyesil/QPHP/blob/master/router.php
Usage:
php -S localhost:8081 router.php

Polymer - url rooting after deployment to subdirectory

Ive created a basic Polymer app from the starter kit (via Yeoman). I've today deployed it to the 'sandbox' on my domain and am getting a strange routing issue. The app is essentially a feed reader.
View app here
When I first visit the app I'm given a blank page whereas locally I'm taken straight to the feed. When clicking on 'News Feed' I'm then taken to the feed as expected.
I've added a route for the path of the domain structure as below but this did not fix it.
You can view the full code of the project here.
routing.html
page('/', function () {
app.route = 'home';
});
page('http://purelywebdesign.co.uk/sandbox/f1feedreader/', function () {
app.route = 'home';
});
I've also tried:
page('/sandbox/f1feedreader/', function () {
app.route = 'home';
});
Any help much appreciated.
Page.js allows you to configure the base path:
page.base('/sandbox/f1feedreader/');
or just use window.location if you don't want to tie is to that specific deployment.
page.base(window.location.pathname);
This is an issue with the way the router page.js works. I assume you were testing with gulp serve (which creates a server and sets the web app base url of "/" to be localhost:3000/). The way you're currently setting your page.js routes is that it's looking exactly after the domain name and not at the "root" of the web directory.
In your case page.js is looking at everything after http://purelywebdesign.co.uk/ (meaning all your routes include should start from sandbox/f1feedreader instead of just /f1feedreader).
The documentation for page.js https://visionmedia.github.io/page.js/ says that it uses regular expressions so you could also update the strings.

Dojo 1.9 JsonRest remote url on a different server

I'm trying to get dojo to show Json data that comes from a remote web service. I need to be clear though - the web server hosting the html/dojo page I access isn't the same server as the one that's running the web service that returns the json data - the web service server just can't serve html pages reliably (don't ask!!).
As a test I move the page into the same web server as the web service and the below works. As soon as I move it back so that the html/dojo is served from Apache (//myhost.nodomain:82 say) and the web service sending the json is "{target:http://myhost.nodomain:8181}", then it stops working.
I've used FFox to look at the network & I see the web service being called ok, the json data is returned too & looks correct (I know it is from the previous test), but the fields are no longer set. I've tried this with DataGrid and the plain page below with the same effects.
Am I tripping up over something obvious???
Thanks
require([
"dojo/store/JsonRest",
"dojo/store/Memory",
"dojo/store/Cache",
"dojox/grid/DataGrid",
"dojo/data/ObjectStore",
"dojo/query",
"dojo/domReady!"
],
function(JsonRest, Memory, Cache, DataGrid, ObjectStore, query) {
var myStore, dataStore, grid;
myStore = JsonRest(
{
target: "http://localhost:8181/ws/job/definition/",
idProperty: "JOB_NAME"
}
);
myStore.query("JOB00001"
).then(function(results) {
var theJobDef = results[0];
dojo.byId("JOB_NAME").innerHTML = theJobDef.JOB_NAME;
dojo.byId("SCHEDULED_DAYS").innerHTML = theJobDef.SCHEDULED_DAYS;
});
}
);
Its true what Frans said about the cross domain restriction but dojo has this link to work around the problem.
require(["dojo/request/iframe"], function(iframe){
iframe("something.xml", {
handleAs: "json"
}).then(function(xmldoc){
// Do something with the XML document
}, function(err){
// Handle the error condition
});
// Progress events are not supported using the iframe provider
});
you can simply use this and the returned data can be inserted into a store and then into the grid.
Are you familiar with the Same Origin Policy:
http://en.wikipedia.org/wiki/Same-origin_policy
Basically it restricts websites to do AJAX requests to other domains than the html page was loaded from. Common solutions to overcome this are CORS and JSON-P. However, remember that these restrictions are made for security reasons.

Loading an asset stored within a chrome extension

Let's say I have a JSON file stored within my extension called settings.json. I can get the URL of the file using:
chrome.extension.getURL("settings.json");
But now that I have the URL, how do I actually load the contents of that file so I can JSON.parse it and use it? The reason I'm doing this is that there is a server component, and I want to make deployment and testing on multiple servers easier (dev, staging, production, etc.) Alternatively if there's a way to add custom attributes to the manifest.json and access them, that would also work.
If you make your setting.js look like:
var settings = {"param":value,...};
Then you can just include it on a background page and use settings variable:
<script src="settings.js"></script>
If you want to have pure json in your file without assigning it to any variables then you can load it using XMLHttpRequest:
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = handleStateChange; // Implemented elsewhere.
xhr.open("GET", chrome.extension.getURL('/config_resources/config.json'), true);
xhr.send();
or if you included jquery into your project:
$.getJSON(chrome.extension.getURL('/config_resources/config.json'), function(settings) {
//..
});
(btw using chrome.extension.getURL is required only if you are accessing a file from a content script, otherwise you can just use relative path /config_resources/config.json)
I can verify that requesting the resource from an XHR in the background page works as previously described. Just be sure to add 'self' to the connect-src portion of your content_security_policy.