Merge pull request #262 from michallepicki/otp-27

Use single quotes around 'maybe' atom for compatibility with OTP 27
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 366bd8d..78dd83c 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -15,16 +15,34 @@
         os:
           - ubuntu-latest
         otp:
-          - "26.0.2.0"
-          - "25.3.2.3"
-          - "24.3.4.13"
-          - "23.3.4.18"
-          - "22.3.4.26"
-          - "21.3.8.24"
-          - "20.3.8.26"
-          - "19.3.6.13"
-          - "18.3.4.11"
+          - "27"
+          - "26"
+          - "25"
+          - "24"
+          - "23"
+          - "22"
+          - "21"
+          - "20"
     steps:
+      - uses: actions/checkout@v4
+      - run: make test
+      - run: make edoc
+      - run: REBAR=rebar make test
+  test-eol:
+    name: test ${{matrix.otp}} on ${{matrix.os}}
+    runs-on: ${{matrix.os}}
+    container:
+      image: erlang:${{matrix.otp}}
+    strategy:
+      fail-fast: false
+      matrix:
+        os:
+          - ubuntu-latest
+        otp:
+          - "19"
+          - "18"
+    steps:
+      # The old containers can't run checkout@v4
       - uses: actions/checkout@v3
       - run: make test
       - run: make edoc
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index f155eed..eee4b58 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -9,7 +9,7 @@
     runs-on: ubuntu-latest
     steps:
       - name: Check out
-        uses: actions/checkout@v3
+        uses: actions/checkout@v4
 
       - name: Publish to Hex.pm
         uses: erlangpack/github-action@v3
diff --git a/CHANGES.md b/CHANGES.md
index c53c74f..b9e22ca 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,5 +1,7 @@
-Version 3.2.2 released XXXX-XX-XX
+Version 3.2.2 released 2024-03-21
 
+* Use single quotes around 'maybe' atom for compatibility with OTP 27
+  https://github.com/mochi/mochiweb/pull/262
 * Update Erlang CI images
   https://github.com/mochi/mochiweb/pull/261
 
diff --git a/src/mochijson2.erl b/src/mochijson2.erl
index 57933e5..4a52437 100644
--- a/src/mochijson2.erl
+++ b/src/mochijson2.erl
@@ -966,8 +966,9 @@
     ?assertEqual(M, decode(Json, [{format, map}])).
 
 encode_map_test() ->
