Merge branch 'master' of github.com:adamdruppe/arsd

This commit is contained in:
Adam D. Ruppe 2017-12-07 11:43:46 -05:00
commit 61f0e5ed48
7 changed files with 880 additions and 113 deletions

8
cgi.d
View File

@ -1312,7 +1312,7 @@ class Cgi {
/** /**
Initializes it from raw HTTP request data. GenericMain uses this when you compile with -version=embedded_httpd. Initializes it from raw HTTP request data. GenericMain uses this when you compile with -version=embedded_httpd.
NOTE: If you are behind a reverse proxy, the values here might not be what you expect.... FIXME somehow. NOTE: If you are behind a reverse proxy, the values here might not be what you expect.... it will use X-Forwarded-For for remote IP and X-Forwarded-Host for host
Params: Params:
inputData = the incoming data, including headers and other raw http data. inputData = the incoming data, including headers and other raw http data.
@ -1478,6 +1478,10 @@ class Cgi {
case "content-length": case "content-length":
contentLength = to!size_t(value); contentLength = to!size_t(value);
break; break;
case "x-forwarded-for":
remoteAddress = value;
break;
case "x-forwarded-host":
case "host": case "host":
host = value; host = value;
break; break;
@ -3225,7 +3229,7 @@ class BufferedInputRange {
if(ret == Socket.ERROR) { if(ret == Socket.ERROR) {
version(Posix) { version(Posix) {
import core.stdc.errno; import core.stdc.errno;
if(errno == EINTR) { if(errno == EINTR || errno == EAGAIN) {
goto try_again; goto try_again;
} }
} }

176
dom.d
View File

@ -1073,6 +1073,9 @@ class Document : FileResource {
// in loose mode, we can see some "bad" nesting (it's valid html, but poorly formed xml). // in loose mode, we can see some "bad" nesting (it's valid html, but poorly formed xml).
// It's hard to handle above though because my code sucks. So, we'll fix it here. // It's hard to handle above though because my code sucks. So, we'll fix it here.
// Where to insert based on the parent (for mixed closed/unclosed <p> tags). See #120
// Kind of inefficient because we can't detect when we recurse back out of a node.
Element[Element] insertLocations;
auto iterator = root.tree; auto iterator = root.tree;
foreach(ele; iterator) { foreach(ele; iterator) {
if(ele.parentNode is null) if(ele.parentNode is null)
@ -1081,7 +1084,12 @@ class Document : FileResource {
if(ele.tagName == "p" && ele.parentNode.tagName == ele.tagName) { if(ele.tagName == "p" && ele.parentNode.tagName == ele.tagName) {
auto shouldBePreviousSibling = ele.parentNode; auto shouldBePreviousSibling = ele.parentNode;
auto holder = shouldBePreviousSibling.parentNode; // this is the two element's mutual holder... auto holder = shouldBePreviousSibling.parentNode; // this is the two element's mutual holder...
holder.insertAfter(shouldBePreviousSibling, ele.removeFromTree()); if (auto p = holder in insertLocations) {
shouldBePreviousSibling = *p;
assert(shouldBePreviousSibling.parentNode is holder);
}
ele = holder.insertAfter(shouldBePreviousSibling, ele.removeFromTree());
insertLocations[holder] = ele;
iterator.currentKilled(); // the current branch can be skipped; we'll hit it soon anyway since it's now next up. iterator.currentKilled(); // the current branch can be skipped; we'll hit it soon anyway since it's now next up.
} }
} }
@ -1313,16 +1321,16 @@ class Document : FileResource {
Do NOT use for anything other than eyeball debugging, Do NOT use for anything other than eyeball debugging,
because whitespace may be significant content in XML. because whitespace may be significant content in XML.
+/ +/
string toPrettyString(bool insertComments = false) const { string toPrettyString(bool insertComments = false, int indentationLevel = 0, string indentWith = "\t") const {
string s = prolog; string s = prolog;
if(insertComments) s ~= "<!--"; if(insertComments) s ~= "<!--";
s ~= "\n"; s ~= "\n";
if(insertComments) s ~= "-->"; if(insertComments) s ~= "-->";
s ~= root.toPrettyString(insertComments); s ~= root.toPrettyString(insertComments, indentationLevel, indentWith);
foreach(a; piecesAfterRoot) foreach(a; piecesAfterRoot)
s ~= a.toPrettyString(insertComments); s ~= a.toPrettyString(insertComments, indentationLevel, indentWith);
return s; return s;
} }
@ -3080,13 +3088,13 @@ class Element {
return writeToAppender(); return writeToAppender();
} }
protected string toPrettyStringIndent(bool insertComments, int indentationLevel) const { protected string toPrettyStringIndent(bool insertComments, int indentationLevel, string indentWith) const {
string s; string s;
if(insertComments) s ~= "<!--"; if(insertComments) s ~= "<!--";
s ~= "\n"; s ~= "\n";
foreach(indent; 0 .. indentationLevel) foreach(indent; 0 .. indentationLevel)
s ~= "\t"; s ~= indentWith;
if(insertComments) s ~= "-->"; if(insertComments) s ~= "-->";
return s; return s;
@ -3096,13 +3104,63 @@ class Element {
Writes out with formatting. Be warned: formatting changes the contents. Use ONLY Writes out with formatting. Be warned: formatting changes the contents. Use ONLY
for eyeball debugging. for eyeball debugging.
+/ +/
string toPrettyString(bool insertComments = false, int indentationLevel = 0) const { string toPrettyString(bool insertComments = false, int indentationLevel = 0, string indentWith = "\t") const {
string s = toPrettyStringIndent(insertComments, indentationLevel);
// first step is to concatenate any consecutive text nodes to simplify
// the white space analysis. this changes the tree! but i'm allowed since
// the comment always says it changes the comments
//
// actually i'm not allowed cuz it is const so i will cheat and lie
/+
TextNode lastTextChild = null;
for(int a = 0; a < this.children.length; a++) {
auto child = this.children[a];
if(auto tn = cast(TextNode) child) {
if(lastTextChild) {
lastTextChild.contents ~= tn.contents;
for(int b = a; b < this.children.length - 1; b++)
this.children[b] = this.children[b + 1];
this.children = this.children[0 .. $-1];
} else {
lastTextChild = tn;
}
} else {
lastTextChild = null;
}
}
+/
const(Element)[] children;
TextNode lastTextChild = null;
for(int a = 0; a < this.children.length; a++) {
auto child = this.children[a];
if(auto tn = cast(const(TextNode)) child) {
if(lastTextChild !is null) {
lastTextChild.contents ~= tn.contents;
} else {
lastTextChild = new TextNode("");
lastTextChild.parentNode = cast(Element) this;
lastTextChild.contents ~= tn.contents;
children ~= lastTextChild;
}
} else {
lastTextChild = null;
children ~= child;
}
}
string s = toPrettyStringIndent(insertComments, indentationLevel, indentWith);
s ~= "<"; s ~= "<";
s ~= tagName; s ~= tagName;
foreach(n, v ; attributes) { // i sort these for consistent output. might be more legible
// but especially it keeps it the same for diff purposes.
import std.algorithm : sort;
auto keys = sort(attributes.keys);
foreach(n; keys) {
auto v = attributes[n];
s ~= " "; s ~= " ";
s ~= n; s ~= n;
s ~= "=\""; s ~= "=\"";
@ -3119,18 +3177,19 @@ class Element {
// for simple `<collection><item>text</item><item>text</item></collection>`, let's // for simple `<collection><item>text</item><item>text</item></collection>`, let's
// just keep them on the same line // just keep them on the same line
if(children.length == 1 && children[0].nodeType == NodeType.Text) if(allAreInlineHtml(children)) {
s ~= children[0].toString(); foreach(child; children) {
else s ~= child.toString();
}
} else {
foreach(child; children) { foreach(child; children) {
assert(child !is null); assert(child !is null);
s ~= child.toPrettyString(insertComments, indentationLevel + 1); s ~= child.toPrettyString(insertComments, indentationLevel + 1, indentWith);
} }
// see comment above s ~= toPrettyStringIndent(insertComments, indentationLevel, indentWith);
if(!(children.length == 1 && children[0].nodeType == NodeType.Text)) }
s ~= toPrettyStringIndent(insertComments, indentationLevel);
s ~= "</"; s ~= "</";
s ~= tagName; s ~= tagName;
@ -3692,10 +3751,10 @@ class DocumentFragment : Element {
return this.innerHTML(where); return this.innerHTML(where);
} }
override string toPrettyString(bool insertComments, int indentationLevel) const { override string toPrettyString(bool insertComments, int indentationLevel, string indentWith) const {
string s; string s;
foreach(child; children) foreach(child; children)
s ~= child.toPrettyString(insertComments, indentationLevel); s ~= child.toPrettyString(insertComments, indentationLevel, indentWith);
return s; return s;
} }
@ -4059,7 +4118,7 @@ class RawSource : SpecialElement {
return source; return source;
} }
override string toPrettyString(bool, int) const { override string toPrettyString(bool, int, string) const {
return source; return source;
} }
@ -4087,7 +4146,7 @@ abstract class ServerSideCode : SpecialElement {
return where.data[start .. $]; return where.data[start .. $];
} }
override string toPrettyString(bool, int) const { override string toPrettyString(bool, int, string) const {
return "<" ~ source ~ ">"; return "<" ~ source ~ ">";
} }
@ -4136,7 +4195,7 @@ class BangInstruction : SpecialElement {
return where.data[start .. $]; return where.data[start .. $];
} }
override string toPrettyString(bool, int) const { override string toPrettyString(bool, int, string) const {
string s; string s;
s ~= "<!"; s ~= "<!";
s ~= source; s ~= source;
@ -4171,7 +4230,7 @@ class QuestionInstruction : SpecialElement {
return where.data[start .. $]; return where.data[start .. $];
} }
override string toPrettyString(bool, int) const { override string toPrettyString(bool, int, string) const {
string s; string s;
s ~= "<"; s ~= "<";
s ~= source; s ~= source;
@ -4207,7 +4266,7 @@ class HtmlComment : SpecialElement {
return where.data[start .. $]; return where.data[start .. $];
} }
override string toPrettyString(bool, int) const { override string toPrettyString(bool, int, string) const {
string s; string s;
s ~= "<!--"; s ~= "<!--";
s ~= source; s ~= source;
@ -4275,14 +4334,39 @@ class TextNode : Element {
return s; return s;
} }
override string toPrettyString(bool insertComments = false, int indentationLevel = 0) const { override string toPrettyString(bool insertComments = false, int indentationLevel = 0, string indentWith = "\t") const {
string s; string s;
string contents = this.contents;
// we will first collapse the whitespace per html
// sort of. note this can break stuff yo!!!!
if(this.parentNode is null || this.parentNode.tagName != "pre") {
string n = "";
bool lastWasWhitespace = indentationLevel > 0;
foreach(char c; contents) {
if(c == ' ' || c == '\n' || c == '\r' || c == '\t') {
if(!lastWasWhitespace)
n ~= ' ';
lastWasWhitespace = true;
} else {
n ~= c;
lastWasWhitespace = false;
}
}
contents = n;
}
if(this.parentNode !is null && this.parentNode.tagName != "p") {
contents = contents.strip;
}
auto e = htmlEntitiesEncode(contents); auto e = htmlEntitiesEncode(contents);
import std.algorithm.iteration : splitter; import std.algorithm.iteration : splitter;
bool first = true; bool first = true;
foreach(line; splitter(e, "\n")) { foreach(line; splitter(e, "\n")) {
if(first) { if(first) {
s ~= toPrettyStringIndent(insertComments, indentationLevel); s ~= toPrettyStringIndent(insertComments, indentationLevel, indentWith);
first = false; first = false;
} else { } else {
s ~= "\n"; s ~= "\n";
@ -4293,7 +4377,7 @@ class TextNode : Element {
if(insertComments) if(insertComments)
s ~= "-->"; s ~= "-->";
} }
s ~= line; s ~= line.stripRight;
} }
return s; return s;
} }
@ -5034,12 +5118,17 @@ struct DomMutationEvent {
} }
private enum static string[] selfClosedElements = [ private immutable static string[] selfClosedElements = [
// html 4 // html 4
"img", "hr", "input", "br", "col", "link", "meta", "img", "hr", "input", "br", "col", "link", "meta",
// html 5 // html 5
"source" ]; "source" ];
private immutable static string[] inlineElements = [
"span", "strong", "em", "b", "i", "a"
];
static import std.conv; static import std.conv;
///. ///.
@ -6961,6 +7050,21 @@ unittest {
assert(stringplate.expand.innerHTML == `<div id="bar"><div class="foo">$foo</div><div class="baz">$baz</div></div>`); assert(stringplate.expand.innerHTML == `<div id="bar"><div class="foo">$foo</div><div class="baz">$baz</div></div>`);
} }
+/ +/
bool allAreInlineHtml(const(Element)[] children) {
foreach(child; children) {
if(child.nodeType == NodeType.Text && child.nodeValue.strip.length) {
// cool
} else if(child.tagName.isInArray(inlineElements) && allAreInlineHtml(child.children)) {
// cool
} else {
// prolly block
return false;
}
}
return true;
}
/* /*
Copyright: Adam D. Ruppe, 2010 - 2017 Copyright: Adam D. Ruppe, 2010 - 2017
License: <a href="http://www.boost.org/LICENSE_1_0.txt">Boost License 1.0</a>. License: <a href="http://www.boost.org/LICENSE_1_0.txt">Boost License 1.0</a>.
@ -6971,3 +7075,21 @@ Distributed under the Boost Software License, Version 1.0.
(See accompanying file LICENSE_1_0.txt or copy at (See accompanying file LICENSE_1_0.txt or copy at
http://www.boost.org/LICENSE_1_0.txt) http://www.boost.org/LICENSE_1_0.txt)
*/ */
unittest {
// Test for issue #120
string s = `<html>
<body>
<P>AN
<P>bubbles</P>
<P>giggles</P>
</body>
</html>`;
auto doc = new Document();
doc.parseUtf8(s, false, false);
auto s2 = doc.toString();
assert(
s2.indexOf("bubbles") < s2.indexOf("giggles"),
"paragraph order incorrect:\n" ~ s2);
}

15
http2.d
View File

@ -15,8 +15,10 @@
module arsd.http2; module arsd.http2;
version(without_openssl) {} version(without_openssl) {}
else else {
version=use_openssl; version=use_openssl;
version=with_openssl;
}
@ -1518,11 +1520,22 @@ class HttpApiClient() {
this.queryParts = queryParts; this.queryParts = queryParts;
} }
RestBuilder _SELF() {
return this;
}
/// The args are so you can call opCall on the returned
/// object, despite @property being broken af in D.
RestBuilder opDispatch(string str, T)(string n, T v) {
return RestBuilder(apiClient, pathParts ~ str, queryParts ~ [n, to!string(v)]);
}
/// ///
RestBuilder opDispatch(string str)() { RestBuilder opDispatch(string str)() {
return RestBuilder(apiClient, pathParts ~ str, queryParts); return RestBuilder(apiClient, pathParts ~ str, queryParts);
} }
/// ///
RestBuilder opIndex(string str) { RestBuilder opIndex(string str) {
return RestBuilder(apiClient, pathParts ~ str, queryParts); return RestBuilder(apiClient, pathParts ~ str, queryParts);

View File

@ -3382,15 +3382,17 @@ class Window : Widget {
} }
bool dispatchKeyEvent(KeyEvent ev) { bool dispatchKeyEvent(KeyEvent ev) {
if(focusedWidget) { auto wid = focusedWidget;
auto event = new Event(ev.pressed ? "keydown" : "keyup", focusedWidget); if(wid is null)
wid = this;
auto event = new Event(ev.pressed ? "keydown" : "keyup", wid);
event.originalKeyEvent = ev; event.originalKeyEvent = ev;
event.character = ev.character; event.character = ev.character;
event.key = ev.key; event.key = ev.key;
event.state = ev.modifierState; event.state = ev.modifierState;
event.shiftKey = (ev.modifierState & ModifierState.shift) ? true : false; event.shiftKey = (ev.modifierState & ModifierState.shift) ? true : false;
event.dispatch(); event.dispatch();
}
return true; return true;
} }

View File

@ -344,7 +344,7 @@ final class AudioPcmOutThread : Thread {
if(frequencyCounter) if(frequencyCounter)
frequencyCounter--; frequencyCounter--;
if(frequencyCounter == 0) { if(frequencyCounter == 0) {
val = -val; val = -(val);
frequencyCounter = currentSample.frequency / 2; frequencyCounter = currentSample.frequency / 2;
} }
} }
@ -537,7 +537,7 @@ struct AudioInput {
read = snd_pcm_readi(handle, buffer.ptr, buffer.length / 2 /* div number of channels apparently */); read = snd_pcm_readi(handle, buffer.ptr, buffer.length / 2 /* div number of channels apparently */);
if(read < 0) if(read < 0)
throw new AlsaException("pcm read", read); throw new AlsaException("pcm read", cast(int)read);
return buffer[0 .. read * 2]; return buffer[0 .. read * 2];
} else static assert(0); } else static assert(0);
@ -610,7 +610,7 @@ struct AudioOutput {
auto ready = snd_pcm_avail_update(handle); auto ready = snd_pcm_avail_update(handle);
if(ready < 0) if(ready < 0)
throw new AlsaException("avail", ready); throw new AlsaException("avail", cast(int)ready);
if(ready > BUFFER_SIZE_FRAMES) if(ready > BUFFER_SIZE_FRAMES)
ready = BUFFER_SIZE_FRAMES; ready = BUFFER_SIZE_FRAMES;
//import std.stdio; writeln("filling ", ready); //import std.stdio; writeln("filling ", ready);
@ -621,8 +621,10 @@ struct AudioOutput {
while(data.length) { while(data.length) {
written = snd_pcm_writei(handle, data.ptr, data.length / 2); written = snd_pcm_writei(handle, data.ptr, data.length / 2);
if(written < 0) if(written < 0) {
throw new AlsaException("pcm write", written); written = snd_pcm_recover(handle, cast(int)written, 0);
if (written < 0) throw new AlsaException("pcm write", cast(int)written);
}
data = data[written * 2 .. $]; data = data[written * 2 .. $];
} }
} }
@ -948,7 +950,7 @@ struct AudioMixer {
int getMasterVolume() { int getMasterVolume() {
version(ALSA) { version(ALSA) {
auto volume = getMasterVolumeExact(); auto volume = getMasterVolumeExact();
return volume * 100 / (maxVolume - minVolume); return cast(int)(volume * 100 / (maxVolume - minVolume));
} else static assert(0); } else static assert(0);
} }
@ -957,7 +959,7 @@ struct AudioMixer {
version(ALSA) { version(ALSA) {
c_long volume; c_long volume;
snd_mixer_selem_get_playback_volume(selem, 0, &volume); snd_mixer_selem_get_playback_volume(selem, 0, &volume);
return volume; return cast(int)volume;
} else static assert(0); } else static assert(0);
} }
@ -966,7 +968,7 @@ struct AudioMixer {
void setMasterVolume(int volume) { void setMasterVolume(int volume) {
version(ALSA) { version(ALSA) {
assert(volume >= 0 && volume <= 100); assert(volume >= 0 && volume <= 100);
setMasterVolumeExact(volume * (maxVolume - minVolume) / 100); setMasterVolumeExact(cast(int)(volume * (maxVolume - minVolume) / 100));
} else static assert(0); } else static assert(0);
} }
@ -1382,6 +1384,16 @@ extern(C):
snd_pcm_sframes_t snd_pcm_avail(snd_pcm_t *pcm); snd_pcm_sframes_t snd_pcm_avail(snd_pcm_t *pcm);
snd_pcm_sframes_t snd_pcm_avail_update(snd_pcm_t *pcm); snd_pcm_sframes_t snd_pcm_avail_update(snd_pcm_t *pcm);
int snd_pcm_recover (snd_pcm_t* pcm, int err, int silent);
alias snd_lib_error_handler_t = void function (const(char)* file, int line, const(char)* function_, int err, const(char)* fmt, ...);
int snd_lib_error_set_handler (snd_lib_error_handler_t handler);
private void alsa_message_silencer (const(char)* file, int line, const(char)* function_, int err, const(char)* fmt, ...) {}
//k8: ALSAlib loves to trash stderr; shut it up
void silence_alsa_messages () { snd_lib_error_set_handler(&alsa_message_silencer); }
shared static this () { silence_alsa_messages(); }
// raw midi // raw midi
static if(is(ssize_t == uint)) static if(is(ssize_t == uint))

File diff suppressed because it is too large Load Diff

4
web.d
View File

@ -642,7 +642,7 @@ class ApiProvider : WebDotDBaseType {
<html> <html>
<head> <head>
<title></title> <title></title>
<link rel=\"stylesheet\" href=\"styles.css?"~compiliationStamp~"\" /> <link rel=\"stylesheet\" id=\"webd-styles-css\" href=\"styles.css?"~compiliationStamp~"\" />
<script> var delayedExecutionQueue = []; </script> <!-- FIXME do some better separation --> <script> var delayedExecutionQueue = []; </script> <!-- FIXME do some better separation -->
<script> <script>
if(document.cookie.indexOf(\"timezone=\") == -1) { if(document.cookie.indexOf(\"timezone=\") == -1) {
@ -658,7 +658,7 @@ class ApiProvider : WebDotDBaseType {
</head> </head>
<body> <body>
<div id=\"body\"></div> <div id=\"body\"></div>
<script src=\"functions.js?"~compiliationStamp~"\"></script> <script id=\"webd-functions-js\" src=\"functions.js?"~compiliationStamp~"\"></script>
" ~ deqFoot ~ " " ~ deqFoot ~ "
</body> </body>
</html>"); </html>");