application/x-www-form-urlencoded は原則 UTF-8 が使用されるが、古いアプリケーションなどは Shift_JIS で送信してくることがある。
shift_jis.rs
use std::borrow::Cow;
use encoding_rs::SHIFT_JIS;
use percent_encoding::percent_decode;
#[inline]
pub fn parse_form_urlencoded(input: &[u8]) -> Parse<'_> {
Parse { input }
}
#[derive(Copy, Clone)]
pub struct Parse<'a> {
input: &'a [u8],
}
impl<'a> Iterator for Parse<'a> {
type Item = (Cow<'a, str>, Cow<'a, str>);
fn next(&mut self) -> Option<Self::Item> {
loop {
if self.input.is_empty() {
return None;
}
let mut split2 = self.input.splitn(2, |&b| b == b'&');
let sequence = split2.next().unwrap();
self.input = split2.next().unwrap_or(&[][..]);
if sequence.is_empty() {
continue;
}
let mut split2 = sequence.splitn(2, |&b| b == b'=');
let name = split2.next().unwrap();
let value = split2.next().unwrap_or(&[][..]);
return Some((decode(name), decode(value)));
}
}
}
fn decode(input: &[u8]) -> Cow<'_, str> {
let replaced = replace_plus(input);
decode_shift_jis(match percent_decode(&replaced).into() {
Cow::Owned(vec) => Cow::Owned(vec),
Cow::Borrowed(_) => replaced,
})
}
fn replace_plus(input: &[u8]) -> Cow<'_, [u8]> {
match input.iter().position(|&b| b == b'+') {
None => Cow::Borrowed(input),
Some(first_position) => {
let mut replaced = input.to_owned();
replaced[first_position] = b' ';
for byte in &mut replaced[first_position + 1..] {
if *byte == b'+' {
*byte = b' ';
}
}
Cow::Owned(replaced)
}
}
}
impl<'a> Parse<'a> {
pub fn into_owned(self) -> ParseIntoOwned<'a> {
ParseIntoOwned { inner: self }
}
}
pub struct ParseIntoOwned<'a> {
inner: Parse<'a>,
}
impl<'a> Iterator for ParseIntoOwned<'a> {
type Item = (String, String);
fn next(&mut self) -> Option<Self::Item> {
self.inner
.next()
.map(|(k, v)| (k.into_owned(), v.into_owned()))
}
}
pub(crate) fn decode_shift_jis(input: Cow<'_, [u8]>) -> Cow<'_, str> { // Shift_JIS 以外のリクエストに対応する場合、この関数を変更する
match input {
Cow::Borrowed(bytes) => SHIFT_JIS.decode(bytes).0, // 具体的にはこの行と
Cow::Owned(bytes) => {
match SHIFT_JIS.decode(&bytes).0 { // この行を変更する
Cow::Borrowed(utf8) => Cow::Owned(String::from(utf8)),
Cow::Owned(s) => Cow::Owned(s),
}
}
}
}
以下が使い方
async fn handler(req: Request<IncomingBody>) -> Result<Response<BoxBody>> {
let req_params = shift_jis::parse_form_urlencoded(req.collect().await?.to_bytes().as_ref()).into_owned().collect::<HashMap<String, String>>();
// 以下はリクエストが UTF-8 の場合
// let req_params = form_urlencoded::parse(req.collect().await?.to_bytes().as_ref()).into_owned().collect::<HashMap<String, String>>();
let param1 = req_params.get("param1"); // Shift_JIS で POST されたパラメータ1を取得
let param2 = req_params.get("param2"); // Shift_JIS で POST されたパラメータ2を取得
// ...