Não consigo informar um "=" dentro de um valor de um query param no HttpStream
Estou testando a seguinte URL: http://httpbin.org/get?a=b=c
Se colocar no browser, os dois campos JSON que são relevantes para este caso são:
-
args
:
"args": {
"a": "b=c"
}
-
url
:
"url": "https://httpbin.org/get?a=b=c"
Ao acessar essa URL com o TotalCross 5.0.0-RC2, entretanto, obtenho os seguintes resultados:
"args": {
"a": "b"
}
...
"url": "https://httpbin.org/get?a=b"
Isso se deve possivelmente à esta mudança na 4.3.1:
HttpStream: Query parameters are now encoded to support the usage of unsafe characters
Porém, na implementação, foi feita ignorando eventuais duplos/triplos =
antes do separador.
Na versão 4.3.0, entretanto, funciona a requisição com eventuais duplos/triplos =
antes do separador.
Código antigo (4.3.0), que delegava a quem estivesse chamando o HttpStream
para normalizar totalmente a URL, permite múltiplos =
s:
private void getResponse(Options options) throws totalcross.io.IOException {
[...]
if (shouldSendData(options)) {
if (options.httpType == null) {
sb.append("POST ");
}
// server path
sb.append(serverPath);
if (uri.query != null) {
sb.append("?").append(uri.query.toString());
}
options.doGet = false;
}
if (GET.equals(options.httpType) || options.doGet) {
useProxy = options.proxyAddress != null;
if (options.httpType == null) {
sb.append("GET ");
}
if (useProxy) {
sb.append(serverPath);
} else {
if (uri.path != null && uri.path.len > 0) {
sb.append(uri.path.toString());
} else {
sb.append("/");
}
if (uri.query != null && uri.query.len > 0) {
sb.append("?");
sb.append(uri.query.toString());
}
}
}
Código novo (5.0.0-RC4), que trata eventuais caracteres duvidosos da query string que o programador passe:
private void getResponse(Options options) throws totalcross.io.IOException {
[...]
if (shouldSendData(options)) {
if (options.httpType == null) {
sb.append("POST ");
}
// server path
sb.append(serverPath);
if (uri.query != null && uri.query.len > 0) {
sb.append('?');
String[] tokens = Convert.tokenizeString(uri.query.toString(), '&');
for (String token : tokens) {
String[] args = Convert.tokenizeString(token, '=');
sb.append(args[0]);
if (args.length > 1) {
sb.append('=').append(URI.encode(args[1]));
}
sb.append('&');
}
if (tokens.length > 0) {
sb.setLength(sb.length() - 1);
}
}
options.doGet = false;
}
if (GET.equals(options.httpType) || options.doGet) {
useProxy = options.proxyAddress != null;
if (options.httpType == null) {
sb.append("GET ");
}
if (useProxy) {
sb.append(serverPath);
} else {
if (uri.path != null && uri.path.len > 0) {
sb.append(uri.path.toString());
} else {
sb.append("/");
}
if (uri.query != null && uri.query.len > 0) {
sb.append('?');
String[] tokens = Convert.tokenizeString(uri.query.toString(), '&');
for (String token : tokens) {
String[] args = Convert.tokenizeString(token, '=');
sb.append(args[0]);
if (args.length > 1) {
sb.append('=').append(URI.encode(args[1]));
}
sb.append('&');
}
if (tokens.length > 0) {
sb.setLength(sb.length() - 1);
}
}
}
}
Note que, na conversão do uri.query
para o percent encoding, há duplicação de código (e, por sinal, o trecho que remove tudo após o segundo =
):
if (uri.query != null && uri.query.len > 0) {
sb.append('?');
String[] tokens = Convert.tokenizeString(uri.query.toString(), '&');
for (String token : tokens) {
String[] args = Convert.tokenizeString(token, '=');
sb.append(args[0]);
if (args.length > 1) {
sb.append('=').append(URI.encode(args[1]));
}
sb.append('&');
}
if (tokens.length > 0) {
sb.setLength(sb.length() - 1);
}
}
Uma solução para não engolir mais os =
excedentes em um campo de formulário seria transformar o if (args.length > 1)
em um laço (e o todo em uma função unificante DRY):
private void printQuery(URI uri, StringBuffer sb) {
if (uri.query != null && uri.query.len > 0) {
sb.append('?');
String[] tokens = Convert.tokenizeString(uri.query.toString(), '&');
for (String token : tokens) {
String[] args = Convert.tokenizeString(token, '=');
sb.append(args[0]);
for (int i = 1; i < args.length; i++) {
sb.append('=').append(URI.encode(args[i]));
}
sb.append('&');
}
if (tokens.length > 0) {
sb.setLength(sb.length() - 1);
}
}
}
Posso sugerir brincar um pouco mais com índices numéricos e evitar a redução do tamanho do StringBuffer
:
private void printQuery(URI uri, StringBuffer sb) {
if (uri.query != null && uri.query.len > 0) {
sb.append('?');
String[] tokens = Convert.tokenizeString(uri.query.toString(), '&');
for (int tkIdx = 0; tkIdx < tokens.length; tkIdx++) {
if (tkIdx != 0) {
sb.append('&');
}
String token = tokens[tkIdx];
String[] args = Convert.tokenizeString(token, '=');
sb.append(args[0]);
for (int i = 1; i < args.length; i++) {
sb.append('=').append(URI.encode(args[i]));
}
}
}
}
Também não há nada que proíba a chave de um elemento da query ser fornecida com espaços, ou ser um argumento sem =
que contenha outros caracteres inválidos internamente. Então, por questão de completude, podemos também proteger o args[0]
:
private void printQuery(URI uri, StringBuffer sb) {
if (uri.query != null && uri.query.len > 0) {
sb.append('?');
String[] tokens = Convert.tokenizeString(uri.query.toString(), '&');
for (int tkIdx = 0; tkIdx < tokens.length; tkIdx++) {
if (tkIdx != 0) {
sb.append('&');
}
String token = tokens[tkIdx];
String[] args = Convert.tokenizeString(token, '=');
sb.append(URI.encode(args[0]));
for (int i = 1; i < args.length; i++) {
sb.append('=').append(URI.encode(args[i]));
}
}
}
}
Reconheço que esse não é um caso comum de se colocar numa query string, porém não é impossível.