-    M = <<"{\"a\":1,\"b\":{\"c\":2}}">>,
-    ?assertEqual(M, iolist_to_binary(encode(#{a => 1, b => #{ c => 2}}))).
+    ?assertEqual(<<"{\"a\":1}">>, iolist_to_binary(encode(#{a => 1}))),
+    M = #{<<"a">> => 1, <<"b">> => #{<<"c">> => 2}},
+    ?assertEqual(M, decode(iolist_to_binary(encode(#{a => 1, b => #{ c => 2}})), [{format, map}])).
 
 encode_empty_map_test() ->
     ?assertEqual(<<"{}">>, encode(#{})).
diff --git a/src/mochiweb.app.src b/src/mochiweb.app.src
index 6e7c915..630759d 100644
--- a/src/mochiweb.app.src
+++ b/src/mochiweb.app.src
@@ -1,7 +1,7 @@
 %% This is generated from src/mochiweb.app.src
 {application, mochiweb,
  [{description, "MochiMedia Web Server"},
-  {vsn, "3.2.1"},
+  {vsn, "3.2.2"},
   {modules, []},
   {registered, []},
   {env, []},
diff --git a/src/mochiweb_multipart.erl b/src/mochiweb_multipart.erl
index 906b723..f57018b 100644
--- a/src/mochiweb_multipart.erl
+++ b/src/mochiweb_multipart.erl
@@ -275,7 +275,7 @@
 	  C1 = Callback({body, Data}),
 	  feed_mp(headers,
 		  State#mp{callback = C1(body_end), buffer = Rest});
-      {maybe, Start} ->
+      {'maybe', Start} ->
 	  <<Data:Start/binary, Rest/binary>> = Buffer,
 	  feed_mp(body,
 		  read_more(State#mp{callback = Callback({body, Data}),
@@ -328,7 +328,7 @@
 		{end_boundary, Skip, size(Prefix) + 4};
 	    _ when size(Data) < PrefixSkip + 4 ->
 		%% Underflow
-		{maybe, Skip};
+		{'maybe', Skip};
 	    _ ->
 		%% False positive
 		not_found
@@ -336,7 +336,7 @@
       {partial, Skip, Length}
 	  when Skip + Length =:= size(Data) ->
 	  %% Underflow
-	  {maybe, Skip};
+	  {'maybe', Skip};
       _ -> not_found
     end.
 
@@ -695,8 +695,8 @@
     {end_boundary, 1, 9} = find_boundary(B,
 					 <<"!\r\n--X--\r\nRest">>),
     not_found = find_boundary(B, <<"--X\r\nRest">>),
-    {maybe, 0} = find_boundary(B, <<"\r\n--X\r">>),
-    {maybe, 1} = find_boundary(B, <<"!\r\n--X\r">>),
+    {'maybe', 0} = find_boundary(B, <<"\r\n--X\r">>),
+    {'maybe', 1} = find_boundary(B, <<"!\r\n--X\r">>),
     P = <<"\r\n-----------------------------160374543510"
 	  "82272548568224146">>,
     B0 = <<55, 212, 131, 77, 206, 23, 216, 198, 35, 87, 252,
@@ -705,7 +705,7 @@
 	   45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,
 	   45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 49, 54, 48, 51,
 	   55, 52, 53, 52, 51, 53, 49>>,
-    {maybe, 30} = find_boundary(P, B0),
+    {'maybe', 30} = find_boundary(P, B0),
     not_found = find_boundary(B, <<"\r\n--XJOPKE">>),
     ok.
 
diff --git a/src/mochiweb_util.erl b/src/mochiweb_util.erl
index 5340945..f5421b6 100644
--- a/src/mochiweb_util.erl
+++ b/src/mochiweb_util.erl
@@ -877,32 +877,32 @@
 
 parse_qvalues_test() ->
     [] = parse_qvalues(""),
-    [{"identity", 0.0}] = parse_qvalues("identity;q=0"),
-    [{"identity", 0.0}] = parse_qvalues("identity ;q=0"),
-    [{"identity", 0.0}] = parse_qvalues(" identity; q =0 "),
-    [{"identity", 0.0}] = parse_qvalues("identity ; q = 0"),
-    [{"identity", 0.0}] = parse_qvalues("identity ; q= 0.0"),
-    [{"gzip", 1.0}, {"deflate", 1.0}, {"identity", 0.0}] = parse_qvalues(
+    [{"identity", +0.0}] = parse_qvalues("identity;q=0"),
+    [{"identity", +0.0}] = parse_qvalues("identity ;q=0"),
+    [{"identity", +0.0}] = parse_qvalues(" identity; q =0 "),
+    [{"identity", +0.0}] = parse_qvalues("identity ; q = 0"),
+    [{"identity", +0.0}] = parse_qvalues("identity ; q= 0.0"),
+    [{"gzip", 1.0}, {"deflate", 1.0}, {"identity", +0.0}] = parse_qvalues(
         "gzip,deflate,identity;q=0.0"
     ),
-    [{"deflate", 1.0}, {"gzip", 1.0}, {"identity", 0.0}] = parse_qvalues(
+    [{"deflate", 1.0}, {"gzip", 1.0}, {"identity", +0.0}] = parse_qvalues(
         "deflate,gzip,identity;q=0.0"
     ),
-    [{"gzip", 1.0}, {"deflate", 1.0}, {"gzip", 1.0}, {"identity", 0.0}] =
+    [{"gzip", 1.0}, {"deflate", 1.0}, {"gzip", 1.0}, {"identity", +0.0}] =
         parse_qvalues("gzip,deflate,gzip,identity;q=0"),
-    [{"gzip", 1.0}, {"deflate", 1.0}, {"identity", 0.0}] = parse_qvalues(
+    [{"gzip", 1.0}, {"deflate", 1.0}, {"identity", +0.0}] = parse_qvalues(
         "gzip, deflate , identity; q=0.0"
     ),
-    [{"gzip", 1.0}, {"deflate", 1.0}, {"identity", 0.0}] = parse_qvalues(
+    [{"gzip", 1.0}, {"deflate", 1.0}, {"identity", +0.0}] = parse_qvalues(
         "gzip; q=1, deflate;q=1.0, identity;q=0.0"
     ),
-    [{"gzip", 0.5}, {"deflate", 1.0}, {"identity", 0.0}] = parse_qvalues(
+    [{"gzip", 0.5}, {"deflate", 1.0}, {"identity", +0.0}] = parse_qvalues(
         "gzip; q=0.5, deflate;q=1.0, identity;q=0"
     ),
-    [{"gzip", 0.5}, {"deflate", 1.0}, {"identity", 0.0}] = parse_qvalues(
+    [{"gzip", 0.5}, {"deflate", 1.0}, {"identity", +0.0}] = parse_qvalues(
         "gzip; q=0.5, deflate , identity;q=0.0"
     ),
-    [{"gzip", 0.5}, {"deflate", 0.8}, {"identity", 0.0}] = parse_qvalues(
+    [{"gzip", 0.5}, {"deflate", 0.8}, {"identity", +0.0}] = parse_qvalues(
         "gzip; q=0.5, deflate;q=0.8, identity;q=0.0"
     ),
     [{"gzip", 0.5}, {"deflate", 1.0}, {"identity", 1.0}] = parse_qvalues(