URL shortener: Difference between revisions

m
(→‎{{header|Perl 6}}: Add a Perl 6 example)
m (→‎{{header|Wren}}: Minor tidy)
 
(23 intermediate revisions by 11 users not shown)
Line 41:
 
=={{header|Crystal}}==
{{libheader| kemal}}
 
<langsyntaxhighlight lang="ruby">require "kemal"
 
CHARS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ".chars
Line 65 ⟶ 66:
end
 
Kemal.run</langsyntaxhighlight>
 
=={{header|Perl 6Delphi}}==
{{libheader| System.SysUtils}}
{{libheader| System.Classes}}
{{libheader| System.Json}}
{{libheader| IdHTTPServer}}
{{libheader| IdContext}}
{{libheader| IdCustomHTTPServer}}
{{libheader| IdGlobal}}
{{libheader| Inifiles}}
Highly inspired in [[#Go]]
<syntaxhighlight lang="delphi">
program URLShortenerServer;
 
{$APPTYPE CONSOLE}
 
uses
System.SysUtils,
System.Classes,
System.Json,
IdHTTPServer,
IdContext,
IdCustomHTTPServer,
IdGlobal,
Inifiles;
 
type
TUrlShortener = class
private
Db: TInifile;
Server: TIdHTTPServer;
function GenerateKey(size: integer): string;
procedure Get(Path: string; AResponseInfo: TIdHTTPResponseInfo);
procedure Post(Url: string; AReqBody: TJSONValue; AResponseInfo: TIdHTTPResponseInfo);
function PostBody(Data: TStream): TJSONValue;
function StoreLongUrl(Url: string): string;
public
procedure DoGet(AContext: TIdContext; ARequestInfo: TIdHTTPRequestInfo;
AResponseInfo: TIdHTTPResponseInfo);
procedure StartListening;
constructor Create;
destructor Destroy; override;
end;
 
const
Host = 'localhost:8080';
CODE_CHARS = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
SHORTENER_SEASON = 'URL_SHORTER';
 
{ Manager }
 
function TUrlShortener.GenerateKey(size: integer): string;
var
le, i: integer;
begin
SetLength(Result, size);
le := Length(CODE_CHARS)-1;
for i := 1 to size do
Result[i] := CODE_CHARS[Random(le)+1];
end;
 
procedure TUrlShortener.StartListening;
begin
Server.Active := true;
end;
 
function TUrlShortener.StoreLongUrl(Url: string): string;
begin
repeat
Result := GenerateKey(8);
until not Db.ValueExists(SHORTENER_SEASON, Result);
Db.WriteString(SHORTENER_SEASON, Result, Url);
Db.UpdateFile;
end;
 
procedure TUrlShortener.Get(Path: string; AResponseInfo: TIdHTTPResponseInfo);
var
longUrl: string;
begin
if Db.ValueExists(SHORTENER_SEASON, Path) then
begin
longUrl := Db.ReadString(SHORTENER_SEASON, Path, '');
AResponseInfo.ResponseNo := 302;
AResponseInfo.Redirect(longUrl);
end
else
begin
AResponseInfo.ResponseNo := 404;
writeln(format('No such shortened url: http://%s/%s', [host, Path]));
end;
end;
 
procedure TUrlShortener.Post(Url: string; AReqBody: TJSONValue; AResponseInfo:
TIdHTTPResponseInfo);
var
longUrl, shortUrl: string;
begin
if Assigned(AReqBody) then
begin
longUrl := AReqBody.GetValue<string>('long');
shortUrl := StoreLongUrl(longUrl);
AResponseInfo.ResponseNo := 200;
AResponseInfo.ContentText := Host + '/' + shortUrl;
end
else
AResponseInfo.ResponseNo := 422;
end;
 
function TUrlShortener.PostBody(Data: TStream): TJSONValue;
var
body: string;
begin
Result := nil;
if assigned(Data) then
begin
Data.Position := 0;
body := ReadStringFromStream(Data);
 
result := TJSONObject.Create;
try
result := TJSONObject.ParseJSONValue(body);
except
on E: Exception do
FreeAndNil(Result);
end;
end;
end;
 
constructor TUrlShortener.Create;
begin
Db := TInifile.Create(ChangeFileExt(ParamStr(0), '.db'));
Server := TIdHTTPServer.Create(nil);
Server.DefaultPort := 8080;
Server.OnCommandGet := DoGet;
end;
 
destructor TUrlShortener.Destroy;
begin
Server.Active := false;
Server.Free;
Db.Free;
inherited;
end;
 
procedure TUrlShortener.DoGet(AContext: TIdContext; ARequestInfo:
TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
var
Path: string;
begin
// Default ResponseNo
AResponseInfo.ResponseNo := 404;
 
Path := ARequestInfo.URI.Replace('/', '', []);
 
case ARequestInfo.CommandType of
hcGET:
Get(Path, AResponseInfo);
hcPOST:
Post(Path, PostBody(ARequestInfo.PostStream), AResponseInfo);
else
Writeln('Unsupprted method: ', ARequestInfo.Command);
end;
end;
 
var
Server: TIdHTTPServer;
Manager: TUrlShortener;
 
begin
Manager := TUrlShortener.Create;
Manager.StartListening;
 
Writeln('Running on ', host);
Writeln('Press ENTER to exit');
readln;
 
Manager.Free;
end.</syntaxhighlight>
{{out}}
<pre>
Running on localhost:8080
Press ENTER to exit
</pre>
=={{header|Go}}==
<syntaxhighlight lang="go">// shortener.go
package main
 
import (
"encoding/json"
"fmt"
"io/ioutil"
"log"
"math/rand"
"net/http"
"time"
)
 
const (
chars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
host = "localhost:8000"
)
 
type database map[string]string
 
type shortener struct {
Long string `json:"long"`
}
 
func (db database) ServeHTTP(w http.ResponseWriter, req *http.Request) {
switch req.Method {
case http.MethodPost: // "POST"
body, err := ioutil.ReadAll(req.Body)
if err != nil {
w.WriteHeader(http.StatusBadRequest) // 400
return
}
var sh shortener
err = json.Unmarshal(body, &sh)
if err != nil {
w.WriteHeader(http.StatusUnprocessableEntity) // 422
return
}
short := generateKey(8)
db[short] = sh.Long
fmt.Fprintf(w, "The shortened URL: http://%s/%s\n", host, short)
case http.MethodGet: // "GET"
path := req.URL.Path[1:]
if v, ok := db[path]; ok {
http.Redirect(w, req, v, http.StatusFound) // 302
} else {
w.WriteHeader(http.StatusNotFound) // 404
fmt.Fprintf(w, "No such shortened url: http://%s/%s\n", host, path)
}
default:
w.WriteHeader(http.StatusNotFound) // 404
fmt.Fprintf(w, "Unsupprted method: %s\n", req.Method)
}
}
 
func generateKey(size int) string {
key := make([]byte, size)
le := len(chars)
for i := 0; i < size; i++ {
key[i] = chars[rand.Intn(le)]
}
return string(key)
}
 
func main() {
rand.Seed(time.Now().UnixNano())
db := make(database)
log.Fatal(http.ListenAndServe(host, db))
}</syntaxhighlight>
 
{{out}}
Sample output (abbreviated) including building and starting the server from Ubuntu 18.04 terminal and entering a valid and then an invalid shortened URL:
<pre>
$ go build shortener.go
 
$ ./shortener &
 
$ curl -X POST 'localhost:8000' \
> -H 'Content-Type: application/json' \
> -d '{
> "long": "https://www.cockroachlabs.com/docs/stable/build-a-go-app-with-cockroachdb.html"
> }'
The shortened URL: http://localhost:8000/3DOPwhRu
 
$ curl -L http://localhost:8000/3DOPwhRu
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
 
<meta name="description" content="Learn how to use CockroachDB from a simple Go application with the Go pq driver.">
 
....
 
</html>
 
$ curl -L http://localhost:8000/3DOPwhRv
No such shortened url: http://localhost:8000/3DOPwhRv
</pre>
 
=={{header|JavaScript}}==
{{works with|Node.js}}
<syntaxhighlight lang="javascript">#!/usr/bin/env node
 
var mapping = new Map();
 
// assure num. above 36 ^ 9
var base = 101559956668416;
// 36 ^ (10 -> length of ID)
var ceil = 3656158440062976;
// these are calculated as:
// l = desired length
// 198 > l > 1
// -> above 198 ends up as Infinity
// -> below 1 ends up as 0, as one would except (pun intended)
// base = 36 ^ (l - 1)
// ceil = 36 ^ l
 
require('http').createServer((req, res) => {
if(req.url === '/') {
// only accept POST requests as JSON to /
if(req.method !== 'POST' || req.headers['content-type'] !== 'application/json') {
// 400 Bad Request
res.writeHead(400);
return res.end();
}
 
var random = (Math.random() * (ceil - base) + base).toString(36);
req.on('data', chunk => {
// trusting input json to be valid, e.g., '{"long":"https://www.example.com/"}'
var body = JSON.parse(chunk.toString());
mapping.set(random.substring(0, 10), body.long); // substr gets the integer part
});
 
// 201 Created
res.writeHead(201);
return res.end('http://localhost:8080/' + random.substring(0, 10));
}
 
var url = mapping.get(req.url.substring(1));
if(url) {
// 302 Found
res.writeHead(302, { 'Location': url });
return res.end();
}
 
// 404 Not Found
res.writeHead(404);
res.end();
}).listen(8080);</syntaxhighlight>
{{out}}
<pre>$ curl -X POST http://localhost:8080/ -H "Content-Type: application/json" --data "{\"long\":\"https://www.example.com\"}"
http://localhost:8080/bcg4x4lla8
$ </pre>
http://localhost:8080/bcg4x4lla8 then redirects to www.example.com
 
=={{header|Julia}}==
Assumes an SQLite database containing a table called LONGNAMESHORTNAME (consisting of two string columns) already exists.
<syntaxhighlight lang="julia">using Base64, HTTP, JSON2, Sockets, SQLite, SHA
 
function processpost(req::HTTP.Request, urilen=8)
json = JSON2.read(String(HTTP.payload(req)))
if haskey(json, :long)
longname = json.long
encoded, shortname = [UInt8(c) for c in base64encode(sha256(longname))], ""
for i in 0:length(encoded)-1
shortname = String(circshift(encoded, i)[1:urilen])
result = SQLite.Query(dbhandle,
"SELECT LONG FROM LONGNAMESHORTNAME WHERE SHORT = \"" * shortname * "\";")
if isempty(result)
SQLite.Query(dbhandle,
"INSERT INTO LONGNAMESHORTNAME VALUES ('" *
longname * "', '" * shortname * "')")
return HTTP.Response(200, JSON2.write(
"$shortname is short name for $longname."))
end
end
end
HTTP.Response(400, JSON2.write("Bad request. Please POST JSON as { long : longname }"))
end
 
function processget(req::HTTP.Request)
shortname = split(req.target, r"[^\w\d\+\\]+")[end]
result = SQLite.Query(dbhandle, "SELECT LONG FROM LONGNAMESHORTNAME WHERE SHORT = \'" *
shortname * "\' ;")
responsebody = isempty(result) ?
"<!DOCTYPE html><html><head></head><body><h2>Not Found</h2></body></html>" :
"<!DOCTYPE html><html><head></head><body>\n<meta http-equiv=\"refresh\"" *
"content = \"0; url = " * first(result).LONG * " /></body></html>"
return HTTP.Response(200, responsebody)
end
 
function run_web_server(server, portnum)
router = HTTP.Router()
HTTP.@register(router, "POST", "", processpost)
HTTP.@register(router, "GET", "/*", processget)
HTTP.serve(router, server, portnum)
end
 
const dbhandle = SQLite.DB("longshort.db")
const serveraddress = Sockets.localhost
const localport = 3000
run_web_server(serveraddress, localport)
</syntaxhighlight>
 
=={{header|Objeck}}==
<syntaxhighlight lang="objeck">use Collection.Generic;
use Data.JSON;
use Web.HTTP;
 
class Shortner from HttpRequestHandler {
@url_mapping : static : Hash<String, String>;
 
function : Init() ~ Nil {
@url_mapping := Hash->New()<String, String>;
}
 
New() {
Parent();
}
 
function : Main(args : String[]) ~ Nil {
if(args->Size() = 1) {
Shortner->Init();
WebServer->Serve(Shortner->New()->GetClass(), args[0]->ToInt(), true);
};
}
 
method : ProcessGet(request_url : String, request_headers : Map<String, String>, response_headers : Map<String, String>) ~ Response {
long_url := @url_mapping->Find(request_url);
if(long_url <> Nil) {
response := Response->New(302);
response->SetReason(long_url);
 
return response;
};
return Response->New(404);
}
method : ProcessPost(buffer : Byte[], request_url : String, request_headers : Map<String, String>, response_headers : Map<String, String>) ~ Response {
response : Byte[];
json := JsonParser->New(String->New(buffer));
if(json->Parse()) {
url_json := json->GetRoot()->Get("long");
long_url := url_json->GetValue();
post_fix := "/";
each(i : 6) {
if(Int->Random(1) % 2 = 0) {
post_fix += Int->Random('a', 'z')->As(Char);
}
else {
post_fix += Int->Random('A', 'Z')->As(Char);
};
};
 
short_url := "http://localhost:60013{$post_fix}";
@url_mapping->Insert(post_fix, long_url);
 
response_headers->Insert("Content-type", "application/json");
response := "{ \"short\": \"{$short_url}\" }"->ToByteArray();
};
 
return Response->New(200, response);
}
}</syntaxhighlight>
 
=={{header|Phix}}==
<!--<syntaxhighlight lang="phix">(notonline)-->
<span style="color: #000080;font-style:italic;">--
-- demo\rosetta\URL_shortener.exw
-- ==============================
--
-- Uses a routine originally written for a code minifier, so were you to run this
-- for a long time, you'd get 52 one-character short urls, ie a..z and A..Z, then
-- 3,224 (=52*62) two-character short urls, as 2nd char on can also be 0..9, then
-- 199,888 (=52*62*62) three-character short urls, and so on. The dictionary used
-- is not [yet] saved/reloaded between runs. No attempt is made to re-produce the
-- same short url if the same long url is passed in twice. Nothing remotely like
-- any form of error handling, as per the usual "for clarity".
--
-- Windows only for now (should be straightforward to get it working on linux)
-- (routines in builtins\sockets.e that be windows-only.)
--
-- See sample session output (in a separate terminal) for usage instructions.
-- </span>
<span style="color: #008080;">without</span> <span style="color: #008080;">js</span>
<span style="color: #008080;">include</span> <span style="color: #000000;">builtins</span><span style="color: #0000FF;">\</span><span style="color: #000000;">sockets</span><span style="color: #0000FF;">.</span><span style="color: #000000;">e</span>
<span style="color: #008080;">include</span> <span style="color: #000000;">builtins</span><span style="color: #0000FF;">\</span><span style="color: #000000;">json</span><span style="color: #0000FF;">.</span><span style="color: #000000;">e</span>
<span style="color: #008080;">constant</span> <span style="color: #000000;">MAX_QUEUE</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">100</span><span style="color: #0000FF;">,</span>
<span style="color: #000000;">ESCAPE</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">#1B</span><span style="color: #0000FF;">,</span>
<span style="color: #000000;">shortened</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">substitute</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"""
HTTP/1.1 200 OK
Content-Type: text/plain; charset=utf-8
Content-Length: %d
The shortened URL: http://localhost:8080/%s
"""</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"\n"</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"\r\n"</span><span style="color: #0000FF;">),</span>
<span style="color: #000000;">redirect</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">substitute</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"""
HTTP/1.1 302 Found
Content-Type: text/html; charset=UTF-8
Content-Length: %d
Location: %s
&lt;a href="%s"&gt;Found&lt;/a&gt;.
"""</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"\n"</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"\r\n"</span><span style="color: #0000FF;">),</span>
<span style="color: #000000;">not_found</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">substitute</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"""
HTTP/1.1 404 Not Found
Content-Type: text/plain; charset=utf-8
Content-Length: %d
No such shortened url: http://localhost:8080/%s
"""</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"\n"</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"\r\n"</span><span style="color: #0000FF;">)</span>
<span style="color: #004080;">integer</span> <span style="color: #000000;">shurl</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">new_dict</span><span style="color: #0000FF;">()</span>
<span style="color: #004080;">string</span> <span style="color: #000000;">response</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">lnk</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">url</span>
<span style="color: #008080;">constant</span> <span style="color: #000000;">alphabet</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">tagset</span><span style="color: #0000FF;">(</span><span style="color: #008000;">'z'</span><span style="color: #0000FF;">,</span><span style="color: #008000;">'a'</span><span style="color: #0000FF;">)&</span><span style="color: #7060A8;">tagset</span><span style="color: #0000FF;">(</span><span style="color: #008000;">'Z'</span><span style="color: #0000FF;">,</span><span style="color: #008000;">'A'</span><span style="color: #0000FF;">)&</span><span style="color: #7060A8;">tagset</span><span style="color: #0000FF;">(</span><span style="color: #008000;">'9'</span><span style="color: #0000FF;">,</span><span style="color: #008000;">'0'</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">function</span> <span style="color: #000000;">short_id</span><span style="color: #0000FF;">(</span><span style="color: #004080;">integer</span> <span style="color: #000000;">n</span><span style="color: #0000FF;">)</span>
<span style="color: #004080;">string</span> <span style="color: #000000;">res</span> <span style="color: #0000FF;">=</span> <span style="color: #008000;">""</span>
<span style="color: #004080;">integer</span> <span style="color: #000000;">base</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">52</span> <span style="color: #000080;font-style:italic;">-- (first char azAZ)</span>
<span style="color: #008080;">while</span> <span style="color: #000000;">n</span> <span style="color: #008080;">do</span>
<span style="color: #000000;">res</span> <span style="color: #0000FF;">&=</span> <span style="color: #000000;">alphabet</span><span style="color: #0000FF;">[</span><span style="color: #7060A8;">remainder</span><span style="color: #0000FF;">(</span><span style="color: #000000;">n</span><span style="color: #0000FF;">-</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #000000;">base</span><span style="color: #0000FF;">)+</span><span style="color: #000000;">1</span><span style="color: #0000FF;">]</span>
<span style="color: #000000;">n</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">floor</span><span style="color: #0000FF;">((</span><span style="color: #000000;">n</span><span style="color: #0000FF;">-</span><span style="color: #000000;">1</span><span style="color: #0000FF;">)/</span><span style="color: #000000;">base</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">base</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">62</span> <span style="color: #000080;font-style:italic;">-- (subsequent chars azAZ09)</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">while</span>
<span style="color: #008080;">return</span> <span style="color: #000000;">res</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">function</span>
<span style="color: #7060A8;">puts</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"server started, open http://localhost:8080/ in browser or curl, press Esc or Q to quit\n"</span><span style="color: #0000FF;">)</span>
<span style="color: #004080;">atom</span> <span style="color: #000000;">sock</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">socket</span><span style="color: #0000FF;">(</span><span style="color: #000000;">AF_INET</span><span style="color: #0000FF;">,</span><span style="color: #000000;">SOCK_STREAM</span><span style="color: #0000FF;">,</span><span style="color: #004600;">NULL</span><span style="color: #0000FF;">),</span>
<span style="color: #000000;">pSockAddr</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">sockaddr_in</span><span style="color: #0000FF;">(</span><span style="color: #000000;">AF_INET</span><span style="color: #0000FF;">,</span> <span style="color: #008000;">""</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">8080</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">if</span> <span style="color: #7060A8;">bind</span><span style="color: #0000FF;">(</span><span style="color: #000000;">sock</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">pSockAddr</span><span style="color: #0000FF;">)=</span><span style="color: #000000;">SOCKET_ERROR</span> <span style="color: #008080;">then</span> <span style="color: #7060A8;">crash</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"bind (%v)"</span><span style="color: #0000FF;">,{</span><span style="color: #7060A8;">get_socket_error</span><span style="color: #0000FF;">()})</span> <span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #008080;">if</span> <span style="color: #7060A8;">listen</span><span style="color: #0000FF;">(</span><span style="color: #000000;">sock</span><span style="color: #0000FF;">,</span><span style="color: #000000;">MAX_QUEUE</span><span style="color: #0000FF;">)=</span><span style="color: #000000;">SOCKET_ERROR</span> <span style="color: #008080;">then</span> <span style="color: #7060A8;">crash</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"listen (%v)"</span><span style="color: #0000FF;">,{</span><span style="color: #7060A8;">get_socket_error</span><span style="color: #0000FF;">()})</span> <span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #008080;">while</span> <span style="color: #008080;">not</span> <span style="color: #7060A8;">find</span><span style="color: #0000FF;">(</span><span style="color: #7060A8;">get_key</span><span style="color: #0000FF;">(),{</span><span style="color: #000000;">ESCAPE</span><span style="color: #0000FF;">,</span><span style="color: #008000;">'q'</span><span style="color: #0000FF;">,</span><span style="color: #008000;">'Q'</span><span style="color: #0000FF;">})</span> <span style="color: #008080;">do</span>
<span style="color: #0000FF;">{</span><span style="color: #004080;">integer</span> <span style="color: #000000;">code</span><span style="color: #0000FF;">}</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">select</span><span style="color: #0000FF;">({</span><span style="color: #000000;">sock</span><span style="color: #0000FF;">},{},{},</span><span style="color: #000000;">250000</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">code</span><span style="color: #0000FF;">=</span><span style="color: #000000;">SOCKET_ERROR</span> <span style="color: #008080;">then</span> <span style="color: #7060A8;">crash</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"select (%v)"</span><span style="color: #0000FF;">,{</span><span style="color: #7060A8;">get_socket_error</span><span style="color: #0000FF;">()})</span> <span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">code</span><span style="color: #0000FF;">></span><span style="color: #000000;">0</span> <span style="color: #008080;">then</span> <span style="color: #000080;font-style:italic;">-- (not timeout)</span>
<span style="color: #004080;">atom</span> <span style="color: #000000;">peer</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">accept</span><span style="color: #0000FF;">(</span><span style="color: #000000;">sock</span><span style="color: #0000FF;">),</span>
<span style="color: #000000;">ip</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">getsockaddr</span><span style="color: #0000FF;">(</span><span style="color: #000000;">peer</span><span style="color: #0000FF;">)</span>
<span style="color: #0000FF;">{</span><span style="color: #004080;">integer</span> <span style="color: #000000;">len</span><span style="color: #0000FF;">,</span> <span style="color: #004080;">string</span> <span style="color: #000000;">request</span><span style="color: #0000FF;">}</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">recv</span><span style="color: #0000FF;">(</span><span style="color: #000000;">peer</span><span style="color: #0000FF;">)</span>
<span style="color: #7060A8;">printf</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"Client IP: %s\n%s\n"</span><span style="color: #0000FF;">,{</span><span style="color: #7060A8;">ip_to_string</span><span style="color: #0000FF;">(</span><span style="color: #000000;">ip</span><span style="color: #0000FF;">),</span><span style="color: #000000;">request</span><span style="color: #0000FF;">})</span>
<span style="color: #008080;">if</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">request</span><span style="color: #0000FF;">)></span><span style="color: #000000;">5</span> <span style="color: #008080;">and</span> <span style="color: #000000;">request</span><span style="color: #0000FF;">[</span><span style="color: #000000;">1</span><span style="color: #0000FF;">..</span><span style="color: #000000;">5</span><span style="color: #0000FF;">]=</span><span style="color: #008000;">"POST "</span> <span style="color: #008080;">then</span>
<span style="color: #004080;">string</span> <span style="color: #000000;">json</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">request</span><span style="color: #0000FF;">[</span><span style="color: #7060A8;">find</span><span style="color: #0000FF;">(</span><span style="color: #008000;">'{'</span><span style="color: #0000FF;">,</span><span style="color: #000000;">request</span><span style="color: #0000FF;">)..$]</span>
<span style="color: #004080;">object</span> <span style="color: #000000;">json_data</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">parse_json</span><span style="color: #0000FF;">(</span><span style="color: #000000;">json</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">url</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">extract_json_field</span><span style="color: #0000FF;">(</span><span style="color: #000000;">json_data</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"long"</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">lnk</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">short_id</span><span style="color: #0000FF;">(</span><span style="color: #7060A8;">dict_size</span><span style="color: #0000FF;">(</span><span style="color: #000000;">shurl</span><span style="color: #0000FF;">)+</span><span style="color: #000000;">1</span><span style="color: #0000FF;">)</span>
<span style="color: #7060A8;">setd</span><span style="color: #0000FF;">(</span><span style="color: #000000;">lnk</span><span style="color: #0000FF;">,</span><span style="color: #000000;">url</span><span style="color: #0000FF;">,</span><span style="color: #000000;">shurl</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">response</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">sprintf</span><span style="color: #0000FF;">(</span><span style="color: #000000;">shortened</span><span style="color: #0000FF;">,{</span><span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">lnk</span><span style="color: #0000FF;">)+</span><span style="color: #000000;">45</span><span style="color: #0000FF;">,</span><span style="color: #000000;">lnk</span><span style="color: #0000FF;">})</span>
<span style="color: #008080;">elsif</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">request</span><span style="color: #0000FF;">)></span><span style="color: #000000;">4</span> <span style="color: #008080;">and</span> <span style="color: #000000;">request</span><span style="color: #0000FF;">[</span><span style="color: #000000;">1</span><span style="color: #0000FF;">..</span><span style="color: #000000;">4</span><span style="color: #0000FF;">]=</span><span style="color: #008000;">"GET "</span> <span style="color: #008080;">then</span>
<span style="color: #000000;">lnk</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">request</span><span style="color: #0000FF;">[</span><span style="color: #000000;">6</span><span style="color: #0000FF;">..</span><span style="color: #7060A8;">find</span><span style="color: #0000FF;">(</span><span style="color: #008000;">' '</span><span style="color: #0000FF;">,</span><span style="color: #000000;">request</span><span style="color: #0000FF;">,</span><span style="color: #000000;">6</span><span style="color: #0000FF;">)-</span><span style="color: #000000;">1</span><span style="color: #0000FF;">]</span>
<span style="color: #004080;">integer</span> <span style="color: #000000;">node</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">getd_index</span><span style="color: #0000FF;">(</span><span style="color: #000000;">lnk</span><span style="color: #0000FF;">,</span><span style="color: #000000;">shurl</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">node</span> <span style="color: #008080;">then</span>
<span style="color: #000000;">url</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">getd_by_index</span><span style="color: #0000FF;">(</span><span style="color: #000000;">node</span><span style="color: #0000FF;">,</span><span style="color: #000000;">shurl</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">response</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">sprintf</span><span style="color: #0000FF;">(</span><span style="color: #000000;">redirect</span><span style="color: #0000FF;">,{</span><span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">url</span><span style="color: #0000FF;">)+</span><span style="color: #000000;">23</span><span style="color: #0000FF;">,</span><span style="color: #000000;">url</span><span style="color: #0000FF;">,</span><span style="color: #000000;">url</span><span style="color: #0000FF;">})</span>
<span style="color: #008080;">else</span>
<span style="color: #000000;">response</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">sprintf</span><span style="color: #0000FF;">(</span><span style="color: #000000;">not_found</span><span style="color: #0000FF;">,{</span><span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">lnk</span><span style="color: #0000FF;">)+</span><span style="color: #000000;">49</span><span style="color: #0000FF;">,</span><span style="color: #000000;">lnk</span><span style="color: #0000FF;">})</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #008080;">else</span>
<span style="color: #0000FF;">?</span><span style="color: #000000;">9</span><span style="color: #0000FF;">/</span><span style="color: #000000;">0</span> <span style="color: #000080;font-style:italic;">-- uh?</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #004080;">integer</span> <span style="color: #000000;">bytes_sent</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">send</span><span style="color: #0000FF;">(</span><span style="color: #000000;">peer</span><span style="color: #0000FF;">,</span><span style="color: #000000;">response</span><span style="color: #0000FF;">)</span>
<span style="color: #7060A8;">printf</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"%d bytes successfully sent\n"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">bytes_sent</span><span style="color: #0000FF;">)</span>
<span style="color: #7060A8;">shutdown</span><span style="color: #0000FF;">(</span><span style="color: #000000;">peer</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">SD_SEND</span><span style="color: #0000FF;">)</span> <span style="color: #000080;font-style:italic;">-- tell curl it's over</span>
<span style="color: #000000;">peer</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">closesocket</span><span style="color: #0000FF;">(</span><span style="color: #000000;">peer</span><span style="color: #0000FF;">)</span> <span style="color: #000080;font-style:italic;">-- (as does this)</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">while</span>
<span style="color: #000000;">sock</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">closesocket</span><span style="color: #0000FF;">(</span><span style="color: #000000;">sock</span><span style="color: #0000FF;">)</span>
<span style="color: #7060A8;">WSACleanup</span><span style="color: #0000FF;">()</span>
<!--</syntaxhighlight>-->
{{out}}
Sample session output:
<pre>
C:\Program Files (x86)\Phix>curl http://localhost:8080/X
No such shortened url: http://localhost:8000/X
 
C:\Program Files (x86)\Phix>curl -X POST "localhost:8080" -H "Content-Type: application/json" -d "{\"long\":\"https://www.cockroachlabs.com/docs/stable/build-a-go-app-with-cockroachdb.html\"}"
The shortened URL: http://localhost:8000/a
 
C:\Program Files (x86)\Phix>curl http://localhost:8080/a
<a href="https://www.cockroachlabs.com/docs/stable/build-a-go-app-with-cockroachdb.html">Found</a>.
</pre>
Of course if you paste http://localhost:8080/a into a browser (with URL_shortener.exw still running!) it goes to the right place.<br>
Meanwhile, in a separate terminal (with * replaced by $ to avoid comment issues) we get:
<pre>
server started, open http://localhost:8080/ in browser or curl, press Esc or Q to quit
Client IP: 127.0.0.1
GET /a HTTP/1.1
Host: localhost:8080
User-Agent: curl/7.55.1
Accept: $/$
 
 
137 bytes successfully sent
Client IP: 127.0.0.1
POST / HTTP/1.1
Host: localhost:8080
User-Agent: curl/7.55.1
Accept: $/$
Content-Type: application/json
Content-Length: 89
 
{"long":"https://www.cockroachlabs.com/docs/stable/build-a-go-app-with-cockroachdb.html"}
126 bytes successfully sent
Client IP: 127.0.0.1
GET /a HTTP/1.1
Host: localhost:8080
User-Agent: curl/7.55.1
Accept: $/$
 
 
276 bytes successfully sent
</pre>
 
=={{header|PicoLisp}}==
<syntaxhighlight lang="picolisp">(load "@lib/http.l")
(allowed NIL "!short" u)
(pool "urls.db" (6))
(de short (R)
(ifn *Post
(redirect (fetch NIL (format R)))
(let K (count)
(dbSync)
(store NIL K (get 'u 'http))
(commit 'upd)
(respond (pack "http://127.0.0.1:8080/?" K "\n")) ) ) )
(server 8080 "!short")</syntaxhighlight>
{{out}}
<pre>
$ nohup pil url-short.l +
$ curl -F 'u=https://reddit.com' 127.0.0.1:8080
http://127.0.0.1:8080/?0
$ curl -F 'u=https://picolisp.com' 127.0.0.1:8080
http://127.0.0.1:8080/?1
$ curl -v http://127.0.0.1:8080/?1
* Trying 127.0.0.1:8080...
* Connected to 127.0.0.1 (127.0.0.1) port 8080 (#0)
> GET /?1 HTTP/1.1
> Host: 127.0.0.1:8080
> User-Agent: curl/7.69.1
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 303 See Other
< Server: PicoLisp
< Location: https://picolisp.com
< Content-Type: text/html
< Content-Length: 89
<
<HTML>
<HEAD><TITLE>303 See Other</TITLE></HEAD>
<BODY><H1>See Other</H1></BODY>
</HTML>
* Connection #0 to host 127.0.0.1 left intact
$
</pre>
 
=={{header|Python}}==
===Flask===
{{libheader|Flask}}
<syntaxhighlight lang="python">
"""A URL shortener using Flask. Requires Python >=3.5."""
 
import sqlite3
import string
import random
 
from http import HTTPStatus
 
from flask import Flask
from flask import Blueprint
from flask import abort
from flask import current_app
from flask import g
from flask import jsonify
from flask import redirect
from flask import request
from flask import url_for
 
 
CHARS = frozenset(string.ascii_letters + string.digits)
MIN_URL_SIZE = 8
RANDOM_ATTEMPTS = 3
 
 
def create_app(*, db=None, server_name=None) -> Flask:
app = Flask(__name__)
app.config.from_mapping(
DATABASE=db or "shorten.sqlite",
SERVER_NAME=server_name,
)
 
with app.app_context():
init_db()
 
app.teardown_appcontext(close_db)
app.register_blueprint(shortener)
 
return app
 
 
def get_db():
if "db" not in g:
g.db = sqlite3.connect(current_app.config["DATABASE"])
g.db.row_factory = sqlite3.Row
 
return g.db
 
 
def close_db(_):
db = g.pop("db", None)
 
if db is not None:
db.close()
 
 
def init_db():
db = get_db()
 
with db:
db.execute(
"CREATE TABLE IF NOT EXISTS shorten ("
"url TEXT PRIMARY KEY, "
"short TEXT NOT NULL UNIQUE ON CONFLICT FAIL)"
)
 
 
shortener = Blueprint("shorten", "short")
 
 
def random_short(size=MIN_URL_SIZE):
"""Return a random URL-safe string `size` characters in length."""
return "".join(random.sample(CHARS, size))
 
 
@shortener.errorhandler(HTTPStatus.NOT_FOUND)
def short_url_not_found(_):
return "short url not found", HTTPStatus.NOT_FOUND
 
 
@shortener.route("/<path:key>", methods=("GET",))
def short(key):
db = get_db()
 
cursor = db.execute("SELECT url FROM shorten WHERE short = ?", (key,))
row = cursor.fetchone()
 
if row is None:
abort(HTTPStatus.NOT_FOUND)
 
# NOTE: Your might want to change this to HTTPStatus.MOVED_PERMANENTLY
return redirect(row["url"], code=HTTPStatus.FOUND)
 
 
class URLExistsError(Exception):
"""Exception raised when we try to insert a URL that is already in the database."""
 
 
class ShortCollisionError(Exception):
"""Exception raised when a short URL is already in use."""
 
 
def _insert_short(long_url, short):
"""Helper function that checks for database integrity errors explicitly
before inserting a new URL."""
db = get_db()
 
if (
db.execute("SELECT * FROM shorten WHERE url = ?", (long_url,)).fetchone()
is not None
):
raise URLExistsError(long_url)
 
if (
db.execute("SELECT * FROM shorten WHERE short = ?", (short,)).fetchone()
is not None
):
raise ShortCollisionError(short)
 
with db:
db.execute("INSERT INTO shorten VALUES (?, ?)", (long_url, short))
 
 
def make_short(long_url):
"""Generate a new short URL for the given long URL."""
size = MIN_URL_SIZE
attempts = 1
short = random_short(size=size)
 
while True:
try:
_insert_short(long_url, short)
except ShortCollisionError:
# Increase the short size if we keep getting collisions.
if not attempts % RANDOM_ATTEMPTS:
size += 1
 
attempts += 1
short = random_short(size=size)
else:
break
 
return short
 
 
@shortener.route("/", methods=("POST",))
def shorten():
data = request.get_json()
 
if data is None:
abort(HTTPStatus.BAD_REQUEST)
 
long_url = data.get("long")
 
if long_url is None:
abort(HTTPStatus.BAD_REQUEST)
 
db = get_db()
 
# Does this URL already have a short?
cursor = db.execute("SELECT short FROM shorten WHERE url = ?", (long_url,))
row = cursor.fetchone()
 
if row is not None:
short_url = url_for("shorten.short", _external=True, key=row["short"])
status_code = HTTPStatus.OK
else:
short_url = url_for("shorten.short", _external=True, key=make_short(long_url))
status_code = HTTPStatus.CREATED
 
mimetype = request.accept_mimetypes.best_match(
matches=["text/plain", "application/json"], default="text/plain"
)
 
if mimetype == "application/json":
return jsonify(long=long_url, short=short_url), status_code
else:
return short_url, status_code
 
 
if __name__ == "__main__":
# Start the development server
app = create_app()
app.env = "development"
app.run(debug=True)
</syntaxhighlight>
 
=={{header|Raku}}==
(formerly Perl 6)
{{works with|Rakudo|2019.11}}
As there is no requirement to obfuscate the shortened urls, I elected to just use a simple base 36 incremental counter starting at "0" to "shorten" the urls.
Line 79 ⟶ 919:
There is '''NO''' security or authentication on this minimal app. Not recommended to run this as-is on a public facing server. It would not be too difficult to ''add'' appropriate security, but it isn't a requirement of this task. Very minimal error checking and recovery.
 
<syntaxhighlight lang="raku" perl6line># Persistent URL storage
use JSON::Fast;
 
my $urlfile = './urls.json'.IO;
my %urls = ($urlfile.IO.e and $urlfile.IO.f and $urlfile.IO.s) ??
( $urlfile.IO.slurp.&from-json ) !!
( index => 1, url => { 0 => 'http://rosettacode.org/wiki/URL_shortener#Perl_6Raku' } );
 
$urlfile.IO.spurt(%urls.&to-json);
 
# Setup parameters
my $add-url-form = qq:to/HTML/;
my $host = 'localhost';
<form action="http://localhost:10000/add_url" method="post">
my $port = 10000;
URL to add:<br><input type="text" name="url"></br>
<input type="submit" value="Submit"></form>
HTML
;
 
# Micro HTTP service
Line 102 ⟶ 939:
my $application = route {
post -> 'add_url' {
redirect :permanentsee-other, '/';
request-body -> (:$url) { store $url }
%urls<url>{ %urls<index>.base(36) } = $url;
++%urls<index>;
$urlfile.spurt(%urls.&to-json);
}
}
 
get -> {
content 'text/html', qq:to/END/;
<form action="http://$host:$port/add_url" method="post">
$add-url-form </br>
URL to add:</br><input type="text" name="url"></br>
Saved URLs:
<input type="submit" value="Submit"></form></br>
<div style="background-color:#eeeeee">
Saved URLs:
{ %urls<url>.sort( +(*.key.parse-base(36)) ).join: '</br>' }
</div style="background-color:#eeeeee">
{ %urls<url>.sort( +(*.key.parse-base(36)) ).join: '</br>' }
END
</div>
END
}
 
get -> $short {
(if my $llink = retrieve(%urls<url>{$short))} ??{
( redirect :permanent, $l) !!link
not-found}
else {
not-found
}
}
}
 
my Cro::Service $shorten = Cro::HTTP::Server.new:
:$host<localhost>, :$port<10000>, :$application;
 
$shorten.start;
 
react whenever signal(SIGINT) { $shorten.stop; exit; }
</syntaxhighlight>
 
=={{header|Wren}}==
sub retrieve ($short) { %urls<url>{$short} }
{{trans|Go}}
{{libheader|WrenGo}}
{{libheader|Wren-json}}
An embedded program with a Go host so we can use its net/http module.
<syntaxhighlight lang="wren">/* URL_shortener.wren */
 
import "./json" for JSON
sub store ($url) {
import "random" for Random
%urls<url>{ %urls<index>.base(36) } = $url;
 
++%urls<index>;
var Chars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
$urlfile.IO.spurt(%urls.&to-json);
 
}</lang>
var MethodPost = "POST"
var MethodGet = "GET"
var StatusBadRequest = 400
var StatusFound = 302
var StatusNotFound = 404
 
var Db = {}
var Rand = Random.new()
 
var GenerateKey = Fn.new { |size|
var key = List.filled(size, null)
var le = Chars.count
for (i in 0...size) key[i] = Chars[Rand.int(le)]
return key.join()
}
 
class ResponseWriter {
foreign static writeHeader(statusCode)
foreign static fprint(str)
}
 
class Request {
foreign static method
foreign static body
foreign static urlPath
}
 
class Http {
foreign static host
 
static serve() {
if (Request.method == MethodPost) {
var body = Request.body
var sh = JSON.parse(body)["long"]
var short = GenerateKey.call(8)
Db[short] = sh
ResponseWriter.fprint("The shortened URL: http://%(host)/%(short)\n")
} else if (Request.method == MethodGet) {
var path = Request.urlPath[1..-1]
if (Db.containsKey(path)) {
redirect(Db[path], StatusFound)
} else {
ResponseWriter.writeHeader(StatusNotFound)
ResponseWriter.fprint("No such shortened url: http://%(host)/%(path)\n")
}
} else {
ResponseWriter.writeHeader(StatusNotFound)
ResponseWriter.fprint("Unsupported method: %(Request.method)\n")
}
}
 
foreign static redirect(url, code)
}</syntaxhighlight>
We now embed this script in the following Go program and build it.
<syntaxhighlight lang="go">/* go build URL_shortener.go */
 
package main
 
import (
"fmt"
wren "github.com/crazyinfin8/WrenGo"
"io/ioutil"
"log"
"net/http"
"strings"
)
 
type any = interface{}
 
var fileName = "URL_shortener.wren"
var host = "localhost:8000"
 
var vm *wren.VM
 
var gw http.ResponseWriter
var greq *http.Request
 
func serveHTTP(w http.ResponseWriter, req *http.Request) {
gw, greq = w, req
wrenVar, _ := vm.GetVariable(fileName, "Http")
wrenClass, _ := wrenVar.(*wren.Handle)
defer wrenClass.Free()
wrenMethod, _ := wrenClass.Func("serve()")
defer wrenMethod.Free()
wrenMethod.Call()
}
 
func writeHeader(vm *wren.VM, parameters []any) (any, error) {
statusCode := int(parameters[1].(float64))
gw.WriteHeader(statusCode)
return nil, nil
}
 
func fprint(vm *wren.VM, parameters []any) (any, error) {
str := parameters[1].(string)
fmt.Fprintf(gw, str)
return nil, nil
}
 
func method(vm *wren.VM, parameters []any) (any, error) {
res := greq.Method
return res, nil
}
 
func body(vm *wren.VM, parameters []any) (any, error) {
res, _ := ioutil.ReadAll(greq.Body)
return res, nil
}
 
func urlPath(vm *wren.VM, parameters []any) (any, error) {
res := greq.URL.Path
return res, nil
}
 
func getHost(vm *wren.VM, parameters []any) (any, error) {
return host, nil
}
 
func redirect(vm *wren.VM, parameters []any) (any, error) {
url := parameters[1].(string)
code := int(parameters[2].(float64))
http.Redirect(gw, greq, url, code)
return nil, nil
}
 
func moduleFn(vm *wren.VM, name string) (string, bool) {
if name != "meta" && name != "random" && !strings.HasSuffix(name, ".wren") {
name += ".wren"
}
return wren.DefaultModuleLoader(vm, name)
}
 
func main() {
cfg := wren.NewConfig()
cfg.LoadModuleFn = moduleFn
vm = cfg.NewVM()
 
responseWriterMethodMap := wren.MethodMap{
"static writeHeader(_)": writeHeader,
"static fprint(_)": fprint,
}
 
requestMethodMap := wren.MethodMap{
"static method": method,
"static body": body,
"static urlPath": urlPath,
}
 
httpMethodMap := wren.MethodMap{
"static host": getHost,
"static redirect(_,_)": redirect,
}
 
classMap := wren.ClassMap{
"ResponseWriter": wren.NewClass(nil, nil, responseWriterMethodMap),
"Request": wren.NewClass(nil, nil, requestMethodMap),
"Http": wren.NewClass(nil, nil, httpMethodMap),
}
 
module := wren.NewModule(classMap)
vm.SetModule(fileName, module)
vm.InterpretFile(fileName)
http.HandleFunc("/", serveHTTP)
log.Fatal(http.ListenAndServe(host, nil))
vm.Free()
}</syntaxhighlight>
{{out}}
Sample output (abbreviated) including building and starting the server from Ubuntu 20.04 terminal and entering a valid and then an invalid shortened URL:
<pre></pre>
<pre>
$ go build URL_shortener.go
 
$ ./URL_shortener &
 
$ curl -X POST 'localhost:8000'/ \
> -H 'Content-Type: application/json' \
> -d '{
> "long": "https://www.cockroachlabs.com/docs/stable/build-a-go-app-with-cockroachdb.html"
> }'
The shortened URL: http://localhost:8000/RRyYg8tK
 
$ curl -L http://localhost:8000/RRyYg8tK
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="Learn how to use CockroachDB from a simple Go application with the Go pgx driver.">
 
....
 
</html>
 
$ curl -L http://localhost:8000/3DOPwhRv
No such shortened url: http://localhost:8000/3DOPwhRv
</pre>
9,482

edits