mirror of https://github.com/adamdruppe/arsd.git
encoding problem
This commit is contained in:
parent
4075b6cad5
commit
a1291a48cd
154
cgi.d
154
cgi.d
|
@ -1,4 +1,8 @@
|
||||||
// FIXME: if an exception is thrown, we shouldn't necessarily cache...
|
// FIXME: if an exception is thrown, we shouldn't necessarily cache...
|
||||||
|
|
||||||
|
// FIXME: to do: add openssl optionally
|
||||||
|
// make sure embedded_httpd doesn't send two answers if one writes() then dies
|
||||||
|
|
||||||
/++
|
/++
|
||||||
Provides a uniform server-side API for CGI, FastCGI, SCGI, and HTTP web applications.
|
Provides a uniform server-side API for CGI, FastCGI, SCGI, and HTTP web applications.
|
||||||
|
|
||||||
|
@ -53,6 +57,20 @@
|
||||||
+/
|
+/
|
||||||
module arsd.cgi;
|
module arsd.cgi;
|
||||||
|
|
||||||
|
version(embedded_httpd) {
|
||||||
|
version(Posix)
|
||||||
|
version=embedded_httpd_processes;
|
||||||
|
else
|
||||||
|
version=embedded_httpd_threads;
|
||||||
|
|
||||||
|
/*
|
||||||
|
version(with_openssl) {
|
||||||
|
pragma(lib, "crypto");
|
||||||
|
pragma(lib, "ssl");
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
enum long defaultMaxContentLength = 5_000_000;
|
enum long defaultMaxContentLength = 5_000_000;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1145,6 +1163,7 @@ class Cgi {
|
||||||
auto question = requestUri.indexOf("?");
|
auto question = requestUri.indexOf("?");
|
||||||
if(question == -1) {
|
if(question == -1) {
|
||||||
queryString = "";
|
queryString = "";
|
||||||
|
// FIXME: double check, this might be wrong since it could be url encoded
|
||||||
pathInfo = requestUri[pathInfoStarts..$];
|
pathInfo = requestUri[pathInfoStarts..$];
|
||||||
} else {
|
} else {
|
||||||
queryString = requestUri[question+1..$];
|
queryString = requestUri[question+1..$];
|
||||||
|
@ -1863,6 +1882,7 @@ string makeDataUrl(string mimeType, in void[] data) {
|
||||||
return "data:" ~ mimeType ~ ";base64," ~ assumeUnique(data64);
|
return "data:" ~ mimeType ~ ";base64," ~ assumeUnique(data64);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME: I don't think this class correctly decodes/encodes the individual parts
|
||||||
/// Represents a url that can be broken down or built up through properties
|
/// Represents a url that can be broken down or built up through properties
|
||||||
struct Uri {
|
struct Uri {
|
||||||
alias toString this; // blargh idk a url really is a string, but should it be implicit?
|
alias toString this; // blargh idk a url really is a string, but should it be implicit?
|
||||||
|
@ -2110,6 +2130,30 @@ string encodeVariables(in string[][string] data) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Encodes all but the explicitly unreserved characters per rfc 3986
|
||||||
|
/// Alphanumeric and -_.~ are the only ones left unencoded
|
||||||
|
/// name is borrowed from php
|
||||||
|
string rawurlencode(in char[] data) {
|
||||||
|
string ret;
|
||||||
|
ret.reserve(data.length * 2);
|
||||||
|
foreach(char c; data) {
|
||||||
|
if(
|
||||||
|
(c >= 'a' && c <= 'z') ||
|
||||||
|
(c >= 'A' && c <= 'Z') ||
|
||||||
|
(c >= '0' && c <= '9') ||
|
||||||
|
c == '-' || c == '_' || c == '.' || c == '~')
|
||||||
|
{
|
||||||
|
ret ~= c;
|
||||||
|
} else {
|
||||||
|
ret ~= '%';
|
||||||
|
// since we iterate on char, this should give us the octets of the full utf8 string
|
||||||
|
ret ~= toHex(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// http helper functions
|
// http helper functions
|
||||||
|
|
||||||
|
@ -2236,7 +2280,110 @@ mixin template CustomCgiMain(CustomCgi, alias fun, long maxContentLength = defau
|
||||||
serveHttp!CustomCgi(&fun, listeningPort(8080));//5005);
|
serveHttp!CustomCgi(&fun, listeningPort(8080));//5005);
|
||||||
return;
|
return;
|
||||||
} else
|
} else
|
||||||
version(embedded_httpd) {
|
version(embedded_httpd_processes) {
|
||||||
|
import core.sys.posix.unistd;
|
||||||
|
//import core.sys.posix.sys.socket;
|
||||||
|
import std.c.linux.socket;
|
||||||
|
|
||||||
|
int sock = socket(AF_INET, SOCK_STREAM, 0);
|
||||||
|
if(sock == -1)
|
||||||
|
throw new Exception("socket");
|
||||||
|
|
||||||
|
{
|
||||||
|
sockaddr_in addr;
|
||||||
|
addr.sin_family = AF_INET;
|
||||||
|
addr.sin_port = htons(listeningPort(8085));
|
||||||
|
addr.sin_addr.s_addr = INADDR_ANY;
|
||||||
|
|
||||||
|
// HACKISH
|
||||||
|
int on = 1;
|
||||||
|
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, on.sizeof);
|
||||||
|
// end hack
|
||||||
|
|
||||||
|
|
||||||
|
if(bind(sock, cast(sockaddr*) &addr, addr.sizeof) == -1) {
|
||||||
|
close(sock);
|
||||||
|
throw new Exception("bind");
|
||||||
|
}
|
||||||
|
|
||||||
|
if(sock.listen(16) == -1) {
|
||||||
|
close(sock);
|
||||||
|
throw new Exception("listen");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int processCount;
|
||||||
|
pid_t newPid;
|
||||||
|
reopen:
|
||||||
|
while(processCount < 8) {
|
||||||
|
newPid = fork();
|
||||||
|
if(newPid == 0) {
|
||||||
|
// start serving on the socket
|
||||||
|
for(;;) {
|
||||||
|
bool closeConnection;
|
||||||
|
uint i;
|
||||||
|
sockaddr addr;
|
||||||
|
i = addr.sizeof;
|
||||||
|
int s = accept(sock, &addr, &i);
|
||||||
|
|
||||||
|
if(s == -1)
|
||||||
|
throw new Exception("accept");
|
||||||
|
auto ir = new BufferedInputRange(s);
|
||||||
|
|
||||||
|
while(!ir.empty) {
|
||||||
|
Cgi cgi;
|
||||||
|
try {
|
||||||
|
cgi = new CustomCgi(ir, &closeConnection);
|
||||||
|
} catch(Throwable t) {
|
||||||
|
// a construction error is either bad code or bad request; bad request is what it should be since this is bug free :P
|
||||||
|
// anyway let's kill the connection
|
||||||
|
stderr.writeln(t.toString());
|
||||||
|
sendAll(ir.source, plainHttpError(false, "400 Bad Request", t));
|
||||||
|
closeConnection = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
assert(cgi !is null);
|
||||||
|
scope(exit)
|
||||||
|
cgi.dispose();
|
||||||
|
|
||||||
|
try {
|
||||||
|
fun(cgi);
|
||||||
|
cgi.close();
|
||||||
|
} catch(Throwable t) {
|
||||||
|
// a processing error can be recovered from
|
||||||
|
stderr.writeln(t.toString);
|
||||||
|
if(!handleException(cgi, t))
|
||||||
|
closeConnection = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(closeConnection) {
|
||||||
|
ir.source.close();
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
if(!ir.empty)
|
||||||
|
ir.popFront(); // get the next
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
processCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// the parent should wait for its children...
|
||||||
|
if(newPid) {
|
||||||
|
import core.sys.posix.sys.wait;
|
||||||
|
int status;
|
||||||
|
// FIXME: maybe we should respawn if one dies unexpectedly
|
||||||
|
while(-1 != wait(&status)) {
|
||||||
|
processCount--;
|
||||||
|
goto reopen;
|
||||||
|
}
|
||||||
|
close(sock);
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
version(embedded_httpd_threads) {
|
||||||
auto manager = new ListeningConnectionManager(listeningPort(8085));
|
auto manager = new ListeningConnectionManager(listeningPort(8085));
|
||||||
foreach(connection; manager) {
|
foreach(connection; manager) {
|
||||||
scope(failure) {
|
scope(failure) {
|
||||||
|
@ -2578,6 +2725,11 @@ import std.socket;
|
||||||
// it is a class primarily for reference semantics
|
// it is a class primarily for reference semantics
|
||||||
// I might change this interface
|
// I might change this interface
|
||||||
class BufferedInputRange {
|
class BufferedInputRange {
|
||||||
|
version(Posix)
|
||||||
|
this(int source, ubyte[] buffer = null) {
|
||||||
|
this(new Socket(cast(socket_t) source, AddressFamily.INET), buffer);
|
||||||
|
}
|
||||||
|
|
||||||
this(Socket source, ubyte[] buffer = null) {
|
this(Socket source, ubyte[] buffer = null) {
|
||||||
this.source = source;
|
this.source = source;
|
||||||
if(buffer is null) {
|
if(buffer is null) {
|
||||||
|
|
16
oauth.d
16
oauth.d
|
@ -255,9 +255,9 @@ OAuthParams aWeber(string apiKey, string apiSecret) {
|
||||||
params.apiSecret = apiSecret;
|
params.apiSecret = apiSecret;
|
||||||
|
|
||||||
params.baseUrl = "https://auth.aweber.com";
|
params.baseUrl = "https://auth.aweber.com";
|
||||||
params.requestTokenPath = "/1.0/oauth/request_token";
|
params.requestTokenPath = "/1.1/oauth/request_token";
|
||||||
params.accessTokenPath = "/1.0/oauth/access_token";
|
params.accessTokenPath = "/1.1/oauth/access_token";
|
||||||
params.authorizePath = "/1.0/oauth/authorize";
|
params.authorizePath = "/1.1/oauth/authorize";
|
||||||
|
|
||||||
// API Base: https://api.aweber.com/1.0/
|
// API Base: https://api.aweber.com/1.0/
|
||||||
|
|
||||||
|
@ -274,14 +274,14 @@ string tweet(OAuthParams params, string oauthToken, string tokenSecret, string m
|
||||||
"token_secret" : tokenSecret,
|
"token_secret" : tokenSecret,
|
||||||
];
|
];
|
||||||
|
|
||||||
auto data = encodeVariables(["status" : message]);
|
auto data = "status=" ~ rawurlencode(message);//encodeVariables(["status" : message]);
|
||||||
|
|
||||||
auto ret = curlOAuth(params, "http://api.twitter.com" ~ "/1/statuses/update.json", args, "POST", data);
|
auto ret = curlOAuth(params, "http://api.twitter.com" ~ "/1.1/statuses/update.json", args, "POST", data);
|
||||||
|
|
||||||
auto val = jsonToVariant(ret).get!(Variant[string]);
|
auto val = jsonToVariant(ret).get!(Variant[string]);
|
||||||
if("id_str" !in val)
|
if("id_str" !in val)
|
||||||
throw new Exception("bad result from twitter: " ~ ret);
|
throw new Exception("bad result from twitter: " ~ ret);
|
||||||
return val["id_str"].get!string;
|
return to!string(val);//val["id_str"].get!string;
|
||||||
}
|
}
|
||||||
|
|
||||||
import std.file;
|
import std.file;
|
||||||
|
@ -512,9 +512,9 @@ struct Pair {
|
||||||
|
|
||||||
string output(bool useQuotes = false) {
|
string output(bool useQuotes = false) {
|
||||||
if(useQuotes)
|
if(useQuotes)
|
||||||
return std.uri.encodeComponent(name) ~ "=\"" ~ std.uri.encodeComponent(value) ~ "\"";
|
return std.uri.encodeComponent(name) ~ "=\"" ~ rawurlencode(value) ~ "\"";
|
||||||
else
|
else
|
||||||
return std.uri.encodeComponent(name) ~ "=" ~ std.uri.encodeComponent(value);
|
return std.uri.encodeComponent(name) ~ "=" ~ rawurlencode(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
int opCmp(Pair rhs) {
|
int opCmp(Pair rhs) {
|
||||||
|
|
Loading…
Reference in New Issue